From bec2accf1a40d03b024232ce2d1751b2b12e06e4 Mon Sep 17 00:00:00 2001 From: snider Date: Tue, 30 Dec 2025 23:01:16 +0000 Subject: [PATCH] feat: Add OpenCL GPU support for ProgPowZ, ETChash, and Blake3DCR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement GPU mining backends for three new algorithms: - ProgPowZ (Zano): DAG-based ProgPow variant with 512 parents, dynamic program generation per period - ETChash (Ethereum Classic): Standard Ethash with 256 parents and ECIP-1099 epoch calculation for post-block 11.7M - Blake3DCR (Decred): Simple Blake3 hash kernel with no DAG requirement, processing 180-byte block headers Each implementation includes OpenCL kernels, GPU runners, thread generators, and build system integration. Also adds fast modulo optimization to ETCCache for GPU kernel performance. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- miner/core/src/backend/opencl/OclWorker.cpp | 24 +- .../src/backend/opencl/cl/blake3/blake3.cl | 215 +++++ .../src/backend/opencl/cl/blake3/blake3_cl.h | 507 +++++++++++ .../src/backend/opencl/cl/etchash/etchash.cl | 362 ++++++++ .../backend/opencl/cl/etchash/etchash_cl.h | 766 +++++++++++++++++ .../backend/opencl/cl/etchash/etchash_dag.cl | 279 +++++++ .../opencl/cl/etchash/etchash_dag_cl.h | 630 ++++++++++++++ .../src/backend/opencl/cl/progpowz/defs.h | 65 ++ .../backend/opencl/cl/progpowz/progpowz.cl | 310 +++++++ .../backend/opencl/cl/progpowz/progpowz_cl.h | 785 ++++++++++++++++++ .../opencl/cl/progpowz/progpowz_dag.cl | 304 +++++++ .../opencl/cl/progpowz/progpowz_dag_cl.h | 702 ++++++++++++++++ .../ocl_generic_blake3_generator.cpp | 73 ++ .../ocl_generic_etchash_generator.cpp | 84 ++ .../ocl_generic_progpowz_generator.cpp | 87 ++ .../etchash/Etchash_CalculateDAGKernel.cpp | 45 + .../etchash/Etchash_CalculateDAGKernel.h | 41 + .../progpowz/ProgPowZ_CalculateDAGKernel.cpp | 49 ++ .../progpowz/ProgPowZ_CalculateDAGKernel.h | 45 + miner/core/src/backend/opencl/opencl.cmake | 39 + .../opencl/runners/OclBlake3Runner.cpp | 173 ++++ .../backend/opencl/runners/OclBlake3Runner.h | 61 ++ .../opencl/runners/OclEtchashRunner.cpp | 264 ++++++ .../backend/opencl/runners/OclEtchashRunner.h | 80 ++ .../opencl/runners/OclProgPowZRunner.cpp | 213 +++++ .../opencl/runners/OclProgPowZRunner.h | 82 ++ .../opencl/runners/tools/OclProgPowZ.cpp | 487 +++++++++++ .../opencl/runners/tools/OclProgPowZ.h | 50 ++ .../src/backend/opencl/wrappers/OclDevice.cpp | 21 + miner/core/src/crypto/etchash/ETCCache.cpp | 38 + miner/core/src/crypto/etchash/ETCCache.h | 3 + pkg/mining/xmrig_gpu_test.go | 12 +- 32 files changed, 6883 insertions(+), 13 deletions(-) create mode 100644 miner/core/src/backend/opencl/cl/blake3/blake3.cl create mode 100644 miner/core/src/backend/opencl/cl/blake3/blake3_cl.h create mode 100644 miner/core/src/backend/opencl/cl/etchash/etchash.cl create mode 100644 miner/core/src/backend/opencl/cl/etchash/etchash_cl.h create mode 100644 miner/core/src/backend/opencl/cl/etchash/etchash_dag.cl create mode 100644 miner/core/src/backend/opencl/cl/etchash/etchash_dag_cl.h create mode 100644 miner/core/src/backend/opencl/cl/progpowz/defs.h create mode 100644 miner/core/src/backend/opencl/cl/progpowz/progpowz.cl create mode 100644 miner/core/src/backend/opencl/cl/progpowz/progpowz_cl.h create mode 100644 miner/core/src/backend/opencl/cl/progpowz/progpowz_dag.cl create mode 100644 miner/core/src/backend/opencl/cl/progpowz/progpowz_dag_cl.h create mode 100644 miner/core/src/backend/opencl/generators/ocl_generic_blake3_generator.cpp create mode 100644 miner/core/src/backend/opencl/generators/ocl_generic_etchash_generator.cpp create mode 100644 miner/core/src/backend/opencl/generators/ocl_generic_progpowz_generator.cpp create mode 100644 miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.cpp create mode 100644 miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h create mode 100644 miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.cpp create mode 100644 miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h create mode 100644 miner/core/src/backend/opencl/runners/OclBlake3Runner.cpp create mode 100644 miner/core/src/backend/opencl/runners/OclBlake3Runner.h create mode 100644 miner/core/src/backend/opencl/runners/OclEtchashRunner.cpp create mode 100644 miner/core/src/backend/opencl/runners/OclEtchashRunner.h create mode 100644 miner/core/src/backend/opencl/runners/OclProgPowZRunner.cpp create mode 100644 miner/core/src/backend/opencl/runners/OclProgPowZRunner.h create mode 100644 miner/core/src/backend/opencl/runners/tools/OclProgPowZ.cpp create mode 100644 miner/core/src/backend/opencl/runners/tools/OclProgPowZ.h diff --git a/miner/core/src/backend/opencl/OclWorker.cpp b/miner/core/src/backend/opencl/OclWorker.cpp index 9b5be37..11b4656 100644 --- a/miner/core/src/backend/opencl/OclWorker.cpp +++ b/miner/core/src/backend/opencl/OclWorker.cpp @@ -39,6 +39,18 @@ # include "backend/opencl/runners/OclKawPowRunner.h" #endif +#ifdef XMRIG_ALGO_PROGPOWZ +# include "backend/opencl/runners/OclProgPowZRunner.h" +#endif + +#ifdef XMRIG_ALGO_ETCHASH +# include "backend/opencl/runners/OclEtchashRunner.h" +#endif + +#ifdef XMRIG_ALGO_BLAKE3DCR +# include "backend/opencl/runners/OclBlake3Runner.h" +#endif + #include #include @@ -94,25 +106,19 @@ xmrig::OclWorker::OclWorker(size_t id, const OclLaunchData &data) : # ifdef XMRIG_ALGO_ETCHASH case Algorithm::ETCHASH: - // ETChash/Ethash GPU support - uses ethash DAG similar to KawPow - // TODO: Implement OclEtchashRunner with proper ethash kernel - m_runner = nullptr; + m_runner = new OclEtchashRunner(id, data); break; # endif # ifdef XMRIG_ALGO_PROGPOWZ case Algorithm::PROGPOWZ: - // ProgPowZ GPU support - ProgPow variant used by Zano - // TODO: Implement OclProgPowZRunner with ProgPowZ kernel - m_runner = nullptr; + m_runner = new OclProgPowZRunner(id, data); break; # endif # ifdef XMRIG_ALGO_BLAKE3DCR case Algorithm::BLAKE3: - // Blake3 GPU support - fast cryptographic hash for Decred - // TODO: Implement OclBlake3Runner - m_runner = nullptr; + m_runner = new OclBlake3Runner(id, data); break; # endif diff --git a/miner/core/src/backend/opencl/cl/blake3/blake3.cl b/miner/core/src/backend/opencl/cl/blake3/blake3.cl new file mode 100644 index 0000000..ad86cd7 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/blake3/blake3.cl @@ -0,0 +1,215 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Blake3 OpenCL mining kernel for Decred + * Based on BLAKE3 reference implementation + * + * 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. + */ + +// +// Blake3 constants +// + +#define BLAKE3_BLOCK_LEN 64 +#define BLAKE3_OUT_LEN 32 + +// Flags +#define CHUNK_START 1 +#define CHUNK_END 2 +#define ROOT 8 + +// Initial vector +#define IV_0 0x6A09E667 +#define IV_1 0xBB67AE85 +#define IV_2 0x3C6EF372 +#define IV_3 0xA54FF53A +#define IV_4 0x510E527F +#define IV_5 0x9B05688C +#define IV_6 0x1F83D9AB +#define IV_7 0x5BE0CD19 + +// Decred block header constants +#define BLOCK_HEADER_SIZE 180 +#define NONCE_OFFSET 140 + +#ifndef GROUP_SIZE +#define GROUP_SIZE 256 +#endif + +// Message schedule for 7 rounds +__constant uchar MSG_SCHEDULE[7][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}, + {3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1}, + {10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6}, + {12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4}, + {9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7}, + {11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13}, +}; + +static inline uint rotr32(uint w, uint c) +{ + return (w >> c) | (w << (32 - c)); +} + +// Quarter round +static inline void g(uint *state, uint a, uint b, uint c, uint d, uint x, uint y) +{ + state[a] = state[a] + state[b] + x; + state[d] = rotr32(state[d] ^ state[a], 16); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 12); + state[a] = state[a] + state[b] + y; + state[d] = rotr32(state[d] ^ state[a], 8); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 7); +} + +static void round_fn(uint *state, const uint *msg, uint round) +{ + __constant uchar *schedule = MSG_SCHEDULE[round]; + g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]); + g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]); + g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]); + g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]); + g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]); + g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]); + g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]); + g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]); +} + +// Compress a single block +static void compress(uint *cv, const uint *msg, uint block_len, ulong counter, uint flags) +{ + uint state[16]; + + // Initialize state + state[0] = cv[0]; + state[1] = cv[1]; + state[2] = cv[2]; + state[3] = cv[3]; + state[4] = cv[4]; + state[5] = cv[5]; + state[6] = cv[6]; + state[7] = cv[7]; + state[8] = IV_0; + state[9] = IV_1; + state[10] = IV_2; + state[11] = IV_3; + state[12] = (uint)counter; + state[13] = (uint)(counter >> 32); + state[14] = block_len; + state[15] = flags; + + // 7 rounds + for (uint r = 0; r < 7; r++) { + round_fn(state, msg, r); + } + + // Finalize + cv[0] = state[0] ^ state[8]; + cv[1] = state[1] ^ state[9]; + cv[2] = state[2] ^ state[10]; + cv[3] = state[3] ^ state[11]; + cv[4] = state[4] ^ state[12]; + cv[5] = state[5] ^ state[13]; + cv[6] = state[6] ^ state[14]; + cv[7] = state[7] ^ state[15]; +} + +// Hash a Decred block header (180 bytes = 3 blocks) +// Block 0: bytes 0-63 (CHUNK_START) +// Block 1: bytes 64-127 +// Block 2: bytes 128-179 (52 bytes, CHUNK_END | ROOT) +static void blake3_hash_header(const uint *header, uint nonce, uint *hash) +{ + uint cv[8]; + uint msg[16]; + + // Initialize CV with IV + cv[0] = IV_0; + cv[1] = IV_1; + cv[2] = IV_2; + cv[3] = IV_3; + cv[4] = IV_4; + cv[5] = IV_5; + cv[6] = IV_6; + cv[7] = IV_7; + + // Block 0: bytes 0-63 (CHUNK_START) + for (uint i = 0; i < 16; i++) { + msg[i] = header[i]; + } + compress(cv, msg, BLAKE3_BLOCK_LEN, 0, CHUNK_START); + + // Block 1: bytes 64-127 + for (uint i = 0; i < 16; i++) { + msg[i] = header[16 + i]; + } + compress(cv, msg, BLAKE3_BLOCK_LEN, 0, 0); + + // Block 2: bytes 128-179 (52 bytes with nonce at offset 140-143) + // Nonce is at byte offset 140, which is word offset 35 (header[35]) + // In block 2, this is word offset 35-32 = 3 + for (uint i = 0; i < 13; i++) { + msg[i] = header[32 + i]; + } + // Insert nonce at the correct position (offset 140 = byte 12 in block 2 = word 3) + msg[3] = nonce; // Nonce is at bytes 140-143 = word 35 = msg[3] in block 2 + + // Zero-pad remaining bytes + for (uint i = 13; i < 16; i++) { + msg[i] = 0; + } + + compress(cv, msg, 52, 0, CHUNK_END | ROOT); + + // Output hash + for (uint i = 0; i < 8; i++) { + hash[i] = cv[i]; + } +} + +// Compare hash against target (little-endian comparison) +static bool check_target(const uint *hash, ulong target) +{ + // For Decred, compare first 8 bytes of hash against target + ulong h = ((ulong)hash[1] << 32) | hash[0]; + return h <= target; +} + +__kernel void blake3_search( + __global const uint *g_header, // 180-byte block header (45 words) + ulong target, // Difficulty target + __global uint *results, // Output: found nonces + __global uint *stop // Stop flag +) +{ + if (*stop) + return; + + const uint gid = get_global_id(0); + + // Load header into private memory + uint header[45]; + for (uint i = 0; i < 45; i++) { + header[i] = g_header[i]; + } + + // Calculate hash with this nonce + uint hash[8]; + blake3_hash_header(header, gid, hash); + + // Check against target + if (check_target(hash, target)) { + *stop = 1; + const uint k = atomic_inc(results) + 1; + if (k <= 15) { + results[k] = gid; + } + } +} diff --git a/miner/core/src/backend/opencl/cl/blake3/blake3_cl.h b/miner/core/src/backend/opencl/cl/blake3/blake3_cl.h new file mode 100644 index 0000000..8034746 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/blake3/blake3_cl.h @@ -0,0 +1,507 @@ +#pragma once + +namespace xmrig { + +static const char blake3_cl[] = { + 0x2f, 0x2a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x32, 0x35, 0x20, 0x4c, 0x65, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x42, + 0x6c, 0x61, 0x6b, 0x65, 0x33, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, + 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x44, 0x65, 0x63, 0x72, 0x65, + 0x64, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x42, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x33, 0x20, 0x72, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x69, 0x73, 0x20, + 0x66, 0x72, 0x65, 0x65, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, + 0x65, 0x3a, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x72, + 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, + 0x69, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x79, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, + 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, + 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20, 0x53, 0x6f, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, + 0x65, 0x2c, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x28, + 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x29, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x42, 0x6c, + 0x61, 0x6b, 0x65, 0x33, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x73, 0x0a, 0x2f, 0x2f, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x33, 0x5f, 0x42, 0x4c, + 0x4f, 0x43, 0x4b, 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x36, 0x34, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x42, 0x4c, 0x41, 0x4b, 0x45, + 0x33, 0x5f, 0x4f, 0x55, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x33, 0x32, + 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x43, 0x48, 0x55, 0x4e, 0x4b, + 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x20, 0x31, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x5f, 0x45, + 0x4e, 0x44, 0x20, 0x32, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x38, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, + 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x76, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x30, 0x20, 0x30, 0x78, 0x36, 0x41, 0x30, 0x39, 0x45, 0x36, + 0x36, 0x37, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x31, 0x20, 0x30, 0x78, 0x42, 0x42, 0x36, 0x37, 0x41, 0x45, + 0x38, 0x35, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x32, 0x20, 0x30, 0x78, 0x33, 0x43, 0x36, 0x45, 0x46, 0x33, + 0x37, 0x32, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x33, 0x20, 0x30, 0x78, 0x41, 0x35, 0x34, 0x46, 0x46, 0x35, + 0x33, 0x41, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x34, 0x20, 0x30, 0x78, 0x35, 0x31, 0x30, 0x45, 0x35, 0x32, + 0x37, 0x46, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x35, 0x20, 0x30, 0x78, 0x39, 0x42, 0x30, 0x35, 0x36, 0x38, + 0x38, 0x43, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x36, 0x20, 0x30, 0x78, 0x31, 0x46, 0x38, 0x33, 0x44, 0x39, + 0x41, 0x42, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x49, + 0x56, 0x5f, 0x37, 0x20, 0x30, 0x78, 0x35, 0x42, 0x45, 0x30, 0x43, 0x44, + 0x31, 0x39, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x65, 0x63, 0x72, 0x65, + 0x64, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x73, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x42, 0x4c, 0x4f, + 0x43, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x5f, 0x53, 0x49, + 0x5a, 0x45, 0x20, 0x31, 0x38, 0x30, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x4e, 0x4f, 0x4e, 0x43, 0x45, 0x5f, 0x4f, 0x46, 0x46, + 0x53, 0x45, 0x54, 0x20, 0x31, 0x34, 0x30, 0x0a, 0x0a, 0x23, 0x69, 0x66, + 0x6e, 0x64, 0x65, 0x66, 0x20, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, + 0x49, 0x5a, 0x45, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x32, + 0x35, 0x36, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x2f, + 0x2f, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x37, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x0a, 0x5f, 0x5f, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, + 0x20, 0x4d, 0x53, 0x47, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, + 0x45, 0x5b, 0x37, 0x5d, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, + 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, + 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x39, 0x2c, 0x20, + 0x31, 0x30, 0x2c, 0x20, 0x31, 0x31, 0x2c, 0x20, 0x31, 0x32, 0x2c, 0x20, + 0x31, 0x33, 0x2c, 0x20, 0x31, 0x34, 0x2c, 0x20, 0x31, 0x35, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x32, 0x2c, 0x20, 0x36, 0x2c, 0x20, + 0x33, 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x34, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, + 0x31, 0x2c, 0x20, 0x31, 0x32, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x39, 0x2c, + 0x20, 0x31, 0x34, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x38, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, + 0x31, 0x30, 0x2c, 0x20, 0x31, 0x32, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, + 0x32, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x34, 0x2c, 0x20, 0x36, 0x2c, + 0x20, 0x35, 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x31, + 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x31, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x31, 0x30, 0x2c, 0x20, 0x37, 0x2c, + 0x20, 0x31, 0x32, 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x31, 0x34, 0x2c, 0x20, + 0x33, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x34, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, + 0x35, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x31, 0x32, 0x2c, 0x20, 0x31, 0x33, + 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x31, 0x31, 0x2c, 0x20, 0x31, 0x35, 0x2c, + 0x20, 0x31, 0x30, 0x2c, 0x20, 0x31, 0x34, 0x2c, 0x20, 0x38, 0x2c, 0x20, + 0x37, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x33, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x34, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x39, 0x2c, 0x20, 0x31, 0x34, 0x2c, + 0x20, 0x31, 0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x31, + 0x32, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x33, + 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, + 0x32, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x37, 0x7d, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x31, 0x31, 0x2c, 0x20, 0x31, 0x35, + 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x39, + 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x34, 0x2c, 0x20, + 0x31, 0x30, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x32, 0x2c, 0x20, 0x33, + 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x33, 0x7d, 0x2c, + 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x72, 0x6f, 0x74, 0x72, 0x33, 0x32, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x77, 0x20, 0x3e, 0x3e, 0x20, 0x63, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x77, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x63, + 0x29, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x51, 0x75, + 0x61, 0x72, 0x74, 0x65, 0x72, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x67, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x61, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x62, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x78, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x29, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x61, 0x5d, + 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x61, 0x5d, 0x20, + 0x2b, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x62, 0x5d, 0x20, 0x2b, + 0x20, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x74, 0x72, 0x33, + 0x32, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x64, 0x5d, 0x20, 0x5e, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x61, 0x5d, 0x2c, 0x20, 0x31, + 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x63, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5b, 0x63, 0x5d, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x62, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x74, 0x72, 0x33, + 0x32, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x62, 0x5d, 0x20, 0x5e, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x63, 0x5d, 0x2c, 0x20, 0x31, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x61, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5b, 0x61, 0x5d, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x62, 0x5d, 0x20, 0x2b, 0x20, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x72, + 0x6f, 0x74, 0x72, 0x33, 0x32, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x64, 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x61, + 0x5d, 0x2c, 0x20, 0x38, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5b, 0x63, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x63, 0x5d, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5b, 0x62, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x6f, + 0x74, 0x72, 0x33, 0x32, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x62, + 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x63, 0x5d, + 0x2c, 0x20, 0x37, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x5f, 0x66, 0x6e, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x6d, 0x73, 0x67, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, + 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x4d, + 0x53, 0x47, 0x5f, 0x53, 0x43, 0x48, 0x45, 0x44, 0x55, 0x4c, 0x45, 0x5b, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x34, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x31, 0x32, 0x2c, 0x20, 0x6d, 0x73, + 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x30, + 0x5d, 0x5d, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x31, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, + 0x31, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x31, 0x33, 0x2c, + 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x5b, 0x32, 0x5d, 0x5d, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x33, 0x5d, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x30, 0x2c, + 0x20, 0x31, 0x34, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x34, 0x5d, 0x5d, 0x2c, 0x20, 0x6d, + 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, + 0x35, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x28, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x37, 0x2c, + 0x20, 0x31, 0x31, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x6d, 0x73, 0x67, + 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x36, 0x5d, + 0x5d, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x5b, 0x37, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x30, + 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x31, 0x35, 0x2c, + 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x5b, 0x38, 0x5d, 0x5d, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x39, 0x5d, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x31, 0x31, 0x2c, + 0x20, 0x31, 0x32, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x31, 0x30, 0x5d, 0x5d, 0x2c, 0x20, + 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x5b, 0x31, 0x31, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x32, 0x2c, 0x20, + 0x37, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, 0x6d, 0x73, + 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x31, + 0x32, 0x5d, 0x5d, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x31, 0x33, 0x5d, 0x5d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x67, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x31, + 0x34, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x5b, 0x31, 0x34, 0x5d, 0x5d, 0x2c, 0x20, 0x6d, 0x73, + 0x67, 0x5b, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5b, 0x31, + 0x35, 0x5d, 0x5d, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, + 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x20, 0x73, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x63, 0x76, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x6d, 0x73, 0x67, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6c, + 0x65, 0x6e, 0x2c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x31, 0x36, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, + 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, 0x31, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, 0x32, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x33, 0x5d, + 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x34, 0x5d, 0x20, 0x3d, + 0x20, 0x63, 0x76, 0x5b, 0x34, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x63, + 0x76, 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, + 0x36, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x76, 0x5b, 0x37, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x39, 0x5d, 0x20, + 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x30, 0x5d, 0x20, 0x3d, 0x20, + 0x49, 0x56, 0x5f, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, + 0x5f, 0x33, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x29, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x33, 0x5d, + 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x28, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x3e, 0x3e, 0x20, 0x33, 0x32, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x31, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x6c, 0x65, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x37, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x72, 0x20, 0x3c, 0x20, 0x37, 0x3b, + 0x20, 0x72, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x6e, + 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x2c, + 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x30, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x30, 0x5d, + 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x38, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x31, 0x5d, 0x20, 0x3d, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x39, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x76, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x31, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x76, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5b, 0x31, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, + 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, + 0x32, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x35, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x35, 0x5d, + 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x33, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x36, 0x5d, 0x20, + 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x36, 0x5d, 0x20, 0x5e, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x34, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x37, 0x5d, 0x20, 0x5e, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x35, 0x5d, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x2f, 0x2f, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x61, 0x20, 0x44, + 0x65, 0x63, 0x72, 0x65, 0x64, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x28, 0x31, 0x38, 0x30, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x33, 0x20, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x29, 0x0a, 0x2f, 0x2f, 0x20, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x20, 0x30, 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, + 0x30, 0x2d, 0x36, 0x33, 0x20, 0x28, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x5f, + 0x53, 0x54, 0x41, 0x52, 0x54, 0x29, 0x0a, 0x2f, 0x2f, 0x20, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x20, 0x31, 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x20, 0x36, 0x34, 0x2d, 0x31, 0x32, 0x37, 0x0a, 0x2f, 0x2f, 0x20, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x32, 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x20, 0x31, 0x32, 0x38, 0x2d, 0x31, 0x37, 0x39, 0x20, 0x28, 0x35, + 0x32, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2c, 0x20, 0x43, 0x48, 0x55, + 0x4e, 0x4b, 0x5f, 0x45, 0x4e, 0x44, 0x20, 0x7c, 0x20, 0x52, 0x4f, 0x4f, + 0x54, 0x29, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x33, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, + 0x68, 0x61, 0x73, 0x68, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x76, 0x5b, 0x38, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x73, 0x67, + 0x5b, 0x31, 0x36, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x20, 0x43, 0x56, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x49, 0x56, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, + 0x49, 0x56, 0x5f, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, + 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, + 0x49, 0x56, 0x5f, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, + 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x33, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x49, 0x56, 0x5f, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, + 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x35, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, 0x5b, 0x36, 0x5d, 0x20, 0x3d, 0x20, + 0x49, 0x56, 0x5f, 0x36, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x76, + 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x49, 0x56, 0x5f, 0x37, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x20, 0x30, 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x30, + 0x2d, 0x36, 0x33, 0x20, 0x28, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x5f, 0x53, + 0x54, 0x41, 0x52, 0x54, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x69, + 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x28, 0x63, 0x76, 0x2c, 0x20, 0x6d, 0x73, 0x67, + 0x2c, 0x20, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x33, 0x5f, 0x42, 0x4c, 0x4f, + 0x43, 0x4b, 0x5f, 0x4c, 0x45, 0x4e, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x43, + 0x48, 0x55, 0x4e, 0x4b, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x20, 0x31, 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, + 0x36, 0x34, 0x2d, 0x31, 0x32, 0x37, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, + 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x31, 0x36, 0x20, 0x2b, 0x20, + 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x28, 0x63, + 0x76, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x2c, 0x20, 0x42, 0x4c, 0x41, 0x4b, + 0x45, 0x33, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x4c, 0x45, 0x4e, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x32, + 0x3a, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x31, 0x32, 0x38, 0x2d, + 0x31, 0x37, 0x39, 0x20, 0x28, 0x35, 0x32, 0x20, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x20, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x31, + 0x34, 0x30, 0x2d, 0x31, 0x34, 0x33, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x61, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x20, 0x31, 0x34, 0x30, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, + 0x68, 0x20, 0x69, 0x73, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x20, 0x33, 0x35, 0x20, 0x28, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x5b, 0x33, 0x35, 0x5d, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x20, 0x32, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, + 0x33, 0x35, 0x2d, 0x33, 0x32, 0x20, 0x3d, 0x20, 0x33, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, + 0x33, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x33, 0x32, + 0x20, 0x2b, 0x20, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x73, 0x65, + 0x72, 0x74, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x20, 0x31, 0x34, 0x30, 0x20, 0x3d, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x20, 0x31, 0x32, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x20, 0x32, 0x20, 0x3d, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x20, 0x33, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x73, 0x67, 0x5b, + 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x3b, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x31, 0x34, + 0x30, 0x2d, 0x31, 0x34, 0x33, 0x20, 0x3d, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x20, 0x33, 0x35, 0x20, 0x3d, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x33, 0x5d, + 0x20, 0x69, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x32, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x5a, 0x65, 0x72, 0x6f, + 0x2d, 0x70, 0x61, 0x64, 0x20, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, + 0x6e, 0x67, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x20, 0x3d, 0x20, 0x31, 0x33, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, + 0x36, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x73, 0x67, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x28, 0x63, 0x76, 0x2c, 0x20, 0x6d, 0x73, 0x67, 0x2c, 0x20, 0x35, + 0x32, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x43, 0x48, 0x55, 0x4e, 0x4b, 0x5f, + 0x45, 0x4e, 0x44, 0x20, 0x7c, 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x20, 0x68, 0x61, 0x73, 0x68, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x38, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x5b, 0x69, 0x5d, 0x20, + 0x3d, 0x20, 0x63, 0x76, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x61, 0x67, + 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x20, 0x28, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x2d, 0x65, 0x6e, 0x64, + 0x69, 0x61, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x29, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x62, + 0x6f, 0x6f, 0x6c, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x68, 0x61, 0x73, 0x68, 0x2c, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x6f, + 0x72, 0x20, 0x44, 0x65, 0x63, 0x72, 0x65, 0x64, 0x2c, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, + 0x38, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x68, + 0x61, 0x73, 0x68, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x68, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x68, 0x61, 0x73, 0x68, 0x5b, 0x31, 0x5d, + 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x32, 0x29, 0x20, 0x7c, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x20, 0x3c, 0x3d, 0x20, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x5f, 0x5f, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x33, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x2a, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x2c, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x31, 0x38, 0x30, 0x2d, 0x62, 0x79, + 0x74, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x20, 0x28, 0x34, 0x35, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2c, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x69, 0x66, 0x66, 0x69, + 0x63, 0x75, 0x6c, 0x74, 0x79, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x2c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x3a, + 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x73, 0x74, 0x6f, + 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x6f, 0x70, 0x20, 0x66, + 0x6c, 0x61, 0x67, 0x0a, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x2a, 0x73, 0x74, 0x6f, 0x70, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x69, 0x64, 0x20, 0x3d, + 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x4c, 0x6f, 0x61, 0x64, 0x20, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x5b, 0x34, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x34, 0x35, 0x3b, + 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x69, + 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x61, 0x6c, 0x63, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x68, 0x5b, 0x38, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x33, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x28, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x2c, 0x20, 0x67, 0x69, 0x64, 0x2c, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, + 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x28, 0x68, 0x61, 0x73, 0x68, 0x2c, 0x20, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x73, 0x74, 0x6f, 0x70, 0x20, + 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x6b, 0x20, 0x3d, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, + 0x6e, 0x63, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x29, 0x20, + 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x6b, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x35, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, + 0x6b, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x00 +}; + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/cl/etchash/etchash.cl b/miner/core/src/backend/opencl/cl/etchash/etchash.cl new file mode 100644 index 0000000..2dde661 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/etchash/etchash.cl @@ -0,0 +1,362 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * ETChash/Ethash OpenCL mining kernel + * Based on various open-source Ethash implementations + * + * 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 . + */ + +// +// ETChash/Ethash mining kernel +// Standard Ethash algorithm: DAG lookup + Keccak +// + +#define FNV_PRIME 0x01000193 +#define ETHASH_ACCESSES 64 +#define ETHASH_MIX_BYTES 128 +#define ETHASH_HASH_BYTES 64 + +#ifndef GROUP_SIZE +#define GROUP_SIZE 128 +#endif + +#ifndef PROGPOW_DAG_ELEMENTS +#define PROGPOW_DAG_ELEMENTS 0 +#endif + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +#if PLATFORM == OPENCL_PLATFORM_NVIDIA && COMPUTE >= 35 +static uint2 ROL2(const uint2 a, const int offset) +{ + uint2 result; + if (offset >= 32) + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.x), "r"(a.y), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.y), "r"(a.x), "r"(offset)); + } + else + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.y), "r"(a.x), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.x), "r"(a.y), "r"(offset)); + } + return result; +} +#elif defined(cl_amd_media_ops) +#pragma OPENCL EXTENSION cl_amd_media_ops : enable +static uint2 ROL2(const uint2 vv, const int r) +{ + if (r <= 32) + { + return amd_bitalign((vv).xy, (vv).yx, 32 - r); + } + else + { + return amd_bitalign((vv).yx, (vv).xy, 64 - r); + } +} +#else +static uint2 ROL2(const uint2 v, const int n) +{ + uint2 result; + if (n <= 32) + { + result.y = ((v.y << (n)) | (v.x >> (32 - n))); + result.x = ((v.x << (n)) | (v.y >> (32 - n))); + } + else + { + result.y = ((v.x << (n - 32)) | (v.y >> (64 - n))); + result.x = ((v.y << (n - 32)) | (v.x >> (64 - n))); + } + return result; +} +#endif + +static void chi(uint2* a, const uint n, const uint2* t) +{ + a[n + 0] = bitselect(t[n + 0] ^ t[n + 2], t[n + 0], t[n + 1]); + a[n + 1] = bitselect(t[n + 1] ^ t[n + 3], t[n + 1], t[n + 2]); + a[n + 2] = bitselect(t[n + 2] ^ t[n + 4], t[n + 2], t[n + 3]); + a[n + 3] = bitselect(t[n + 3] ^ t[n + 0], t[n + 3], t[n + 4]); + a[n + 4] = bitselect(t[n + 4] ^ t[n + 1], t[n + 4], t[n + 0]); +} + +static void keccak_f1600_round(uint2* a, uint r) +{ + uint2 t[25]; + uint2 u; + + // Theta + t[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + t[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + t[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + t[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + t[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + u = t[4] ^ ROL2(t[1], 1); + a[0] ^= u; + a[5] ^= u; + a[10] ^= u; + a[15] ^= u; + a[20] ^= u; + u = t[0] ^ ROL2(t[2], 1); + a[1] ^= u; + a[6] ^= u; + a[11] ^= u; + a[16] ^= u; + a[21] ^= u; + u = t[1] ^ ROL2(t[3], 1); + a[2] ^= u; + a[7] ^= u; + a[12] ^= u; + a[17] ^= u; + a[22] ^= u; + u = t[2] ^ ROL2(t[4], 1); + a[3] ^= u; + a[8] ^= u; + a[13] ^= u; + a[18] ^= u; + a[23] ^= u; + u = t[3] ^ ROL2(t[0], 1); + a[4] ^= u; + a[9] ^= u; + a[14] ^= u; + a[19] ^= u; + a[24] ^= u; + + // Rho Pi + t[0] = a[0]; + t[10] = ROL2(a[1], 1); + t[20] = ROL2(a[2], 62); + t[5] = ROL2(a[3], 28); + t[15] = ROL2(a[4], 27); + + t[16] = ROL2(a[5], 36); + t[1] = ROL2(a[6], 44); + t[11] = ROL2(a[7], 6); + t[21] = ROL2(a[8], 55); + t[6] = ROL2(a[9], 20); + + t[7] = ROL2(a[10], 3); + t[17] = ROL2(a[11], 10); + t[2] = ROL2(a[12], 43); + t[12] = ROL2(a[13], 25); + t[22] = ROL2(a[14], 39); + + t[23] = ROL2(a[15], 41); + t[8] = ROL2(a[16], 45); + t[18] = ROL2(a[17], 15); + t[3] = ROL2(a[18], 21); + t[13] = ROL2(a[19], 8); + + t[14] = ROL2(a[20], 18); + t[24] = ROL2(a[21], 2); + t[9] = ROL2(a[22], 61); + t[19] = ROL2(a[23], 56); + t[4] = ROL2(a[24], 14); + + // Chi + chi(a, 0, t); + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + chi(a, 5, t); + chi(a, 10, t); + chi(a, 15, t); + chi(a, 20, t); +} + +static void keccak_f1600(uint2* a) +{ + for (uint r = 0; r < 24; ++r) + { + keccak_f1600_round(a, r); + } +} + +static uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +static uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +typedef union +{ + uint words[64 / sizeof(uint)]; + uint2 uint2s[64 / sizeof(uint2)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint words[128 / sizeof(uint)]; + uint4 uint4s[128 / sizeof(uint4)]; +} hash128_t; + +typedef union +{ + uint words[200 / sizeof(uint)]; + uint2 uint2s[200 / sizeof(uint2)]; +} hash200_t; + +// Keccak-256 final hash (first 4 uint2s = 256 bits) +static void keccak_f256(uint2* state) +{ + // Pad for Keccak-256 + for (uint i = 4; i < 25; ++i) + { + state[i] = (uint2)(0, 0); + } + state[4].x = 0x00000001; + state[8].y = 0x80000000; + keccak_f1600(state); +} + +__kernel void ethash_search( + __global uint4 const* g_dag, + __global uint const* g_header, + ulong target, + uint hack_false, + __global uint* results, + __global uint* stop) +{ + if (*stop) + return; + + const uint gid = get_global_id(0); + + // Initialize state with header (32 bytes = 8 words) + hash200_t state; + for (uint i = 0; i < 25; ++i) + state.uint2s[i] = (uint2)(0, 0); + + // Load header hash (32 bytes) + state.words[0] = g_header[0]; + state.words[1] = g_header[1]; + state.words[2] = g_header[2]; + state.words[3] = g_header[3]; + state.words[4] = g_header[4]; + state.words[5] = g_header[5]; + state.words[6] = g_header[6]; + state.words[7] = g_header[7]; + + // Add nonce (8 bytes) + state.words[8] = gid; + state.words[9] = 0; + + // Keccak-512 padding + state.words[10] = 0x00000001; + state.uint2s[8].y = 0x80000000; + + // Keccak-512 to get seed + keccak_f1600(state.uint2s); + + // Initialize mix (128 bytes = 32 words) + uint mix[32]; + for (uint i = 0; i < 16; ++i) + { + mix[i] = state.words[i % 16]; + mix[i + 16] = state.words[i % 16]; + } + + // DAG accesses + const uint dag_elements = PROGPOW_DAG_ELEMENTS; + for (uint i = 0; i < ETHASH_ACCESSES; ++i) + { + uint p = fnv(i ^ state.words[0], mix[i % 32]) % dag_elements; + + // Load 128 bytes from DAG (2 * 64 bytes) + uint4 dag_data[8]; + for (uint j = 0; j < 8; ++j) + { + dag_data[j] = g_dag[p * 2 + j / 4]; + } + + // FNV mix + for (uint j = 0; j < 32; ++j) + { + mix[j] = fnv(mix[j], ((uint*)dag_data)[j]); + } + } + + // Compress mix to 32 bytes (8 words) + uint cmix[8]; + for (uint i = 0; i < 8; ++i) + { + cmix[i] = fnv(fnv(fnv(mix[i*4], mix[i*4+1]), mix[i*4+2]), mix[i*4+3]); + } + + // Final Keccak-256 + hash200_t final_state; + for (uint i = 0; i < 25; ++i) + final_state.uint2s[i] = (uint2)(0, 0); + + // Copy seed state (first 8 words) and mix (8 words) + for (uint i = 0; i < 8; ++i) + { + final_state.words[i] = state.words[i]; + } + for (uint i = 0; i < 8; ++i) + { + final_state.words[8 + i] = cmix[i]; + } + + // Keccak-256 padding + final_state.words[16] = 0x00000001; + final_state.uint2s[8].y = 0x80000000; + + keccak_f1600(final_state.uint2s); + + // Check against target (compare first 8 bytes / 64 bits) + ulong result = as_ulong(as_uchar8((ulong)final_state.words[0] | ((ulong)final_state.words[1] << 32)).s76543210); + + if (result <= target) + { + *stop = 1; + const uint k = atomic_inc(results) + 1; + if (k <= 15) + results[k] = gid; + } +} diff --git a/miner/core/src/backend/opencl/cl/etchash/etchash_cl.h b/miner/core/src/backend/opencl/cl/etchash/etchash_cl.h new file mode 100644 index 0000000..0571cf1 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/etchash/etchash_cl.h @@ -0,0 +1,766 @@ +#pragma once + +namespace xmrig { + +static const char etchash_cl[] = { + 0x2f, 0x2a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x32, 0x35, 0x20, 0x4c, 0x65, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x45, + 0x54, 0x43, 0x68, 0x61, 0x73, 0x68, 0x2f, 0x45, 0x74, 0x68, 0x61, 0x73, + 0x68, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x20, 0x6d, 0x69, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x42, 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, + 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x70, 0x65, + 0x6e, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x74, 0x68, + 0x61, 0x73, 0x68, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x2a, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, + 0x67, 0x72, 0x61, 0x6d, 0x20, 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, + 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x3a, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69, 0x74, 0x20, 0x61, + 0x6e, 0x64, 0x2f, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x61, + 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x46, 0x72, 0x65, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, + 0x65, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2c, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x6f, + 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x28, 0x61, 0x74, 0x20, 0x79, + 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, + 0x61, 0x6e, 0x79, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, + 0x73, 0x65, 0x66, 0x75, 0x6c, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x62, 0x75, 0x74, 0x20, 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, + 0x41, 0x4e, 0x59, 0x20, 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, + 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x65, 0x76, + 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69, + 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x20, + 0x6f, 0x66, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x4d, 0x45, 0x52, 0x43, + 0x48, 0x41, 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, + 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, + 0x4f, 0x52, 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, + 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, + 0x20, 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, + 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, + 0x65, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, + 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x70, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, + 0x74, 0x2c, 0x20, 0x73, 0x65, 0x65, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x2f, + 0x3e, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, + 0x2f, 0x20, 0x45, 0x54, 0x43, 0x68, 0x61, 0x73, 0x68, 0x2f, 0x45, 0x74, + 0x68, 0x61, 0x73, 0x68, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x0a, 0x2f, 0x2f, 0x20, 0x53, 0x74, + 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x45, 0x74, 0x68, 0x61, 0x73, + 0x68, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x3a, + 0x20, 0x44, 0x41, 0x47, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x20, + 0x2b, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x0a, 0x2f, 0x2f, 0x0a, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x46, 0x4e, 0x56, + 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x45, 0x20, 0x30, 0x78, 0x30, 0x31, 0x30, + 0x30, 0x30, 0x31, 0x39, 0x33, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x45, 0x54, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x41, 0x43, 0x43, + 0x45, 0x53, 0x53, 0x45, 0x53, 0x20, 0x36, 0x34, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x45, 0x54, 0x48, 0x41, 0x53, 0x48, 0x5f, + 0x4d, 0x49, 0x58, 0x5f, 0x42, 0x59, 0x54, 0x45, 0x53, 0x20, 0x31, 0x32, + 0x38, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x45, 0x54, + 0x48, 0x41, 0x53, 0x48, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x42, 0x59, + 0x54, 0x45, 0x53, 0x20, 0x36, 0x34, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x6e, + 0x64, 0x65, 0x66, 0x20, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x49, + 0x5a, 0x45, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x47, + 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x20, 0x31, 0x32, + 0x38, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x23, 0x69, + 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, + 0x57, 0x5f, 0x44, 0x41, 0x47, 0x5f, 0x45, 0x4c, 0x45, 0x4d, 0x45, 0x4e, + 0x54, 0x53, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x50, + 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, 0x41, 0x47, 0x5f, 0x45, + 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x53, 0x20, 0x30, 0x0a, 0x23, 0x65, + 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x5f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, + 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x52, 0x43, 0x5b, 0x32, 0x34, 0x5d, + 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x38, 0x30, 0x38, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x38, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x61, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x38, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, + 0x39, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, + 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x39, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x38, 0x30, 0x30, 0x33, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x61, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x2c, 0x20, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, + 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, + 0x30, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x50, 0x4c, 0x41, 0x54, 0x46, 0x4f, 0x52, 0x4d, 0x20, 0x3d, 0x3d, + 0x20, 0x4f, 0x50, 0x45, 0x4e, 0x43, 0x4c, 0x5f, 0x50, 0x4c, 0x41, 0x54, + 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0x20, + 0x26, 0x26, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x45, 0x20, 0x3e, + 0x3d, 0x20, 0x33, 0x35, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x61, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3e, 0x3d, 0x20, 0x33, 0x32, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, + 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, + 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, + 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x3a, 0x20, + 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, + 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, + 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, + 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, + 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, + 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x29, 0x20, + 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, + 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, + 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, + 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, + 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, + 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, + 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, + 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x3a, + 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, + 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, + 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, + 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, + 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, + 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, + 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x29, + 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, + 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, + 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x63, 0x6c, 0x5f, 0x61, 0x6d, 0x64, + 0x5f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x6f, 0x70, 0x73, 0x29, 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, 0x20, 0x3a, 0x20, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x76, 0x76, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x72, + 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x72, 0x20, 0x3c, 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6d, 0x64, 0x5f, 0x62, 0x69, + 0x74, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x28, 0x28, 0x76, 0x76, 0x29, 0x2e, + 0x78, 0x79, 0x2c, 0x20, 0x28, 0x76, 0x76, 0x29, 0x2e, 0x79, 0x78, 0x2c, + 0x20, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6d, + 0x64, 0x5f, 0x62, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x28, 0x28, + 0x76, 0x76, 0x29, 0x2e, 0x79, 0x78, 0x2c, 0x20, 0x28, 0x76, 0x76, 0x29, + 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x72, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x23, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x76, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6e, + 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x20, 0x3c, 0x3d, 0x20, 0x33, + 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, + 0x79, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3c, 0x3c, + 0x20, 0x28, 0x6e, 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x78, + 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x6e, 0x29, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, + 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x29, 0x29, + 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x28, + 0x33, 0x32, 0x20, 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, + 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3c, 0x3c, 0x20, + 0x28, 0x6e, 0x20, 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, 0x20, 0x7c, 0x20, + 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x36, 0x34, 0x20, + 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, + 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3c, 0x3c, 0x20, + 0x28, 0x6e, 0x20, 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, 0x20, 0x7c, 0x20, + 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x36, 0x34, 0x20, + 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, + 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x63, 0x68, 0x69, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, + 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, + 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x2c, + 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, + 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x20, 0x5e, 0x20, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, 0x20, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, + 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x34, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, + 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x20, + 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x2c, 0x20, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, 0x20, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x34, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, + 0x5d, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, + 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x74, 0x5b, 0x32, 0x35, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x20, 0x75, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x54, 0x68, 0x65, 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x35, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x30, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x5e, 0x20, 0x61, + 0x5b, 0x32, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x36, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x31, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x5e, 0x20, 0x61, + 0x5b, 0x32, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x37, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x32, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x5e, 0x20, 0x61, + 0x5b, 0x32, 0x32, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x38, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x33, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x5e, 0x20, 0x61, + 0x5b, 0x32, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x39, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x34, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x5e, 0x20, 0x61, + 0x5b, 0x32, 0x34, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, + 0x3d, 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x74, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x35, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x31, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, + 0x3d, 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x74, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x36, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x31, 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x31, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, + 0x3d, 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x74, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x37, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x31, 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x32, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, + 0x3d, 0x20, 0x74, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x74, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x38, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x31, 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, + 0x3d, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x74, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x39, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x31, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x34, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x52, 0x68, 0x6f, 0x20, 0x50, 0x69, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x30, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x30, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, + 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x32, 0x5d, 0x2c, 0x20, 0x36, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x32, 0x38, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x32, + 0x37, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, + 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x35, 0x5d, 0x2c, 0x20, 0x33, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x34, 0x34, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x36, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x31, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x35, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, + 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x32, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x33, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x37, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x31, 0x5d, + 0x2c, 0x20, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, + 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, 0x34, 0x33, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x52, + 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x32, + 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x32, + 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, + 0x34, 0x5d, 0x2c, 0x20, 0x33, 0x39, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x32, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x34, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x38, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x36, 0x5d, + 0x2c, 0x20, 0x34, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, + 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x2c, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x52, + 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x2c, 0x20, 0x32, + 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x33, + 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, + 0x39, 0x5d, 0x2c, 0x20, 0x38, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x2c, 0x20, 0x31, 0x38, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x34, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x31, 0x5d, + 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x32, 0x32, 0x5d, 0x2c, 0x20, 0x36, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x2c, 0x20, 0x35, 0x36, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x34, 0x5d, + 0x2c, 0x20, 0x31, 0x34, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x43, 0x68, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x74, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6f, 0x74, + 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, + 0x30, 0x30, 0x5f, 0x52, 0x43, 0x5b, 0x72, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x35, 0x2c, + 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, + 0x28, 0x61, 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x31, + 0x35, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x74, 0x29, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, + 0x66, 0x31, 0x36, 0x30, 0x30, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, + 0x20, 0x61, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x72, 0x20, 0x3c, 0x20, 0x32, 0x34, 0x3b, 0x20, 0x2b, + 0x2b, 0x72, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x28, 0x61, 0x2c, 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6e, 0x76, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x29, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, + 0x49, 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x20, 0x66, 0x6e, 0x76, 0x34, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, + 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x79, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, 0x49, + 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, + 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x36, 0x34, 0x20, + 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x34, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x36, 0x34, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x5b, 0x31, 0x32, 0x38, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, + 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x34, 0x73, 0x5b, 0x31, 0x32, 0x38, 0x20, 0x2f, 0x20, 0x73, 0x69, + 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x29, 0x5d, + 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x31, 0x32, 0x38, 0x5f, + 0x74, 0x3b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, + 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x32, + 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, + 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, + 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, + 0x20, 0x68, 0x61, 0x73, 0x68, 0x32, 0x30, 0x30, 0x5f, 0x74, 0x3b, 0x0a, + 0x0a, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x2d, 0x32, + 0x35, 0x36, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x68, 0x61, 0x73, + 0x68, 0x20, 0x28, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x34, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x73, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x36, 0x20, + 0x62, 0x69, 0x74, 0x73, 0x29, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x66, 0x32, 0x35, 0x36, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x50, 0x61, 0x64, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x2d, 0x32, 0x35, 0x36, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x34, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x32, 0x35, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x34, 0x5d, 0x2e, 0x78, 0x20, 0x3d, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x38, + 0x5d, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, + 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x28, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x5f, 0x5f, + 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, + 0x65, 0x74, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, + 0x68, 0x28, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x2a, 0x20, 0x67, 0x5f, 0x64, 0x61, 0x67, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x2a, + 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, + 0x20, 0x73, 0x74, 0x6f, 0x70, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x2a, 0x73, 0x74, 0x6f, 0x70, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x69, 0x64, 0x20, + 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x28, 0x33, 0x32, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x38, 0x20, 0x77, + 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x32, 0x30, 0x30, 0x5f, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x32, 0x35, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x4c, 0x6f, 0x61, 0x64, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x28, 0x33, 0x32, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x30, 0x5d, + 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, + 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x31, 0x5d, 0x20, 0x3d, + 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x31, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x67, + 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x32, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, + 0x72, 0x64, 0x73, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x5b, 0x34, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, + 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x36, 0x5d, + 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, + 0x36, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x37, 0x5d, 0x20, 0x3d, + 0x20, 0x67, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5b, 0x37, 0x5d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x64, + 0x64, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x28, 0x38, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x38, 0x5d, + 0x20, 0x3d, 0x20, 0x67, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, + 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x2d, 0x35, + 0x31, 0x32, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, + 0x64, 0x73, 0x5b, 0x31, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x73, 0x5b, 0x38, 0x5d, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x2d, + 0x35, 0x31, 0x32, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x73, + 0x65, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x28, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x29, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x6d, 0x69, 0x78, 0x20, 0x28, + 0x31, 0x32, 0x38, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20, + 0x33, 0x32, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x33, + 0x32, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x2b, 0x2b, 0x69, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x5d, 0x20, 0x3d, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x5b, 0x69, 0x20, 0x25, 0x20, 0x31, 0x36, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x20, + 0x2b, 0x20, 0x31, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x69, 0x20, 0x25, 0x20, + 0x31, 0x36, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x44, 0x41, 0x47, 0x20, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x64, + 0x61, 0x67, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x3d, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, 0x41, + 0x47, 0x5f, 0x45, 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x53, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, + 0x20, 0x45, 0x54, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x41, 0x43, 0x43, 0x45, + 0x53, 0x53, 0x45, 0x53, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x6e, + 0x76, 0x28, 0x69, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x6d, 0x69, + 0x78, 0x5b, 0x69, 0x20, 0x25, 0x20, 0x33, 0x32, 0x5d, 0x29, 0x20, 0x25, + 0x20, 0x64, 0x61, 0x67, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x4c, 0x6f, 0x61, 0x64, 0x20, 0x31, 0x32, 0x38, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x44, + 0x41, 0x47, 0x20, 0x28, 0x32, 0x20, 0x2a, 0x20, 0x36, 0x34, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x64, 0x61, 0x67, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x5b, 0x38, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x6a, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x6a, 0x20, + 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x2b, 0x2b, 0x6a, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x67, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x5b, 0x6a, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x5f, + 0x64, 0x61, 0x67, 0x5b, 0x70, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, + 0x6a, 0x20, 0x2f, 0x20, 0x34, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x46, 0x4e, 0x56, 0x20, 0x6d, 0x69, + 0x78, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6a, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x6a, 0x20, 0x3c, 0x20, 0x33, 0x32, 0x3b, 0x20, 0x2b, + 0x2b, 0x6a, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x6a, 0x5d, 0x20, 0x3d, 0x20, 0x66, + 0x6e, 0x76, 0x28, 0x6d, 0x69, 0x78, 0x5b, 0x6a, 0x5d, 0x2c, 0x20, 0x28, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x29, 0x64, 0x61, 0x67, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x29, 0x5b, 0x6a, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6d, 0x69, 0x78, 0x20, 0x74, + 0x6f, 0x20, 0x33, 0x32, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x28, + 0x38, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6d, 0x69, 0x78, 0x5b, 0x38, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, + 0x66, 0x6e, 0x76, 0x28, 0x66, 0x6e, 0x76, 0x28, 0x66, 0x6e, 0x76, 0x28, + 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x2a, 0x34, 0x5d, 0x2c, 0x20, 0x6d, 0x69, + 0x78, 0x5b, 0x69, 0x2a, 0x34, 0x2b, 0x31, 0x5d, 0x29, 0x2c, 0x20, 0x6d, + 0x69, 0x78, 0x5b, 0x69, 0x2a, 0x34, 0x2b, 0x32, 0x5d, 0x29, 0x2c, 0x20, + 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x2a, 0x34, 0x2b, 0x33, 0x5d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x4b, 0x65, 0x63, + 0x63, 0x61, 0x6b, 0x2d, 0x32, 0x35, 0x36, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x68, 0x61, 0x73, 0x68, 0x32, 0x30, 0x30, 0x5f, 0x74, 0x20, 0x66, 0x69, + 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, + 0x32, 0x35, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x5b, + 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x20, 0x73, 0x65, 0x65, + 0x64, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x28, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x20, 0x38, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x69, 0x78, 0x20, 0x28, 0x38, 0x20, 0x77, + 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x2b, 0x2b, + 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x69, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, + 0x72, 0x64, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, + 0x20, 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x38, 0x20, 0x2b, 0x20, 0x69, + 0x5d, 0x20, 0x3d, 0x20, 0x63, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x2d, 0x32, 0x35, + 0x36, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x31, 0x36, 0x5d, 0x20, + 0x3d, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, + 0x5b, 0x38, 0x5d, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, + 0x30, 0x28, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x20, 0x28, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x38, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x20, 0x2f, 0x20, 0x36, 0x34, 0x20, 0x62, 0x69, 0x74, 0x73, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x61, 0x73, 0x5f, + 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x28, 0x61, 0x73, 0x5f, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x38, 0x28, 0x28, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x66, + 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, + 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x30, 0x5d, 0x20, 0x7c, 0x20, 0x28, 0x28, + 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, + 0x31, 0x5d, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x32, 0x29, 0x29, 0x2e, 0x73, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x20, 0x3c, 0x3d, 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2a, 0x73, 0x74, 0x6f, 0x70, 0x20, 0x3d, 0x20, + 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6b, 0x20, + 0x3d, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x63, + 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x29, 0x20, 0x2b, 0x20, + 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x6b, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x35, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, 0x6b, 0x5d, 0x20, 0x3d, + 0x20, 0x67, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x00 +}; + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/cl/etchash/etchash_dag.cl b/miner/core/src/backend/opencl/cl/etchash/etchash_dag.cl new file mode 100644 index 0000000..00ac423 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/etchash/etchash_dag.cl @@ -0,0 +1,279 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow OpenCL implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +// +// ETChash DAG calculation kernel +// Standard Ethash DAG with 256 parents (not 512 like ProgPow) +// + +#define FNV_PRIME 0x01000193 +#define ETHASH_DATASET_PARENTS 256 +#define NODE_WORDS (64 / 4) + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +#if PLATFORM == OPENCL_PLATFORM_NVIDIA && COMPUTE >= 35 +static uint2 ROL2(const uint2 a, const int offset) +{ + uint2 result; + if (offset >= 32) + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.x), "r"(a.y), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.y), "r"(a.x), "r"(offset)); + } + else + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.y), "r"(a.x), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.x), "r"(a.y), "r"(offset)); + } + return result; +} +#elif defined(cl_amd_media_ops) +#pragma OPENCL EXTENSION cl_amd_media_ops : enable +static uint2 ROL2(const uint2 vv, const int r) +{ + if (r <= 32) + { + return amd_bitalign((vv).xy, (vv).yx, 32 - r); + } + else + { + return amd_bitalign((vv).yx, (vv).xy, 64 - r); + } +} +#else +static uint2 ROL2(const uint2 v, const int n) +{ + uint2 result; + if (n <= 32) + { + result.y = ((v.y << (n)) | (v.x >> (32 - n))); + result.x = ((v.x << (n)) | (v.y >> (32 - n))); + } + else + { + result.y = ((v.x << (n - 32)) | (v.y >> (64 - n))); + result.x = ((v.y << (n - 32)) | (v.x >> (64 - n))); + } + return result; +} +#endif + +static void chi(uint2* a, const uint n, const uint2* t) +{ + a[n + 0] = bitselect(t[n + 0] ^ t[n + 2], t[n + 0], t[n + 1]); + a[n + 1] = bitselect(t[n + 1] ^ t[n + 3], t[n + 1], t[n + 2]); + a[n + 2] = bitselect(t[n + 2] ^ t[n + 4], t[n + 2], t[n + 3]); + a[n + 3] = bitselect(t[n + 3] ^ t[n + 0], t[n + 3], t[n + 4]); + a[n + 4] = bitselect(t[n + 4] ^ t[n + 1], t[n + 4], t[n + 0]); +} + +static void keccak_f1600_round(uint2* a, uint r) +{ + uint2 t[25]; + uint2 u; + + // Theta + t[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + t[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + t[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + t[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + t[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + u = t[4] ^ ROL2(t[1], 1); + a[0] ^= u; + a[5] ^= u; + a[10] ^= u; + a[15] ^= u; + a[20] ^= u; + u = t[0] ^ ROL2(t[2], 1); + a[1] ^= u; + a[6] ^= u; + a[11] ^= u; + a[16] ^= u; + a[21] ^= u; + u = t[1] ^ ROL2(t[3], 1); + a[2] ^= u; + a[7] ^= u; + a[12] ^= u; + a[17] ^= u; + a[22] ^= u; + u = t[2] ^ ROL2(t[4], 1); + a[3] ^= u; + a[8] ^= u; + a[13] ^= u; + a[18] ^= u; + a[23] ^= u; + u = t[3] ^ ROL2(t[0], 1); + a[4] ^= u; + a[9] ^= u; + a[14] ^= u; + a[19] ^= u; + a[24] ^= u; + + // Rho Pi + t[0] = a[0]; + t[10] = ROL2(a[1], 1); + t[20] = ROL2(a[2], 62); + t[5] = ROL2(a[3], 28); + t[15] = ROL2(a[4], 27); + + t[16] = ROL2(a[5], 36); + t[1] = ROL2(a[6], 44); + t[11] = ROL2(a[7], 6); + t[21] = ROL2(a[8], 55); + t[6] = ROL2(a[9], 20); + + t[7] = ROL2(a[10], 3); + t[17] = ROL2(a[11], 10); + t[2] = ROL2(a[12], 43); + t[12] = ROL2(a[13], 25); + t[22] = ROL2(a[14], 39); + + t[23] = ROL2(a[15], 41); + t[8] = ROL2(a[16], 45); + t[18] = ROL2(a[17], 15); + t[3] = ROL2(a[18], 21); + t[13] = ROL2(a[19], 8); + + t[14] = ROL2(a[20], 18); + t[24] = ROL2(a[21], 2); + t[9] = ROL2(a[22], 61); + t[19] = ROL2(a[23], 56); + t[4] = ROL2(a[24], 14); + + // Chi + chi(a, 0, t); + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + chi(a, 5, t); + chi(a, 10, t); + chi(a, 15, t); + chi(a, 20, t); +} + +static void keccak_f1600_no_absorb(uint2* a, uint out_size, uint isolate) +{ + for (uint r = 0; r < 24;) + { + if (isolate) + { + keccak_f1600_round(a, r++); + } + } +} + +#define copy(dst, src, count) \ + for (uint i = 0; i != count; ++i) \ + { \ + (dst)[i] = (src)[i]; \ + } + +static uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +static uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +typedef union +{ + uint words[64 / sizeof(uint)]; + uint2 uint2s[64 / sizeof(uint2)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint words[200 / sizeof(uint)]; + uint2 uint2s[200 / sizeof(uint2)]; + uint4 uint4s[200 / sizeof(uint4)]; +} hash200_t; + +static void SHA3_512(uint2* s, uint isolate) +{ + for (uint i = 8; i != 25; ++i) + { + s[i] = (uint2){0, 0}; + } + s[8].x = 0x00000001; + s[8].y = 0x80000000; + keccak_f1600_no_absorb(s, 8, isolate); +} + +static uint fast_mod(uint a, uint4 d) +{ + const ulong t = a; + const uint q = ((t + d.y) * d.x) >> d.z; + return a - q * d.w; +} + +__kernel void ethash_calculate_dag_item(uint start, __global hash64_t const* g_light, __global hash64_t* g_dag, uint isolate, uint dag_words, uint4 light_words) +{ + uint const node_index = start + get_global_id(0); + if (node_index >= dag_words) + return; + + hash200_t dag_node; + copy(dag_node.uint4s, g_light[fast_mod(node_index, light_words)].uint4s, 4); + dag_node.words[0] ^= node_index; + SHA3_512(dag_node.uint2s, isolate); + + for (uint i = 0; i != ETHASH_DATASET_PARENTS; ++i) + { + uint parent_index = fast_mod(fnv(node_index ^ i, dag_node.words[i % NODE_WORDS]), light_words); + + for (uint w = 0; w != 4; ++w) + dag_node.uint4s[w] = fnv4(dag_node.uint4s[w], g_light[parent_index].uint4s[w]); + } + + SHA3_512(dag_node.uint2s, isolate); + copy(g_dag[node_index].uint4s, dag_node.uint4s, 4); +} diff --git a/miner/core/src/backend/opencl/cl/etchash/etchash_dag_cl.h b/miner/core/src/backend/opencl/cl/etchash/etchash_dag_cl.h new file mode 100644 index 0000000..bfb16ce --- /dev/null +++ b/miner/core/src/backend/opencl/cl/etchash/etchash_dag_cl.h @@ -0,0 +1,630 @@ +#pragma once + +namespace xmrig { + +static const char etchash_dag_cl[] = { + 0x2f, 0x2a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x32, 0x35, 0x20, 0x4c, 0x65, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x42, + 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x58, 0x4d, 0x52, 0x69, + 0x67, 0x20, 0x4b, 0x61, 0x77, 0x50, 0x6f, 0x77, 0x20, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x4c, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, + 0x31, 0x38, 0x2d, 0x32, 0x30, 0x32, 0x31, 0x20, 0x53, 0x43, 0x68, 0x65, + 0x72, 0x6e, 0x79, 0x6b, 0x68, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x43, 0x68, 0x65, 0x72, 0x6e, 0x79, 0x6b, + 0x68, 0x3e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x32, + 0x30, 0x32, 0x31, 0x20, 0x58, 0x4d, 0x52, 0x69, 0x67, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x6d, 0x72, 0x69, 0x67, 0x3e, 0x2c, 0x20, 0x3c, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x40, 0x78, 0x6d, 0x72, 0x69, 0x67, 0x2e, 0x63, + 0x6f, 0x6d, 0x3e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x20, 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x73, 0x6f, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x3a, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x20, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, + 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, + 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x69, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x33, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x28, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x61, 0x6e, 0x79, 0x20, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, + 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x6f, + 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20, + 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, 0x3b, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x20, 0x77, + 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, + 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, + 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, + 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, + 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x53, 0x65, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x64, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x2c, 0x20, 0x73, + 0x65, 0x65, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x2f, 0x3e, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x45, 0x54, + 0x43, 0x68, 0x61, 0x73, 0x68, 0x20, 0x44, 0x41, 0x47, 0x20, 0x63, 0x61, + 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x0a, 0x2f, 0x2f, 0x20, 0x53, 0x74, 0x61, 0x6e, + 0x64, 0x61, 0x72, 0x64, 0x20, 0x45, 0x74, 0x68, 0x61, 0x73, 0x68, 0x20, + 0x44, 0x41, 0x47, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x32, 0x35, 0x36, + 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x28, 0x6e, 0x6f, + 0x74, 0x20, 0x35, 0x31, 0x32, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x50, + 0x72, 0x6f, 0x67, 0x50, 0x6f, 0x77, 0x29, 0x0a, 0x2f, 0x2f, 0x0a, 0x0a, + 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x46, 0x4e, 0x56, 0x5f, + 0x50, 0x52, 0x49, 0x4d, 0x45, 0x20, 0x30, 0x78, 0x30, 0x31, 0x30, 0x30, + 0x30, 0x31, 0x39, 0x33, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x45, 0x54, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x53, 0x45, 0x54, 0x5f, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x53, 0x20, + 0x32, 0x35, 0x36, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x53, 0x20, 0x28, + 0x36, 0x34, 0x20, 0x2f, 0x20, 0x34, 0x29, 0x0a, 0x0a, 0x5f, 0x5f, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x4b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x52, 0x43, 0x5b, + 0x32, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x62, 0x2c, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, + 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, + 0x39, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, + 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x38, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x39, 0x2c, 0x20, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x33, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x2c, 0x20, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x61, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, + 0x38, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, + 0x30, 0x38, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x30, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x50, 0x4c, 0x41, 0x54, 0x46, 0x4f, 0x52, 0x4d, + 0x20, 0x3d, 0x3d, 0x20, 0x4f, 0x50, 0x45, 0x4e, 0x43, 0x4c, 0x5f, 0x50, + 0x4c, 0x41, 0x54, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x4e, 0x56, 0x49, 0x44, + 0x49, 0x41, 0x20, 0x26, 0x26, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, + 0x45, 0x20, 0x3e, 0x3d, 0x20, 0x33, 0x35, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x20, 0x61, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, + 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3e, 0x3d, + 0x20, 0x33, 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, + 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, + 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, + 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, + 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x29, + 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, + 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, + 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, + 0x28, 0x22, 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, + 0x2e, 0x62, 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, + 0x20, 0x25, 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, + 0x22, 0x3d, 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, + 0x79, 0x29, 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, + 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, + 0x20, 0x22, 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, + 0x22, 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, + 0x62, 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, + 0x25, 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, + 0x3d, 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, + 0x29, 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, + 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, + 0x22, 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, + 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, + 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, + 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, + 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x79, 0x29, 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, + 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, + 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, + 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x28, 0x63, 0x6c, 0x5f, + 0x61, 0x6d, 0x64, 0x5f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x6f, 0x70, + 0x73, 0x29, 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, 0x20, 0x3a, 0x20, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, + 0x76, 0x76, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x20, 0x72, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x72, 0x20, 0x3c, 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6d, 0x64, + 0x5f, 0x62, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x28, 0x28, 0x76, + 0x76, 0x29, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x28, 0x76, 0x76, 0x29, 0x2e, + 0x79, 0x78, 0x2c, 0x20, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x72, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x61, 0x6d, 0x64, 0x5f, 0x62, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x67, + 0x6e, 0x28, 0x28, 0x76, 0x76, 0x29, 0x2e, 0x79, 0x78, 0x2c, 0x20, 0x28, + 0x76, 0x76, 0x29, 0x2e, 0x78, 0x79, 0x2c, 0x20, 0x36, 0x34, 0x20, 0x2d, + 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, + 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x20, 0x76, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, + 0x74, 0x20, 0x6e, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x20, 0x3c, + 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x79, + 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x76, 0x2e, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, + 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x20, + 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3c, 0x3c, 0x20, 0x28, + 0x6e, 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3e, + 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x78, 0x20, + 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x20, 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, + 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x28, + 0x36, 0x34, 0x20, 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, 0x79, 0x20, + 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x20, 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, + 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x28, + 0x36, 0x34, 0x20, 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, + 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x63, 0x68, + 0x69, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6e, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x2a, 0x20, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x62, + 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x32, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x30, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, + 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, + 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x2c, 0x20, 0x74, + 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x20, 0x3d, + 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, + 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x32, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x33, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, + 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, + 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, + 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x20, 0x5e, 0x20, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x30, 0x5d, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, + 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, + 0x61, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x74, + 0x5b, 0x32, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x20, 0x75, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x30, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x35, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, + 0x31, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x20, + 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x31, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x36, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, + 0x31, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x20, + 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x32, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x37, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, + 0x31, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x20, + 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x32, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x33, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x38, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, + 0x31, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x20, + 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x34, 0x5d, + 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x39, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, + 0x31, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x20, + 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x34, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x35, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, + 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x36, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, + 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x37, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, + 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x33, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x38, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, + 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x34, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x39, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x5e, + 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, + 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x68, 0x6f, 0x20, 0x50, 0x69, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x61, + 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, + 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x31, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x5b, 0x32, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x36, 0x32, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x52, + 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x32, 0x38, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x35, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x34, 0x5d, + 0x2c, 0x20, 0x32, 0x37, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x33, 0x36, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x52, + 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x34, 0x34, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x31, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x37, 0x5d, + 0x2c, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x32, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, + 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x35, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x61, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x32, 0x30, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x37, 0x5d, 0x20, 0x3d, + 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x30, 0x5d, 0x2c, + 0x20, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, + 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x61, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, 0x34, 0x33, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x32, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x33, 0x5d, + 0x2c, 0x20, 0x32, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x32, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, + 0x61, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x33, 0x39, 0x29, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x33, 0x5d, 0x20, 0x3d, + 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x2c, + 0x20, 0x34, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x31, 0x36, 0x5d, 0x2c, 0x20, 0x34, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x2c, 0x20, 0x31, 0x35, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x38, 0x5d, + 0x2c, 0x20, 0x32, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x31, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, + 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x2c, 0x20, 0x38, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x2c, 0x20, + 0x31, 0x38, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, + 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x32, 0x31, 0x5d, 0x2c, 0x20, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x32, 0x32, 0x5d, 0x2c, 0x20, 0x36, 0x31, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x3d, + 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x2c, + 0x20, 0x35, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, + 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x32, 0x34, 0x5d, 0x2c, 0x20, 0x31, 0x34, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x68, 0x69, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x74, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x49, 0x6f, 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x30, + 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, + 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x52, 0x43, 0x5b, 0x72, 0x5d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, + 0x20, 0x35, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x74, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, + 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x32, 0x30, 0x2c, + 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x6e, 0x6f, 0x5f, + 0x61, 0x62, 0x73, 0x6f, 0x72, 0x62, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x2a, 0x20, 0x61, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x75, + 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x72, 0x20, 0x3c, + 0x20, 0x32, 0x34, 0x3b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x69, 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, + 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x28, 0x61, 0x2c, 0x20, 0x72, 0x2b, 0x2b, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x28, 0x64, 0x73, 0x74, 0x2c, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x20, + 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x64, 0x73, 0x74, 0x29, 0x5b, 0x69, 0x5d, 0x20, 0x3d, + 0x20, 0x28, 0x73, 0x72, 0x63, 0x29, 0x5b, 0x69, 0x5d, 0x3b, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6e, 0x76, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x29, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, + 0x49, 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x20, 0x66, 0x6e, 0x76, 0x34, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, + 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x79, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, 0x49, + 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, + 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x36, 0x34, 0x20, + 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x34, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x36, 0x34, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, + 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, + 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, + 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x32, + 0x30, 0x30, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, + 0x35, 0x31, 0x32, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x73, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, + 0x74, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x38, 0x3b, 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x32, 0x35, 0x3b, 0x20, + 0x2b, 0x2b, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x5b, 0x69, 0x5d, 0x20, + 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x7b, 0x30, 0x2c, + 0x20, 0x30, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x5b, 0x38, 0x5d, 0x2e, 0x78, 0x20, 0x3d, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x5b, 0x38, 0x5d, 0x2e, 0x79, 0x20, 0x3d, + 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, + 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x6e, 0x6f, 0x5f, 0x61, 0x62, 0x73, + 0x6f, 0x72, 0x62, 0x28, 0x73, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x69, 0x73, + 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, + 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x61, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x64, 0x29, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x61, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x71, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x74, + 0x20, 0x2b, 0x20, 0x64, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x64, 0x2e, + 0x78, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x64, 0x2e, 0x7a, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, + 0x2d, 0x20, 0x71, 0x20, 0x2a, 0x20, 0x64, 0x2e, 0x77, 0x3b, 0x0a, 0x7d, + 0x0a, 0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x65, 0x74, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x63, + 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x67, + 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x68, 0x61, 0x73, 0x68, 0x36, 0x34, 0x5f, 0x74, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x2a, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x68, 0x61, 0x73, 0x68, 0x36, 0x34, 0x5f, 0x74, 0x2a, 0x20, 0x67, + 0x5f, 0x64, 0x61, 0x67, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x64, 0x61, 0x67, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, + 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x2b, 0x20, 0x67, 0x65, 0x74, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3e, 0x3d, + 0x20, 0x64, 0x61, 0x67, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, + 0x68, 0x32, 0x30, 0x30, 0x5f, 0x74, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x70, + 0x79, 0x28, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, + 0x69, 0x6e, 0x74, 0x34, 0x73, 0x2c, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x5b, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, + 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, + 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x2c, 0x20, 0x34, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x30, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, + 0x35, 0x31, 0x32, 0x28, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x6f, + 0x6c, 0x61, 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x45, 0x54, + 0x48, 0x41, 0x53, 0x48, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x53, 0x45, 0x54, + 0x5f, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x53, 0x3b, 0x20, 0x2b, 0x2b, + 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, + 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, 0x66, 0x6e, + 0x76, 0x28, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x20, 0x5e, 0x20, 0x69, 0x2c, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x69, 0x20, 0x25, + 0x20, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x53, 0x5d, + 0x29, 0x2c, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x77, 0x6f, 0x72, + 0x64, 0x73, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x77, 0x20, 0x21, 0x3d, 0x20, + 0x34, 0x3b, 0x20, 0x2b, 0x2b, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x67, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, + 0x77, 0x5d, 0x20, 0x3d, 0x20, 0x66, 0x6e, 0x76, 0x34, 0x28, 0x64, 0x61, + 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x73, 0x5b, 0x77, 0x5d, 0x2c, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x5b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x77, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x35, 0x31, 0x32, 0x28, + 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x28, + 0x67, 0x5f, 0x64, 0x61, 0x67, 0x5b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, + 0x2c, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, + 0x69, 0x6e, 0x74, 0x34, 0x73, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0a, 0x7d, + 0x00 +}; + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/cl/progpowz/defs.h b/miner/core/src/backend/opencl/cl/progpowz/defs.h new file mode 100644 index 0000000..be07135 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/progpowz/defs.h @@ -0,0 +1,65 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow OpenCL implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#ifdef cl_clang_storage_class_specifiers +#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable +#endif + +#ifndef GROUP_SIZE +#define GROUP_SIZE 256 +#endif +#define GROUP_SHARE (GROUP_SIZE / 16) + +typedef unsigned int uint32_t; +typedef unsigned long uint64_t; +#define ROTL32(x, n) rotate((x), (uint32_t)(n)) +#define ROTR32(x, n) rotate((x), (uint32_t)(32-n)) + +// ProgPowZ algorithm constants (differs from KawPow) +#define PROGPOW_LANES 16 +#define PROGPOW_REGS 32 +#define PROGPOW_DAG_LOADS 4 +#define PROGPOW_CACHE_WORDS 4096 +#define PROGPOW_CNT_DAG 64 +#define PROGPOW_CNT_CACHE 12 // KawPow uses 11 +#define PROGPOW_CNT_MATH 20 // KawPow uses 18 + +#define OPENCL_PLATFORM_UNKNOWN 0 +#define OPENCL_PLATFORM_NVIDIA 1 +#define OPENCL_PLATFORM_AMD 2 +#define OPENCL_PLATFORM_CLOVER 3 + +#ifndef MAX_OUTPUTS +#define MAX_OUTPUTS 63U +#endif + +#ifndef PLATFORM +#ifdef cl_amd_media_ops +#define PLATFORM OPENCL_PLATFORM_AMD +#else +#define PLATFORM OPENCL_PLATFORM_UNKNOWN +#endif +#endif + +#define HASHES_PER_GROUP (GROUP_SIZE / PROGPOW_LANES) + +#define FNV_PRIME 0x1000193 +#define FNV_OFFSET_BASIS 0x811c9dc5 diff --git a/miner/core/src/backend/opencl/cl/progpowz/progpowz.cl b/miner/core/src/backend/opencl/cl/progpowz/progpowz.cl new file mode 100644 index 0000000..9c7d27e --- /dev/null +++ b/miner/core/src/backend/opencl/cl/progpowz/progpowz.cl @@ -0,0 +1,310 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow OpenCL implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#include "defs.h" + +typedef struct __attribute__ ((aligned(16))) {uint32_t s[PROGPOW_DAG_LOADS];} dag_t; + +// Implementation based on: +// https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c + +__constant const uint32_t keccakf_rndc[24] = {0x00000001, 0x00008082, 0x0000808a, 0x80008000, + 0x0000808b, 0x80000001, 0x80008081, 0x00008009, 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, + 0x8000808b, 0x0000008b, 0x00008089, 0x00008003, 0x00008002, 0x00000080, 0x0000800a, 0x8000000a, + 0x80008081, 0x00008080, 0x80000001, 0x80008008}; + +// ProgPowZ uses null padding (zeros) instead of "RAVENCOIN" +__constant const uint32_t progpowz_rndc[15] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +// Implementation of the Keccakf transformation with a width of 800 +void keccak_f800_round(uint32_t st[25], const int r) +{ + const uint32_t keccakf_rotc[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44}; + const uint32_t keccakf_piln[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1}; + + uint32_t t, bc[5]; + // Theta + for (int i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (int i = 0; i < 5; i++) + { + t = bc[(i + 4) % 5] ^ ROTL32(bc[(i + 1) % 5], 1u); + for (uint32_t j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (int i = 0; i < 24; i++) + { + uint32_t j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL32(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (uint32_t j = 0; j < 25; j += 5) + { + for (int i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (int i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[r]; +} + +// Keccak - implemented as a variant of SHAKE +// The width is 800, with a bitrate of 576, a capacity of 224, and no padding +// Only need 64 bits of output for mining +void keccak_f800(uint32_t* st) +{ + // Complete all 22 rounds as a separate impl to + // evaluate only first 8 words is wasteful of regsters + for (int r = 0; r < 22; r++) { + keccak_f800_round(st, r); + } +} + +#define fnv1a(h, d) (h = (h ^ d) * FNV_PRIME) + +typedef struct +{ + uint32_t z, w, jsr, jcong; +} kiss99_t; + +// KISS99 is simple, fast, and passes the TestU01 suite +// https://en.wikipedia.org/wiki/KISS_(algorithm) +// http://www.cse.yorku.ca/~oz/marsaglia-rng.html +uint32_t kiss99(kiss99_t* st) +{ + st->z = 36969 * (st->z & 65535) + (st->z >> 16); + st->w = 18000 * (st->w & 65535) + (st->w >> 16); + uint32_t MWC = ((st->z << 16) + st->w); + st->jsr ^= (st->jsr << 17); + st->jsr ^= (st->jsr >> 13); + st->jsr ^= (st->jsr << 5); + st->jcong = 69069 * st->jcong + 1234567; + return ((MWC ^ st->jcong) + st->jsr); +} + +void fill_mix(local uint32_t* seed, uint32_t lane_id, uint32_t* mix) +{ + // Use FNV to expand the per-warp seed to per-lane + // Use KISS to expand the per-lane seed to fill mix + uint32_t fnv_hash = FNV_OFFSET_BASIS; + kiss99_t st; + st.z = fnv1a(fnv_hash, seed[0]); + st.w = fnv1a(fnv_hash, seed[1]); + st.jsr = fnv1a(fnv_hash, lane_id); + st.jcong = fnv1a(fnv_hash, lane_id); +#pragma unroll + for (int i = 0; i < PROGPOW_REGS; i++) + mix[i] = kiss99(&st); +} + +typedef struct +{ + uint32_t uint32s[PROGPOW_LANES]; +} shuffle_t; + +typedef struct +{ + uint32_t uint32s[32 / sizeof(uint32_t)]; +} hash32_t; + +#if PLATFORM != OPENCL_PLATFORM_NVIDIA // use maxrregs on nv +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +#endif +__kernel void progpow_search(__global dag_t const* g_dag, __global uint* job_blob, ulong target, uint hack_false, volatile __global uint* results, volatile __global uint* stop) +{ + const uint32_t lid = get_local_id(0); + const uint32_t gid = get_global_id(0); + + if (stop[0]) { + if (lid == 0) { + // Count groups of skipped hashes (if we don't count them we'll break hashrate display) + atomic_inc(stop + 1); + } + return; + } + + __local shuffle_t share[HASHES_PER_GROUP]; + __local uint32_t c_dag[PROGPOW_CACHE_WORDS]; + + const uint32_t lane_id = lid & (PROGPOW_LANES - 1); + const uint32_t group_id = lid / PROGPOW_LANES; + + // Load the first portion of the DAG into the cache + for (uint32_t word = lid * PROGPOW_DAG_LOADS; word < PROGPOW_CACHE_WORDS; word += GROUP_SIZE * PROGPOW_DAG_LOADS) + { + dag_t load = g_dag[word / PROGPOW_DAG_LOADS]; + for (int i = 0; i < PROGPOW_DAG_LOADS; i++) + c_dag[word + i] = load.s[i]; + } + + uint32_t hash_seed[2]; // KISS99 initiator + hash32_t digest; // Carry-over from mix output + + uint32_t state2[8]; + + { + // Absorb phase for initial round of keccak + + uint32_t state[25]; // Keccak's state + + // 1st fill with job data + for (int i = 0; i < 10; i++) + state[i] = job_blob[i]; + + // Apply nonce + state[8] = gid; + + // 3rd apply ProgPowZ input constraints (null padding) + for (int i = 10; i < 25; i++) + state[i] = progpowz_rndc[i-10]; + + // Run intial keccak round + keccak_f800(state); + + for (int i = 0; i < 8; i++) + state2[i] = state[i]; + + } + +#pragma unroll 1 + for (uint32_t h = 0; h < PROGPOW_LANES; h++) + { + uint32_t mix[PROGPOW_REGS]; + + // share the hash's seed across all lanes + if (lane_id == h) { + share[group_id].uint32s[0] = state2[0]; + share[group_id].uint32s[1] = state2[1]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + // initialize mix for all lanes + fill_mix(share[group_id].uint32s, lane_id, mix); + + #pragma unroll 2 + for (uint32_t loop = 0; loop < PROGPOW_CNT_DAG; ++loop) + { + // global load + if(lane_id == (loop % PROGPOW_LANES)) + share[0].uint32s[group_id] = mix[0]; + + barrier(CLK_LOCAL_MEM_FENCE); + + uint32_t offset = share[0].uint32s[group_id]; + offset %= PROGPOW_DAG_ELEMENTS; + offset = offset * PROGPOW_LANES + (lane_id ^ loop) % PROGPOW_LANES; + dag_t data_dag = g_dag[offset]; + + // hack to prevent compiler from reordering LD and usage + if (hack_false) barrier(CLK_LOCAL_MEM_FENCE); + + uint32_t data; + XMRIG_INCLUDE_PROGPOW_RANDOM_MATH + + // consume global load data + // hack to prevent compiler from reordering LD and usage + if (hack_false) barrier(CLK_LOCAL_MEM_FENCE); + + XMRIG_INCLUDE_PROGPOW_DATA_LOADS + } + + // Reduce mix data to a per-lane 32-bit digest + uint32_t mix_hash = FNV_OFFSET_BASIS; +#pragma unroll + for (int i = 0; i < PROGPOW_REGS; i++) + fnv1a(mix_hash, mix[i]); + + // Reduce all lanes to a single 256-bit digest + hash32_t digest_temp; + for (int i = 0; i < 8; i++) + digest_temp.uint32s[i] = FNV_OFFSET_BASIS; + share[group_id].uint32s[lane_id] = mix_hash; + barrier(CLK_LOCAL_MEM_FENCE); +#pragma unroll + for (int i = 0; i < PROGPOW_LANES; i++) + fnv1a(digest_temp.uint32s[i % 8], share[group_id].uint32s[i]); + if (h == lane_id) + digest = digest_temp; + } + + + // Absorb phase for last round of keccak (256 bits) + uint64_t result; + + { + uint32_t state[25] = {0x0}; // Keccak's state + + // 1st initial 8 words of state are kept as carry-over from initial keccak + for (int i = 0; i < 8; i++) + state[i] = state2[i]; + + // 2nd subsequent 8 words are carried from digest/mix + for (int i = 8; i < 16; i++) + state[i] = digest.uint32s[i - 8]; + + // 3rd apply ProgPowZ input constraints (null padding) + for (int i = 16; i < 25; i++) + state[i] = progpowz_rndc[i - 16]; + + // Run keccak loop + keccak_f800(state); + + uint64_t res = (uint64_t)state[1] << 32 | state[0]; + result = as_ulong(as_uchar8(res).s76543210); + } + + if (result <= target) + { + *stop = 1; + + const uint k = atomic_inc(results) + 1; + if (k <= 15) + results[k] = gid; + } +} diff --git a/miner/core/src/backend/opencl/cl/progpowz/progpowz_cl.h b/miner/core/src/backend/opencl/cl/progpowz/progpowz_cl.h new file mode 100644 index 0000000..7d2088f --- /dev/null +++ b/miner/core/src/backend/opencl/cl/progpowz/progpowz_cl.h @@ -0,0 +1,785 @@ +#pragma once + +namespace xmrig { + +static const char progpowz_cl[] = { + 0x2f, 0x2a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x32, 0x35, 0x20, 0x4c, 0x65, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x42, + 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x58, 0x4d, 0x52, 0x69, + 0x67, 0x20, 0x4b, 0x61, 0x77, 0x50, 0x6f, 0x77, 0x20, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x4c, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, + 0x31, 0x38, 0x2d, 0x32, 0x30, 0x32, 0x31, 0x20, 0x53, 0x43, 0x68, 0x65, + 0x72, 0x6e, 0x79, 0x6b, 0x68, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x43, 0x68, 0x65, 0x72, 0x6e, 0x79, 0x6b, + 0x68, 0x3e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x32, + 0x30, 0x32, 0x31, 0x20, 0x58, 0x4d, 0x52, 0x69, 0x67, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x6d, 0x72, 0x69, 0x67, 0x3e, 0x2c, 0x20, 0x3c, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x40, 0x78, 0x6d, 0x72, 0x69, 0x67, 0x2e, 0x63, + 0x6f, 0x6d, 0x3e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x20, 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x73, 0x6f, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x3a, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x20, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, + 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, + 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x69, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x33, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x28, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x61, 0x6e, 0x79, 0x20, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, + 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x6f, + 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20, + 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, 0x3b, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x20, 0x77, + 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, + 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, + 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, + 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, + 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x53, 0x65, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x64, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x2c, 0x20, 0x73, + 0x65, 0x65, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x2f, 0x3e, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x20, 0x22, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x20, 0x5f, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x5f, 0x5f, 0x20, 0x28, 0x28, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x28, 0x31, 0x36, 0x29, 0x29, 0x29, 0x20, 0x7b, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x5b, 0x50, 0x52, 0x4f, 0x47, + 0x50, 0x4f, 0x57, 0x5f, 0x44, 0x41, 0x47, 0x5f, 0x4c, 0x4f, 0x41, 0x44, + 0x53, 0x5d, 0x3b, 0x7d, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x74, 0x3b, 0x0a, + 0x0a, 0x2f, 0x2f, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x61, 0x73, 0x65, 0x64, + 0x20, 0x6f, 0x6e, 0x3a, 0x0a, 0x2f, 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6d, 0x6a, 0x6f, 0x73, 0x61, 0x61, 0x72, 0x69, 0x6e, + 0x65, 0x6e, 0x2f, 0x74, 0x69, 0x6e, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x33, + 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x2f, 0x73, 0x68, 0x61, 0x33, 0x2e, 0x63, 0x0a, 0x0a, 0x5f, 0x5f, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6b, + 0x65, 0x63, 0x63, 0x61, 0x6b, 0x66, 0x5f, 0x72, 0x6e, 0x64, 0x63, 0x5b, + 0x32, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x61, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x38, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, + 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, + 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, + 0x39, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, + 0x33, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, + 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, + 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x61, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x38, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x38, 0x30, 0x30, 0x38, 0x7d, 0x3b, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, + 0x50, 0x72, 0x6f, 0x67, 0x50, 0x6f, 0x77, 0x5a, 0x20, 0x75, 0x73, 0x65, + 0x73, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x20, 0x28, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x29, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x22, 0x52, + 0x41, 0x56, 0x45, 0x4e, 0x43, 0x4f, 0x49, 0x4e, 0x22, 0x0a, 0x5f, 0x5f, + 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, + 0x70, 0x72, 0x6f, 0x67, 0x70, 0x6f, 0x77, 0x7a, 0x5f, 0x72, 0x6e, 0x64, + 0x63, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x7d, 0x3b, + 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x66, 0x20, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x38, 0x30, 0x30, 0x0a, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x38, + 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x74, 0x5b, 0x32, 0x35, 0x5d, + 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, + 0x72, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, + 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x66, 0x5f, 0x72, 0x6f, 0x74, 0x63, + 0x5b, 0x32, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x36, + 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x32, 0x31, + 0x2c, 0x20, 0x32, 0x38, 0x2c, 0x20, 0x33, 0x36, 0x2c, 0x20, 0x34, 0x35, + 0x2c, 0x20, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x31, 0x34, 0x2c, + 0x20, 0x32, 0x37, 0x2c, 0x20, 0x34, 0x31, 0x2c, 0x20, 0x35, 0x36, 0x2c, + 0x20, 0x38, 0x2c, 0x20, 0x32, 0x35, 0x2c, 0x20, 0x34, 0x33, 0x2c, 0x20, + 0x36, 0x32, 0x2c, 0x20, 0x31, 0x38, 0x2c, 0x20, 0x33, 0x39, 0x2c, 0x20, + 0x36, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x34, 0x34, 0x7d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x66, 0x5f, 0x70, 0x69, 0x6c, 0x6e, 0x5b, 0x32, 0x34, 0x5d, + 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x31, 0x30, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x31, 0x31, 0x2c, 0x20, + 0x31, 0x37, 0x2c, 0x20, 0x31, 0x38, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x35, + 0x2c, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x32, 0x31, 0x2c, + 0x20, 0x32, 0x34, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, + 0x32, 0x33, 0x2c, 0x20, 0x31, 0x39, 0x2c, 0x20, 0x31, 0x33, 0x2c, 0x20, + 0x31, 0x32, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x31, + 0x34, 0x2c, 0x20, 0x32, 0x32, 0x2c, 0x20, 0x39, 0x2c, 0x20, 0x36, 0x2c, + 0x20, 0x31, 0x7d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x74, 0x2c, 0x20, 0x62, 0x63, + 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x54, 0x68, 0x65, 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x35, 0x3b, 0x20, 0x69, 0x2b, 0x2b, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x63, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x5b, 0x69, 0x5d, 0x20, + 0x5e, 0x20, 0x73, 0x74, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x35, 0x5d, 0x20, + 0x5e, 0x20, 0x73, 0x74, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x30, 0x5d, + 0x20, 0x5e, 0x20, 0x73, 0x74, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x35, + 0x5d, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x32, + 0x30, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x35, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x62, 0x63, 0x5b, 0x28, 0x69, + 0x20, 0x2b, 0x20, 0x34, 0x29, 0x20, 0x25, 0x20, 0x35, 0x5d, 0x20, 0x5e, + 0x20, 0x52, 0x4f, 0x54, 0x4c, 0x33, 0x32, 0x28, 0x62, 0x63, 0x5b, 0x28, + 0x69, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x20, 0x25, 0x20, 0x35, 0x5d, 0x2c, + 0x20, 0x31, 0x75, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x5f, 0x74, 0x20, 0x6a, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x6a, + 0x20, 0x3c, 0x20, 0x32, 0x35, 0x3b, 0x20, 0x6a, 0x20, 0x2b, 0x3d, 0x20, + 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x5b, 0x6a, 0x20, 0x2b, 0x20, 0x69, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x68, 0x6f, + 0x20, 0x50, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x5b, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x32, 0x34, 0x3b, 0x20, 0x69, + 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x6a, 0x20, 0x3d, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, + 0x6b, 0x66, 0x5f, 0x70, 0x69, 0x6c, 0x6e, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x63, 0x5b, 0x30, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x5b, 0x6a, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x5b, 0x6a, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x54, 0x4c, 0x33, 0x32, 0x28, 0x74, 0x2c, + 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x66, 0x5f, 0x72, 0x6f, 0x74, + 0x63, 0x5b, 0x69, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x62, 0x63, 0x5b, 0x30, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x20, 0x43, 0x68, 0x69, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x6a, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x6a, 0x20, + 0x3c, 0x20, 0x32, 0x35, 0x3b, 0x20, 0x6a, 0x20, 0x2b, 0x3d, 0x20, 0x35, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, + 0x35, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x63, 0x5b, 0x69, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x5b, 0x6a, 0x20, 0x2b, 0x20, 0x69, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x35, 0x3b, 0x20, 0x69, 0x2b, + 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x5b, 0x6a, 0x20, 0x2b, 0x20, 0x69, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x28, 0x7e, 0x62, 0x63, 0x5b, 0x28, 0x69, 0x20, + 0x2b, 0x20, 0x31, 0x29, 0x20, 0x25, 0x20, 0x35, 0x5d, 0x29, 0x20, 0x26, + 0x20, 0x62, 0x63, 0x5b, 0x28, 0x69, 0x20, 0x2b, 0x20, 0x32, 0x29, 0x20, + 0x25, 0x20, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x20, 0x49, 0x6f, 0x74, + 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x5b, 0x30, 0x5d, 0x20, + 0x5e, 0x3d, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x66, 0x5f, 0x72, + 0x6e, 0x64, 0x63, 0x5b, 0x72, 0x5d, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, + 0x2f, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x20, 0x2d, 0x20, 0x69, + 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x20, 0x61, + 0x73, 0x20, 0x61, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x53, 0x48, 0x41, 0x4b, 0x45, 0x0a, 0x2f, 0x2f, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x20, 0x69, 0x73, + 0x20, 0x38, 0x30, 0x30, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, + 0x20, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x35, 0x37, 0x36, 0x2c, 0x20, 0x61, 0x20, 0x63, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x32, 0x32, 0x34, 0x2c, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x6e, 0x6f, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x0a, 0x2f, 0x2f, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x6e, + 0x65, 0x65, 0x64, 0x20, 0x36, 0x34, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x0a, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x38, 0x30, + 0x30, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x2a, 0x20, + 0x73, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x6c, + 0x6c, 0x20, 0x32, 0x32, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, + 0x61, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, + 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, + 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x20, 0x38, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x77, 0x61, 0x73, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x20, 0x6f, 0x66, 0x20, + 0x72, 0x65, 0x67, 0x73, 0x74, 0x65, 0x72, 0x73, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x72, 0x20, 0x3c, 0x20, 0x32, 0x32, 0x3b, + 0x20, 0x72, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, + 0x38, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x73, 0x74, + 0x2c, 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x7d, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x66, + 0x6e, 0x76, 0x31, 0x61, 0x28, 0x68, 0x2c, 0x20, 0x64, 0x29, 0x20, 0x28, + 0x68, 0x20, 0x3d, 0x20, 0x28, 0x68, 0x20, 0x5e, 0x20, 0x64, 0x29, 0x20, + 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, 0x49, 0x4d, 0x45, 0x29, + 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x7a, 0x2c, 0x20, 0x77, + 0x2c, 0x20, 0x6a, 0x73, 0x72, 0x2c, 0x20, 0x6a, 0x63, 0x6f, 0x6e, 0x67, + 0x3b, 0x0a, 0x7d, 0x20, 0x6b, 0x69, 0x73, 0x73, 0x39, 0x39, 0x5f, 0x74, + 0x3b, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x4b, 0x49, 0x53, 0x53, 0x39, 0x39, + 0x20, 0x69, 0x73, 0x20, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x20, + 0x66, 0x61, 0x73, 0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, + 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x65, 0x73, + 0x74, 0x55, 0x30, 0x31, 0x20, 0x73, 0x75, 0x69, 0x74, 0x65, 0x0a, 0x2f, + 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x65, 0x6e, + 0x2e, 0x77, 0x69, 0x6b, 0x69, 0x70, 0x65, 0x64, 0x69, 0x61, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x77, 0x69, 0x6b, 0x69, 0x2f, 0x4b, 0x49, 0x53, 0x53, + 0x5f, 0x28, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x29, + 0x0a, 0x2f, 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x63, 0x73, 0x65, 0x2e, 0x79, 0x6f, 0x72, 0x6b, 0x75, + 0x2e, 0x63, 0x61, 0x2f, 0x7e, 0x6f, 0x7a, 0x2f, 0x6d, 0x61, 0x72, 0x73, + 0x61, 0x67, 0x6c, 0x69, 0x61, 0x2d, 0x72, 0x6e, 0x67, 0x2e, 0x68, 0x74, + 0x6d, 0x6c, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, + 0x6b, 0x69, 0x73, 0x73, 0x39, 0x39, 0x28, 0x6b, 0x69, 0x73, 0x73, 0x39, + 0x39, 0x5f, 0x74, 0x2a, 0x20, 0x73, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x2d, 0x3e, 0x7a, 0x20, 0x3d, 0x20, 0x33, + 0x36, 0x39, 0x36, 0x39, 0x20, 0x2a, 0x20, 0x28, 0x73, 0x74, 0x2d, 0x3e, + 0x7a, 0x20, 0x26, 0x20, 0x36, 0x35, 0x35, 0x33, 0x35, 0x29, 0x20, 0x2b, + 0x20, 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x7a, 0x20, 0x3e, 0x3e, 0x20, 0x31, + 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2d, 0x3e, + 0x77, 0x20, 0x3d, 0x20, 0x31, 0x38, 0x30, 0x30, 0x30, 0x20, 0x2a, 0x20, + 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x77, 0x20, 0x26, 0x20, 0x36, 0x35, 0x35, + 0x33, 0x35, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x77, + 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x4d, 0x57, + 0x43, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x7a, 0x20, + 0x3c, 0x3c, 0x20, 0x31, 0x36, 0x29, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x2d, + 0x3e, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2d, + 0x3e, 0x6a, 0x73, 0x72, 0x20, 0x5e, 0x3d, 0x20, 0x28, 0x73, 0x74, 0x2d, + 0x3e, 0x6a, 0x73, 0x72, 0x20, 0x3c, 0x3c, 0x20, 0x31, 0x37, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2d, 0x3e, 0x6a, 0x73, 0x72, + 0x20, 0x5e, 0x3d, 0x20, 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x6a, 0x73, 0x72, + 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x2d, 0x3e, 0x6a, 0x73, 0x72, 0x20, 0x5e, 0x3d, 0x20, + 0x28, 0x73, 0x74, 0x2d, 0x3e, 0x6a, 0x73, 0x72, 0x20, 0x3c, 0x3c, 0x20, + 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2d, 0x3e, + 0x6a, 0x63, 0x6f, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x36, 0x39, 0x30, 0x36, + 0x39, 0x20, 0x2a, 0x20, 0x73, 0x74, 0x2d, 0x3e, 0x6a, 0x63, 0x6f, 0x6e, + 0x67, 0x20, 0x2b, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x28, 0x28, 0x4d, 0x57, 0x43, 0x20, 0x5e, 0x20, 0x73, 0x74, 0x2d, 0x3e, + 0x6a, 0x63, 0x6f, 0x6e, 0x67, 0x29, 0x20, 0x2b, 0x20, 0x73, 0x74, 0x2d, + 0x3e, 0x6a, 0x73, 0x72, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x28, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x2a, 0x20, 0x73, 0x65, 0x65, 0x64, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, + 0x69, 0x64, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, + 0x2a, 0x20, 0x6d, 0x69, 0x78, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x55, 0x73, 0x65, 0x20, 0x46, 0x4e, 0x56, 0x20, + 0x74, 0x6f, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x70, 0x65, 0x72, 0x2d, 0x77, 0x61, 0x72, 0x70, 0x20, 0x73, + 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x2d, 0x6c, + 0x61, 0x6e, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x55, + 0x73, 0x65, 0x20, 0x4b, 0x49, 0x53, 0x53, 0x20, 0x74, 0x6f, 0x20, 0x65, + 0x78, 0x70, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, + 0x72, 0x2d, 0x6c, 0x61, 0x6e, 0x65, 0x20, 0x73, 0x65, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x20, 0x6d, 0x69, 0x78, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, + 0x20, 0x66, 0x6e, 0x76, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x20, 0x3d, 0x20, + 0x46, 0x4e, 0x56, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x42, + 0x41, 0x53, 0x49, 0x53, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x69, + 0x73, 0x73, 0x39, 0x39, 0x5f, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x66, 0x6e, + 0x76, 0x31, 0x61, 0x28, 0x66, 0x6e, 0x76, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x2c, 0x20, 0x73, 0x65, 0x65, 0x64, 0x5b, 0x30, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x66, + 0x6e, 0x76, 0x31, 0x61, 0x28, 0x66, 0x6e, 0x76, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x2c, 0x20, 0x73, 0x65, 0x65, 0x64, 0x5b, 0x31, 0x5d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2e, 0x6a, 0x73, 0x72, 0x20, + 0x3d, 0x20, 0x66, 0x6e, 0x76, 0x31, 0x61, 0x28, 0x66, 0x6e, 0x76, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, + 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x2e, 0x6a, + 0x63, 0x6f, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x66, 0x6e, 0x76, 0x31, 0x61, + 0x28, 0x66, 0x6e, 0x76, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x2c, 0x20, 0x6c, + 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x3b, 0x0a, 0x23, 0x70, 0x72, + 0x61, 0x67, 0x6d, 0x61, 0x20, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, + 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, + 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x52, 0x45, 0x47, 0x53, + 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, + 0x6b, 0x69, 0x73, 0x73, 0x39, 0x39, 0x28, 0x26, 0x73, 0x74, 0x29, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, + 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, + 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, + 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x73, 0x5b, 0x33, 0x32, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, + 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x29, 0x5d, + 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x33, 0x32, 0x5f, 0x74, + 0x3b, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x50, 0x4c, 0x41, 0x54, 0x46, + 0x4f, 0x52, 0x4d, 0x20, 0x21, 0x3d, 0x20, 0x4f, 0x50, 0x45, 0x4e, 0x43, + 0x4c, 0x5f, 0x50, 0x4c, 0x41, 0x54, 0x46, 0x4f, 0x52, 0x4d, 0x5f, 0x4e, + 0x56, 0x49, 0x44, 0x49, 0x41, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x75, 0x73, + 0x65, 0x20, 0x6d, 0x61, 0x78, 0x72, 0x72, 0x65, 0x67, 0x73, 0x20, 0x6f, + 0x6e, 0x20, 0x6e, 0x76, 0x0a, 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, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, + 0x49, 0x5a, 0x45, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x29, + 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x5f, 0x5f, 0x6b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x70, 0x72, + 0x6f, 0x67, 0x70, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x64, 0x61, + 0x67, 0x5f, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x2a, 0x20, 0x67, + 0x5f, 0x64, 0x61, 0x67, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x6a, 0x6f, 0x62, + 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x2c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x61, 0x6c, 0x73, 0x65, + 0x2c, 0x20, 0x76, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x5f, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x2a, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x2c, 0x20, 0x76, + 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6c, 0x65, 0x20, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x2a, 0x20, 0x73, + 0x74, 0x6f, 0x70, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x74, 0x20, 0x6c, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x69, 0x64, 0x20, + 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x74, 0x6f, 0x70, 0x5b, 0x30, 0x5d, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x6c, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x30, + 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x73, + 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x20, 0x28, 0x69, 0x66, 0x20, 0x77, 0x65, 0x20, 0x64, 0x6f, 0x6e, + 0x27, 0x74, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x65, + 0x6d, 0x20, 0x77, 0x65, 0x27, 0x6c, 0x6c, 0x20, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x20, 0x68, 0x61, 0x73, 0x68, 0x72, 0x61, 0x74, 0x65, 0x20, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x74, 0x6f, 0x6d, + 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x63, 0x28, 0x73, 0x74, 0x6f, 0x70, 0x20, + 0x2b, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x20, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x74, + 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x48, 0x41, 0x53, 0x48, 0x45, + 0x53, 0x5f, 0x50, 0x45, 0x52, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x63, + 0x5f, 0x64, 0x61, 0x67, 0x5b, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, + 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x53, + 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, + 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x64, + 0x20, 0x26, 0x20, 0x28, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, + 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x6c, 0x69, 0x64, 0x20, 0x2f, 0x20, + 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, + 0x53, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4c, + 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x20, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x44, 0x41, 0x47, 0x20, 0x69, 0x6e, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x63, 0x68, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x3d, + 0x20, 0x6c, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, + 0x4f, 0x57, 0x5f, 0x44, 0x41, 0x47, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x53, + 0x3b, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x3c, 0x20, 0x50, 0x52, 0x4f, + 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x57, + 0x4f, 0x52, 0x44, 0x53, 0x3b, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x20, 0x2b, + 0x3d, 0x20, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x53, 0x49, 0x5a, 0x45, + 0x20, 0x2a, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, + 0x41, 0x47, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x53, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x64, 0x61, 0x67, 0x5f, 0x74, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x3d, + 0x20, 0x67, 0x5f, 0x64, 0x61, 0x67, 0x5b, 0x77, 0x6f, 0x72, 0x64, 0x20, + 0x2f, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, 0x41, + 0x47, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x53, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, 0x41, + 0x47, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x53, 0x3b, 0x20, 0x69, 0x2b, 0x2b, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x5f, 0x64, 0x61, 0x67, 0x5b, 0x77, 0x6f, 0x72, 0x64, + 0x20, 0x2b, 0x20, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x61, 0x64, + 0x2e, 0x73, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x65, 0x65, 0x64, + 0x5b, 0x32, 0x5d, 0x3b, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, 0x49, 0x53, + 0x53, 0x39, 0x39, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, + 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x3b, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x43, 0x61, 0x72, + 0x72, 0x79, 0x2d, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x6d, 0x69, 0x78, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x32, 0x5b, 0x38, 0x5d, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, 0x62, 0x73, 0x6f, 0x72, + 0x62, 0x20, 0x70, 0x68, 0x61, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, + 0x32, 0x35, 0x5d, 0x3b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x27, 0x73, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x31, 0x73, 0x74, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x20, 0x64, 0x61, 0x74, + 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x30, 0x3b, 0x20, 0x69, 0x2b, + 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x69, 0x5d, 0x20, + 0x3d, 0x20, 0x6a, 0x6f, 0x62, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5b, 0x69, + 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x20, 0x6e, 0x6f, 0x6e, + 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5b, 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x69, + 0x64, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x33, 0x72, 0x64, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x20, 0x50, 0x72, 0x6f, 0x67, 0x50, 0x6f, 0x77, 0x5a, 0x20, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x73, 0x20, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x70, 0x61, + 0x64, 0x64, 0x69, 0x6e, 0x67, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, + 0x32, 0x35, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x70, 0x6f, 0x77, 0x7a, 0x5f, 0x72, 0x6e, 0x64, 0x63, 0x5b, 0x69, 0x2d, + 0x31, 0x30, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x38, 0x30, 0x30, + 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x32, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, + 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x20, 0x31, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x68, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x68, 0x20, + 0x3c, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, + 0x4e, 0x45, 0x53, 0x3b, 0x20, 0x68, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6d, 0x69, 0x78, + 0x5b, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x52, 0x45, 0x47, + 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x27, 0x73, 0x20, 0x73, 0x65, 0x65, + 0x64, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x20, 0x61, 0x6c, 0x6c, + 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6c, 0x61, 0x6e, 0x65, 0x5f, + 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x68, 0x61, 0x72, 0x65, 0x5b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x30, + 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x32, 0x5b, 0x30, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x73, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x32, 0x5b, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 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, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x6d, 0x69, 0x78, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x73, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, + 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x28, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x5d, 0x2e, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x73, 0x2c, 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, + 0x69, 0x64, 0x2c, 0x20, 0x6d, 0x69, 0x78, 0x29, 0x3b, 0x0a, 0x0a, 0x09, + 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x75, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x20, 0x32, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x70, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x3c, + 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x43, 0x4e, 0x54, + 0x5f, 0x44, 0x41, 0x47, 0x3b, 0x20, 0x2b, 0x2b, 0x6c, 0x6f, 0x6f, 0x70, + 0x29, 0x0a, 0x09, 0x7b, 0x0a, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x0a, 0x09, 0x09, + 0x69, 0x66, 0x28, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x3d, + 0x3d, 0x20, 0x28, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x25, 0x20, 0x50, 0x52, + 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x29, + 0x29, 0x0a, 0x09, 0x09, 0x09, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x30, + 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x5d, 0x20, 0x3d, 0x20, 0x6d, 0x69, + 0x78, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 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, 0x0a, 0x09, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x30, 0x5d, 0x2e, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x73, 0x5b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x5d, 0x3b, 0x0a, 0x09, 0x09, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x20, 0x25, 0x3d, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, + 0x44, 0x41, 0x47, 0x5f, 0x45, 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x53, + 0x3b, 0x0a, 0x09, 0x09, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, + 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x2a, 0x20, 0x50, 0x52, + 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x20, + 0x2b, 0x20, 0x28, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x5e, + 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x29, 0x20, 0x25, 0x20, 0x50, 0x52, 0x4f, + 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x3b, 0x0a, + 0x09, 0x09, 0x64, 0x61, 0x67, 0x5f, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x64, 0x61, 0x67, 0x20, 0x3d, 0x20, 0x67, 0x5f, 0x64, 0x61, 0x67, + 0x5b, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5d, 0x3b, 0x0a, 0x0a, 0x09, + 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x20, 0x74, 0x6f, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x69, 0x6c, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x72, 0x65, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x44, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x09, 0x09, + 0x69, 0x66, 0x20, 0x28, 0x68, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x29, 0x20, 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, 0x0a, 0x09, + 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x64, 0x61, + 0x74, 0x61, 0x3b, 0x0a, 0x09, 0x09, 0x58, 0x4d, 0x52, 0x49, 0x47, 0x5f, + 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x47, + 0x50, 0x4f, 0x57, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x5f, 0x4d, + 0x41, 0x54, 0x48, 0x0a, 0x0a, 0x09, 0x09, 0x2f, 0x2f, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x20, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x09, + 0x09, 0x2f, 0x2f, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x20, 0x74, 0x6f, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x69, 0x6c, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x72, 0x65, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x44, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x09, 0x09, + 0x69, 0x66, 0x20, 0x28, 0x68, 0x61, 0x63, 0x6b, 0x5f, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x29, 0x20, 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, 0x0a, 0x09, + 0x09, 0x58, 0x4d, 0x52, 0x49, 0x47, 0x5f, 0x49, 0x4e, 0x43, 0x4c, 0x55, + 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x44, + 0x41, 0x54, 0x41, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x53, 0x0a, 0x09, 0x7d, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x52, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x6d, 0x69, 0x78, 0x20, + 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x70, 0x65, + 0x72, 0x2d, 0x6c, 0x61, 0x6e, 0x65, 0x20, 0x33, 0x32, 0x2d, 0x62, 0x69, + 0x74, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x74, 0x20, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x20, 0x3d, + 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, + 0x42, 0x41, 0x53, 0x49, 0x53, 0x3b, 0x0a, 0x23, 0x70, 0x72, 0x61, 0x67, + 0x6d, 0x61, 0x20, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x52, 0x45, + 0x47, 0x53, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x76, + 0x31, 0x61, 0x28, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x2c, + 0x20, 0x6d, 0x69, 0x78, 0x5b, 0x69, 0x5d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6c, 0x61, 0x6e, + 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x20, 0x32, 0x35, 0x36, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x64, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x64, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x2e, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, + 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, + 0x42, 0x41, 0x53, 0x49, 0x53, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x73, 0x5b, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x5d, 0x20, + 0x3d, 0x20, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 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, 0x23, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x20, 0x75, 0x6e, + 0x72, 0x6f, 0x6c, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x50, 0x52, 0x4f, + 0x47, 0x50, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x4e, 0x45, 0x53, 0x3b, 0x20, + 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x76, 0x31, 0x61, 0x28, 0x64, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x2e, 0x75, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x69, 0x20, 0x25, 0x20, 0x38, + 0x5d, 0x2c, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x5b, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x73, 0x5b, 0x69, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x68, 0x20, 0x3d, 0x3d, + 0x20, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x41, + 0x62, 0x73, 0x6f, 0x72, 0x62, 0x20, 0x70, 0x68, 0x61, 0x73, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x20, 0x28, 0x32, 0x35, 0x36, 0x20, 0x62, 0x69, 0x74, 0x73, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x74, 0x20, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x32, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x30, 0x78, + 0x30, 0x7d, 0x3b, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4b, + 0x65, 0x63, 0x63, 0x61, 0x6b, 0x27, 0x73, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x31, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x38, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x6f, 0x66, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6b, + 0x65, 0x70, 0x74, 0x20, 0x61, 0x73, 0x20, 0x63, 0x61, 0x72, 0x72, 0x79, + 0x2d, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, + 0x6b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x38, 0x3b, 0x20, 0x69, 0x2b, 0x2b, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x69, 0x5d, 0x20, 0x3d, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x32, 0x5b, 0x69, 0x5d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x32, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x73, 0x65, 0x71, 0x75, 0x65, + 0x6e, 0x74, 0x20, 0x38, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x64, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x2f, 0x6d, + 0x69, 0x78, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x38, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x69, + 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x2e, 0x75, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x73, 0x5b, 0x69, 0x20, 0x2d, 0x20, 0x38, 0x5d, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x33, 0x72, 0x64, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x20, + 0x50, 0x72, 0x6f, 0x67, 0x50, 0x6f, 0x77, 0x5a, 0x20, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, + 0x74, 0x73, 0x20, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x69, + 0x20, 0x3d, 0x20, 0x31, 0x36, 0x3b, 0x20, 0x69, 0x20, 0x3c, 0x20, 0x32, + 0x35, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x70, + 0x6f, 0x77, 0x7a, 0x5f, 0x72, 0x6e, 0x64, 0x63, 0x5b, 0x69, 0x20, 0x2d, + 0x20, 0x31, 0x36, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, 0x75, 0x6e, 0x20, 0x6b, 0x65, + 0x63, 0x63, 0x61, 0x6b, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x66, 0x38, 0x30, 0x30, 0x28, 0x73, 0x74, 0x61, 0x74, 0x65, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x72, 0x65, 0x73, 0x20, + 0x3d, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x74, 0x29, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x5d, 0x20, 0x3c, 0x3c, 0x20, + 0x33, 0x32, 0x20, 0x7c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x30, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x61, 0x73, 0x5f, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x28, 0x61, 0x73, 0x5f, 0x75, 0x63, 0x68, 0x61, + 0x72, 0x38, 0x28, 0x72, 0x65, 0x73, 0x29, 0x2e, 0x73, 0x37, 0x36, 0x35, + 0x34, 0x33, 0x32, 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3c, 0x3d, 0x20, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x73, 0x74, 0x6f, 0x70, + 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x6b, 0x20, 0x3d, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, + 0x5f, 0x69, 0x6e, 0x63, 0x28, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x29, 0x20, 0x2b, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6b, 0x20, 0x3c, 0x3d, 0x20, + 0x31, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x5b, + 0x6b, 0x5d, 0x20, 0x3d, 0x20, 0x67, 0x69, 0x64, 0x3b, 0x0a, 0x20, 0x20, + 0x00 +}; + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag.cl b/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag.cl new file mode 100644 index 0000000..cfc8b5c --- /dev/null +++ b/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag.cl @@ -0,0 +1,304 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow OpenCL implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#include "defs.h" + +// +// DAG calculation logic - same as KawPow (standard Ethash DAG) +// + + +#define ETHASH_DATASET_PARENTS 512 +#define NODE_WORDS (64 / 4) + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +#if PLATFORM == OPENCL_PLATFORM_NVIDIA && COMPUTE >= 35 +static uint2 ROL2(const uint2 a, const int offset) +{ + uint2 result; + if (offset >= 32) + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.x), "r"(a.y), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.y), "r"(a.x), "r"(offset)); + } + else + { + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.x) : "r"(a.y), "r"(a.x), "r"(offset)); + asm("shf.l.wrap.b32 %0, %1, %2, %3;" : "=r"(result.y) : "r"(a.x), "r"(a.y), "r"(offset)); + } + return result; +} +#elif defined(cl_amd_media_ops) +#pragma OPENCL EXTENSION cl_amd_media_ops : enable +static uint2 ROL2(const uint2 vv, const int r) +{ + if (r <= 32) + { + return amd_bitalign((vv).xy, (vv).yx, 32 - r); + } + else + { + return amd_bitalign((vv).yx, (vv).xy, 64 - r); + } +} +#else +static uint2 ROL2(const uint2 v, const int n) +{ + uint2 result; + if (n <= 32) + { + result.y = ((v.y << (n)) | (v.x >> (32 - n))); + result.x = ((v.x << (n)) | (v.y >> (32 - n))); + } + else + { + result.y = ((v.x << (n - 32)) | (v.y >> (64 - n))); + result.x = ((v.y << (n - 32)) | (v.x >> (64 - n))); + } + return result; +} +#endif + +static void chi(uint2* a, const uint n, const uint2* t) +{ + a[n + 0] = bitselect(t[n + 0] ^ t[n + 2], t[n + 0], t[n + 1]); + a[n + 1] = bitselect(t[n + 1] ^ t[n + 3], t[n + 1], t[n + 2]); + a[n + 2] = bitselect(t[n + 2] ^ t[n + 4], t[n + 2], t[n + 3]); + a[n + 3] = bitselect(t[n + 3] ^ t[n + 0], t[n + 3], t[n + 4]); + a[n + 4] = bitselect(t[n + 4] ^ t[n + 1], t[n + 4], t[n + 0]); +} + +static void keccak_f1600_round(uint2* a, uint r) +{ + uint2 t[25]; + uint2 u; + + // Theta + t[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + t[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + t[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + t[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + t[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + u = t[4] ^ ROL2(t[1], 1); + a[0] ^= u; + a[5] ^= u; + a[10] ^= u; + a[15] ^= u; + a[20] ^= u; + u = t[0] ^ ROL2(t[2], 1); + a[1] ^= u; + a[6] ^= u; + a[11] ^= u; + a[16] ^= u; + a[21] ^= u; + u = t[1] ^ ROL2(t[3], 1); + a[2] ^= u; + a[7] ^= u; + a[12] ^= u; + a[17] ^= u; + a[22] ^= u; + u = t[2] ^ ROL2(t[4], 1); + a[3] ^= u; + a[8] ^= u; + a[13] ^= u; + a[18] ^= u; + a[23] ^= u; + u = t[3] ^ ROL2(t[0], 1); + a[4] ^= u; + a[9] ^= u; + a[14] ^= u; + a[19] ^= u; + a[24] ^= u; + + // Rho Pi + + t[0] = a[0]; + t[10] = ROL2(a[1], 1); + t[20] = ROL2(a[2], 62); + t[5] = ROL2(a[3], 28); + t[15] = ROL2(a[4], 27); + + t[16] = ROL2(a[5], 36); + t[1] = ROL2(a[6], 44); + t[11] = ROL2(a[7], 6); + t[21] = ROL2(a[8], 55); + t[6] = ROL2(a[9], 20); + + t[7] = ROL2(a[10], 3); + t[17] = ROL2(a[11], 10); + t[2] = ROL2(a[12], 43); + t[12] = ROL2(a[13], 25); + t[22] = ROL2(a[14], 39); + + t[23] = ROL2(a[15], 41); + t[8] = ROL2(a[16], 45); + t[18] = ROL2(a[17], 15); + t[3] = ROL2(a[18], 21); + t[13] = ROL2(a[19], 8); + + t[14] = ROL2(a[20], 18); + t[24] = ROL2(a[21], 2); + t[9] = ROL2(a[22], 61); + t[19] = ROL2(a[23], 56); + t[4] = ROL2(a[24], 14); + + // Chi + chi(a, 0, t); + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + chi(a, 5, t); + chi(a, 10, t); + chi(a, 15, t); + chi(a, 20, t); +} + +static void keccak_f1600_no_absorb(uint2* a, uint out_size, uint isolate) +{ + // Originally I unrolled the first and last rounds to interface + // better with surrounding code, however I haven't done this + // without causing the AMD compiler to blow up the VGPR usage. + + + // uint o = 25; + for (uint r = 0; r < 24;) + { + // This dynamic branch stops the AMD compiler unrolling the loop + // and additionally saves about 33% of the VGPRs, enough to gain another + // wavefront. Ideally we'd get 4 in flight, but 3 is the best I can + // massage out of the compiler. It doesn't really seem to matter how + // much we try and help the compiler save VGPRs because it seems to throw + // that information away, hence the implementation of keccak here + // doesn't bother. + if (isolate) + { + keccak_f1600_round(a, r++); + // if (r == 23) o = out_size; + } + } + + + // final round optimised for digest size + // keccak_f1600_round(a, 23, out_size); +} + +#define copy(dst, src, count) \ + for (uint i = 0; i != count; ++i) \ + { \ + (dst)[i] = (src)[i]; \ + } + +static uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +static uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +typedef union +{ + uint words[64 / sizeof(uint)]; + uint2 uint2s[64 / sizeof(uint2)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint words[200 / sizeof(uint)]; + uint2 uint2s[200 / sizeof(uint2)]; + uint4 uint4s[200 / sizeof(uint4)]; +} hash200_t; + +typedef struct +{ + uint4 uint4s[128 / sizeof(uint4)]; +} hash128_t; + +static void SHA3_512(uint2* s, uint isolate) +{ + for (uint i = 8; i != 25; ++i) + { + s[i] = (uint2){0, 0}; + } + s[8].x = 0x00000001; + s[8].y = 0x80000000; + keccak_f1600_no_absorb(s, 8, isolate); +} + +static uint fast_mod(uint a, uint4 d) +{ + const ulong t = a; + const uint q = ((t + d.y) * d.x) >> d.z; + return a - q * d.w; +} + +__kernel void ethash_calculate_dag_item(uint start, __global hash64_t const* g_light, __global hash64_t* g_dag, uint isolate, uint dag_words, uint4 light_words) +{ + uint const node_index = start + get_global_id(0); + if (node_index >= dag_words) + return; + + hash200_t dag_node; + copy(dag_node.uint4s, g_light[fast_mod(node_index, light_words)].uint4s, 4); + dag_node.words[0] ^= node_index; + SHA3_512(dag_node.uint2s, isolate); + + for (uint i = 0; i != ETHASH_DATASET_PARENTS; ++i) + { + uint parent_index = fast_mod(fnv(node_index ^ i, dag_node.words[i % NODE_WORDS]), light_words); + + for (uint w = 0; w != 4; ++w) + dag_node.uint4s[w] = fnv4(dag_node.uint4s[w], g_light[parent_index].uint4s[w]); + } + + SHA3_512(dag_node.uint2s, isolate); + copy(g_dag[node_index].uint4s, dag_node.uint4s, 4); +} diff --git a/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag_cl.h b/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag_cl.h new file mode 100644 index 0000000..a0e0cc7 --- /dev/null +++ b/miner/core/src/backend/opencl/cl/progpowz/progpowz_dag_cl.h @@ -0,0 +1,702 @@ +#pragma once + +namespace xmrig { + +static const char progpowz_dag_cl[] = { + 0x2f, 0x2a, 0x20, 0x4d, 0x69, 0x6e, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x32, 0x30, 0x32, 0x35, 0x20, 0x4c, 0x65, 0x74, 0x68, 0x65, + 0x61, 0x6e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x42, + 0x61, 0x73, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x58, 0x4d, 0x52, 0x69, + 0x67, 0x20, 0x4b, 0x61, 0x77, 0x50, 0x6f, 0x77, 0x20, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x4c, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, + 0x31, 0x38, 0x2d, 0x32, 0x30, 0x32, 0x31, 0x20, 0x53, 0x43, 0x68, 0x65, + 0x72, 0x6e, 0x79, 0x6b, 0x68, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x43, 0x68, 0x65, 0x72, 0x6e, 0x79, 0x6b, + 0x68, 0x3e, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x43, 0x6f, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x32, + 0x30, 0x32, 0x31, 0x20, 0x58, 0x4d, 0x52, 0x69, 0x67, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, + 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x6d, 0x72, 0x69, 0x67, 0x3e, 0x2c, 0x20, 0x3c, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x40, 0x78, 0x6d, 0x72, 0x69, 0x67, 0x2e, 0x63, + 0x6f, 0x6d, 0x3e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, + 0x20, 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, 0x73, 0x6f, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x3a, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x20, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, + 0x72, 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x61, 0x73, 0x20, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, + 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, + 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x65, 0x69, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x33, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x28, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x20, 0x61, 0x6e, 0x79, 0x20, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x54, + 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, + 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x6f, + 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x66, 0x75, + 0x6c, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20, + 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, 0x3b, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x20, 0x77, + 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20, + 0x2a, 0x20, 0x20, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, + 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, + 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, + 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, + 0x50, 0x55, 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x53, 0x65, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x2a, 0x20, 0x20, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, + 0x20, 0x20, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, + 0x64, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x64, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, 0x20, 0x2a, 0x20, + 0x20, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, + 0x6d, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x2c, 0x20, 0x73, + 0x65, 0x65, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x2f, 0x3e, 0x2e, 0x0a, 0x20, + 0x2a, 0x2f, 0x0a, 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x20, 0x22, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x2f, + 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x44, 0x41, 0x47, 0x20, 0x63, 0x61, 0x6c, + 0x63, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x67, + 0x69, 0x63, 0x20, 0x2d, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x73, + 0x20, 0x4b, 0x61, 0x77, 0x50, 0x6f, 0x77, 0x20, 0x28, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x45, 0x74, 0x68, 0x61, 0x73, 0x68, + 0x20, 0x44, 0x41, 0x47, 0x29, 0x0a, 0x2f, 0x2f, 0x0a, 0x0a, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x45, 0x54, 0x48, 0x41, 0x53, + 0x48, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x53, 0x45, 0x54, 0x5f, 0x50, 0x41, + 0x52, 0x45, 0x4e, 0x54, 0x53, 0x20, 0x35, 0x31, 0x32, 0x0a, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x57, + 0x4f, 0x52, 0x44, 0x53, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2f, 0x20, 0x34, + 0x29, 0x0a, 0x0a, 0x5f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, + 0x30, 0x30, 0x5f, 0x52, 0x43, 0x5b, 0x32, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, + 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, + 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, + 0x30, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x38, 0x30, 0x38, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x61, 0x2c, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x38, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x39, 0x2c, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x62, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, + 0x62, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, + 0x38, 0x39, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, + 0x30, 0x30, 0x33, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x38, 0x30, 0x30, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x38, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x38, 0x30, 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x38, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x31, 0x2c, 0x20, 0x30, 0x78, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, 0x30, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x38, 0x30, 0x38, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x28, + 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2c, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, + 0x28, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x38, 0x30, 0x30, 0x38, 0x2c, + 0x20, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x2c, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x50, 0x4c, + 0x41, 0x54, 0x46, 0x4f, 0x52, 0x4d, 0x20, 0x3d, 0x3d, 0x20, 0x4f, 0x50, + 0x45, 0x4e, 0x43, 0x4c, 0x5f, 0x50, 0x4c, 0x41, 0x54, 0x46, 0x4f, 0x52, + 0x4d, 0x5f, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0x20, 0x26, 0x26, 0x20, + 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x45, 0x20, 0x3e, 0x3d, 0x20, 0x33, + 0x35, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x61, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x20, 0x3e, 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, 0x2e, 0x6c, 0x2e, + 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, 0x25, 0x30, 0x2c, + 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, 0x25, 0x33, 0x3b, + 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x22, 0x72, 0x22, + 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x61, + 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, 0x2e, + 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, 0x25, + 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, 0x25, + 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x29, 0x20, 0x3a, 0x20, 0x22, + 0x72, 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, + 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, 0x2e, 0x6c, + 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, 0x25, 0x30, + 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, 0x25, 0x33, + 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x29, 0x20, 0x3a, 0x20, 0x22, 0x72, + 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, + 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x73, 0x6d, 0x28, 0x22, 0x73, 0x68, 0x66, + 0x2e, 0x6c, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x2e, 0x62, 0x33, 0x32, 0x20, + 0x25, 0x30, 0x2c, 0x20, 0x25, 0x31, 0x2c, 0x20, 0x25, 0x32, 0x2c, 0x20, + 0x25, 0x33, 0x3b, 0x22, 0x20, 0x3a, 0x20, 0x22, 0x3d, 0x72, 0x22, 0x28, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x29, 0x20, 0x3a, 0x20, + 0x22, 0x72, 0x22, 0x28, 0x61, 0x2e, 0x78, 0x29, 0x2c, 0x20, 0x22, 0x72, + 0x22, 0x28, 0x61, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x22, 0x72, 0x22, 0x28, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x7d, + 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x28, 0x63, 0x6c, 0x5f, 0x61, 0x6d, 0x64, 0x5f, 0x6d, 0x65, + 0x64, 0x69, 0x61, 0x5f, 0x6f, 0x70, 0x73, 0x29, 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, 0x20, 0x3a, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x76, 0x76, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x20, 0x3c, + 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x61, 0x6d, 0x64, 0x5f, 0x62, 0x69, 0x74, 0x61, 0x6c, + 0x69, 0x67, 0x6e, 0x28, 0x28, 0x76, 0x76, 0x29, 0x2e, 0x78, 0x79, 0x2c, + 0x20, 0x28, 0x76, 0x76, 0x29, 0x2e, 0x79, 0x78, 0x2c, 0x20, 0x33, 0x32, + 0x20, 0x2d, 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6d, 0x64, 0x5f, 0x62, + 0x69, 0x74, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x28, 0x28, 0x76, 0x76, 0x29, + 0x2e, 0x79, 0x78, 0x2c, 0x20, 0x28, 0x76, 0x76, 0x29, 0x2e, 0x78, 0x79, + 0x2c, 0x20, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x72, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, + 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x76, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x28, 0x6e, 0x20, 0x3c, 0x3d, 0x20, 0x33, 0x32, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x20, 0x3d, + 0x20, 0x28, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, + 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3e, 0x3e, + 0x20, 0x28, 0x33, 0x32, 0x20, 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x76, 0x2e, + 0x78, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x29, 0x29, 0x20, 0x7c, 0x20, + 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x33, 0x32, 0x20, + 0x2d, 0x20, 0x6e, 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x79, 0x20, 0x3d, 0x20, + 0x28, 0x28, 0x76, 0x2e, 0x78, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x20, + 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, + 0x79, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x6e, + 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x78, 0x20, 0x3d, 0x20, + 0x28, 0x28, 0x76, 0x2e, 0x79, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x6e, 0x20, + 0x2d, 0x20, 0x33, 0x32, 0x29, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x76, 0x2e, + 0x78, 0x20, 0x3e, 0x3e, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x6e, + 0x29, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, 0x23, 0x65, 0x6e, 0x64, + 0x69, 0x66, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x63, 0x68, 0x69, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, + 0x74, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x74, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x20, + 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x2c, 0x20, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x2c, 0x20, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, + 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, + 0x2b, 0x20, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, + 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, + 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x32, 0x5d, 0x2c, 0x20, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x20, + 0x3d, 0x20, 0x62, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, + 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x74, + 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, + 0x20, 0x2b, 0x20, 0x33, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x34, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, + 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x69, 0x74, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x28, 0x74, 0x5b, 0x6e, 0x20, 0x2b, + 0x20, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, + 0x31, 0x5d, 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x34, 0x5d, + 0x2c, 0x20, 0x74, 0x5b, 0x6e, 0x20, 0x2b, 0x20, 0x30, 0x5d, 0x29, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, + 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x72, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x20, 0x74, 0x5b, 0x32, 0x35, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x75, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, + 0x74, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x30, 0x5d, 0x20, + 0x3d, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x35, + 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x30, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x30, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x5d, 0x20, + 0x3d, 0x20, 0x61, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x36, + 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x31, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x5d, 0x20, + 0x3d, 0x20, 0x61, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x37, + 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x32, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, + 0x3d, 0x20, 0x61, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x38, + 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x33, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x33, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, + 0x3d, 0x20, 0x61, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x39, + 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x5e, 0x20, + 0x61, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x5e, 0x20, 0x61, 0x5b, 0x32, 0x34, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, + 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x35, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x30, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, + 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, + 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x36, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x31, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x31, 0x36, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x31, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, + 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, + 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x37, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x32, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, + 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x38, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x33, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x31, 0x38, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x20, 0x3d, 0x20, 0x74, + 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x74, + 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x39, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x31, 0x34, 0x5d, + 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x75, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x5b, 0x32, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x75, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x52, + 0x68, 0x6f, 0x20, 0x50, 0x69, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x30, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x31, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x30, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x36, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, + 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x32, 0x38, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x31, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x32, 0x37, 0x29, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x36, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x35, 0x5d, + 0x2c, 0x20, 0x33, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, + 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x34, 0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, + 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x36, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x31, 0x5d, 0x20, 0x3d, + 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x38, 0x5d, 0x2c, 0x20, + 0x35, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x36, + 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x39, + 0x5d, 0x2c, 0x20, 0x32, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x33, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x37, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, + 0x31, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, + 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, + 0x32, 0x5d, 0x2c, 0x20, 0x34, 0x33, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x61, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x32, 0x35, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x32, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x34, 0x5d, + 0x2c, 0x20, 0x33, 0x39, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x5b, 0x32, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x34, 0x31, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x38, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x36, 0x5d, 0x2c, 0x20, + 0x34, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, + 0x38, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, + 0x31, 0x37, 0x5d, 0x2c, 0x20, 0x31, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x74, 0x5b, 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, + 0x32, 0x28, 0x61, 0x5b, 0x31, 0x38, 0x5d, 0x2c, 0x20, 0x32, 0x31, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x31, 0x33, 0x5d, 0x20, + 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x31, 0x39, 0x5d, + 0x2c, 0x20, 0x38, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, + 0x61, 0x5b, 0x32, 0x30, 0x5d, 0x2c, 0x20, 0x31, 0x38, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x32, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x31, 0x5d, 0x2c, 0x20, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x39, 0x5d, + 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x32, + 0x5d, 0x2c, 0x20, 0x36, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x74, 0x5b, 0x31, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x52, 0x4f, 0x4c, 0x32, + 0x28, 0x61, 0x5b, 0x32, 0x33, 0x5d, 0x2c, 0x20, 0x35, 0x36, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, + 0x52, 0x4f, 0x4c, 0x32, 0x28, 0x61, 0x5b, 0x32, 0x34, 0x5d, 0x2c, 0x20, + 0x31, 0x34, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x43, 0x68, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, + 0x28, 0x61, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x49, 0x6f, 0x74, 0x61, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x61, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x3d, 0x20, + 0x4b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, + 0x5f, 0x52, 0x43, 0x5b, 0x72, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x74, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, + 0x2c, 0x20, 0x31, 0x30, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x68, 0x69, 0x28, 0x61, 0x2c, 0x20, 0x31, 0x35, 0x2c, + 0x20, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x68, 0x69, + 0x28, 0x61, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x74, 0x29, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, + 0x36, 0x30, 0x30, 0x5f, 0x6e, 0x6f, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x72, + 0x62, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x61, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x6f, 0x6c, + 0x61, 0x74, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x49, 0x20, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, + 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x62, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x75, + 0x72, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, + 0x64, 0x65, 0x2c, 0x20, 0x68, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, 0x20, + 0x49, 0x20, 0x68, 0x61, 0x76, 0x65, 0x6e, 0x27, 0x74, 0x20, 0x64, 0x6f, + 0x6e, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x63, + 0x61, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, + 0x4d, 0x44, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, + 0x74, 0x6f, 0x20, 0x62, 0x6c, 0x6f, 0x77, 0x20, 0x75, 0x70, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x56, 0x47, 0x50, 0x52, 0x20, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x20, 0x3d, 0x20, 0x32, 0x35, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x72, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x72, 0x20, + 0x3c, 0x20, 0x32, 0x34, 0x3b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x20, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x20, 0x73, 0x74, 0x6f, 0x70, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x4d, 0x44, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x75, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x6f, + 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x61, 0x76, 0x65, 0x73, 0x20, + 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x33, 0x33, 0x25, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x56, 0x47, 0x50, 0x52, 0x73, 0x2c, 0x20, + 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x61, + 0x69, 0x6e, 0x20, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x77, 0x61, + 0x76, 0x65, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x2e, 0x20, 0x49, 0x64, 0x65, + 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x77, 0x65, 0x27, 0x64, 0x20, 0x67, 0x65, + 0x74, 0x20, 0x34, 0x20, 0x69, 0x6e, 0x20, 0x66, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x33, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x20, 0x49, 0x20, 0x63, + 0x61, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6d, 0x61, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x75, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x64, 0x6f, + 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x73, 0x65, 0x65, 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x20, 0x68, 0x6f, 0x77, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6d, 0x75, 0x63, 0x68, 0x20, + 0x77, 0x65, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, + 0x65, 0x6c, 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, + 0x69, 0x6c, 0x65, 0x72, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x56, 0x47, + 0x50, 0x52, 0x73, 0x20, 0x62, 0x65, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, + 0x69, 0x74, 0x20, 0x73, 0x65, 0x65, 0x6d, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x72, 0x6f, 0x77, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x6e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x77, + 0x61, 0x79, 0x2c, 0x20, 0x68, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x20, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x6e, + 0x27, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x69, + 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x66, 0x31, 0x36, 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x28, 0x61, 0x2c, 0x20, 0x72, 0x2b, 0x2b, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x33, + 0x29, 0x20, 0x6f, 0x20, 0x3d, 0x20, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x69, + 0x7a, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, + 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, 0x36, + 0x30, 0x30, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x61, 0x2c, 0x20, + 0x32, 0x33, 0x2c, 0x20, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x28, 0x64, 0x73, 0x74, 0x2c, 0x20, + 0x73, 0x72, 0x63, 0x2c, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x29, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, 0x20, + 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x64, 0x73, 0x74, 0x29, 0x5b, 0x69, 0x5d, 0x20, 0x3d, + 0x20, 0x28, 0x73, 0x72, 0x63, 0x29, 0x5b, 0x69, 0x5d, 0x3b, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6e, 0x76, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x79, 0x29, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, + 0x49, 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, + 0x20, 0x66, 0x6e, 0x76, 0x34, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, + 0x78, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x79, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x78, 0x20, 0x2a, 0x20, 0x46, 0x4e, 0x56, 0x5f, 0x50, 0x52, 0x49, + 0x4d, 0x45, 0x20, 0x5e, 0x20, 0x79, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, + 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, + 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x32, 0x73, 0x5b, 0x36, 0x34, 0x20, 0x2f, 0x20, + 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x29, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x36, 0x34, 0x20, + 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, + 0x74, 0x34, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, + 0x36, 0x34, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x65, 0x66, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, + 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x32, 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, 0x20, 0x73, 0x69, + 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x32, 0x30, 0x30, 0x20, 0x2f, + 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, + 0x34, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x32, + 0x30, 0x30, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, + 0x65, 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x31, 0x32, 0x38, 0x20, 0x2f, 0x20, 0x73, + 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x29, + 0x5d, 0x3b, 0x0a, 0x7d, 0x20, 0x68, 0x61, 0x73, 0x68, 0x31, 0x32, 0x38, + 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, + 0x76, 0x6f, 0x69, 0x64, 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x35, 0x31, + 0x32, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x2a, 0x20, 0x73, 0x2c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, + 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x38, 0x3b, + 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x32, 0x35, 0x3b, 0x20, 0x2b, 0x2b, + 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x32, 0x29, 0x7b, 0x30, 0x2c, 0x20, 0x30, + 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x5b, 0x38, 0x5d, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x5b, 0x38, 0x5d, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x30, + 0x78, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x5f, 0x66, 0x31, + 0x36, 0x30, 0x30, 0x5f, 0x6e, 0x6f, 0x5f, 0x61, 0x62, 0x73, 0x6f, 0x72, + 0x62, 0x28, 0x73, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x69, 0x73, 0x6f, 0x6c, + 0x61, 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x61, 0x73, + 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x61, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x20, 0x64, 0x29, 0x0a, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x61, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x71, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x74, 0x20, 0x2b, + 0x20, 0x64, 0x2e, 0x79, 0x29, 0x20, 0x2a, 0x20, 0x64, 0x2e, 0x78, 0x29, + 0x20, 0x3e, 0x3e, 0x20, 0x64, 0x2e, 0x7a, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x2d, 0x20, + 0x71, 0x20, 0x2a, 0x20, 0x64, 0x2e, 0x77, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 0x76, 0x6f, 0x69, + 0x64, 0x20, 0x65, 0x74, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x63, 0x61, 0x6c, + 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x67, 0x5f, 0x69, + 0x74, 0x65, 0x6d, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x68, 0x61, 0x73, 0x68, 0x36, 0x34, 0x5f, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x2a, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x68, + 0x61, 0x73, 0x68, 0x36, 0x34, 0x5f, 0x74, 0x2a, 0x20, 0x67, 0x5f, 0x64, + 0x61, 0x67, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x6f, + 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x64, + 0x61, 0x67, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x34, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x77, 0x6f, + 0x72, 0x64, 0x73, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x20, 0x2b, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3e, 0x3d, 0x20, 0x64, + 0x61, 0x67, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x73, 0x68, 0x32, + 0x30, 0x30, 0x5f, 0x74, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, + 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x28, + 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, + 0x74, 0x34, 0x73, 0x2c, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x5b, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x6c, 0x69, + 0x67, 0x68, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x29, 0x5d, 0x2e, + 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x2c, 0x20, 0x34, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x3d, + 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x35, 0x31, + 0x32, 0x28, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, + 0x69, 0x6e, 0x74, 0x32, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, + 0x74, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x20, 0x3d, 0x20, + 0x30, 0x3b, 0x20, 0x69, 0x20, 0x21, 0x3d, 0x20, 0x45, 0x54, 0x48, 0x41, + 0x53, 0x48, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x53, 0x45, 0x54, 0x5f, 0x50, + 0x41, 0x52, 0x45, 0x4e, 0x54, 0x53, 0x3b, 0x20, 0x2b, 0x2b, 0x69, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, + 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x28, 0x66, 0x6e, 0x76, 0x28, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x5e, + 0x20, 0x69, 0x2c, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x69, 0x20, 0x25, 0x20, 0x4e, + 0x4f, 0x44, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x53, 0x5d, 0x29, 0x2c, + 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x77, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x77, 0x20, 0x21, 0x3d, 0x20, 0x34, 0x3b, + 0x20, 0x2b, 0x2b, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x77, 0x5d, + 0x20, 0x3d, 0x20, 0x66, 0x6e, 0x76, 0x34, 0x28, 0x64, 0x61, 0x67, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, + 0x77, 0x5d, 0x2c, 0x20, 0x67, 0x5f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5b, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x5b, 0x77, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x35, 0x31, 0x32, 0x28, 0x64, 0x61, + 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x32, + 0x73, 0x2c, 0x20, 0x69, 0x73, 0x6f, 0x6c, 0x61, 0x74, 0x65, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x28, 0x67, 0x5f, + 0x64, 0x61, 0x67, 0x5b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5d, 0x2e, 0x75, 0x69, 0x6e, 0x74, 0x34, 0x73, 0x2c, 0x20, + 0x64, 0x61, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x75, 0x69, 0x6e, + 0x00 +}; + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/generators/ocl_generic_blake3_generator.cpp b/miner/core/src/backend/opencl/generators/ocl_generic_blake3_generator.cpp new file mode 100644 index 0000000..ce678f3 --- /dev/null +++ b/miner/core/src/backend/opencl/generators/ocl_generic_blake3_generator.cpp @@ -0,0 +1,73 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + + +#include "backend/opencl/OclThreads.h" +#include "backend/opencl/wrappers/OclDevice.h" +#include "base/crypto/Algorithm.h" + + +#include + + +namespace xmrig { + + +bool ocl_generic_blake3_generator(const OclDevice &device, const Algorithm &algorithm, OclThreads &threads) +{ + if (algorithm.family() != Algorithm::BLAKE3) { + return false; + } + + // Blake3 is compute-bound with minimal memory requirements + // Each work item only needs ~200 bytes for state + + // Calculate intensity based on compute units + // Blake3 is very parallel - maximize GPU utilization + uint32_t intensity = device.computeUnits() * 1024 * 8; + + // Cap intensity based on available memory (very permissive for Blake3) + const size_t freeMem = device.freeMemSize(); + const size_t memPerThread = 256; // ~256 bytes per work item + const uint32_t maxByMem = static_cast(freeMem / memPerThread); + intensity = std::min(intensity, maxByMem); + + // Round down to work group size multiple + uint32_t worksize = 256; + + // NVIDIA cards often perform better with smaller work groups + if (device.vendorId() == OCL_VENDOR_NVIDIA) { + worksize = 128; + } + + intensity = (intensity / worksize) * worksize; + + // Minimum intensity + if (intensity < worksize * 256) { + intensity = worksize * 256; + } + + // Maximum intensity (avoid GPU timeout) + intensity = std::min(intensity, static_cast(1 << 24)); + + threads.add(OclThread(device.index(), intensity, worksize, 1)); + + return true; +} + + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/generators/ocl_generic_etchash_generator.cpp b/miner/core/src/backend/opencl/generators/ocl_generic_etchash_generator.cpp new file mode 100644 index 0000000..4231aaa --- /dev/null +++ b/miner/core/src/backend/opencl/generators/ocl_generic_etchash_generator.cpp @@ -0,0 +1,84 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + + +#include "backend/opencl/OclThreads.h" +#include "backend/opencl/wrappers/OclDevice.h" +#include "base/crypto/Algorithm.h" + + +#include + + +namespace xmrig { + + +bool ocl_generic_etchash_generator(const OclDevice &device, const Algorithm &algorithm, OclThreads &threads) +{ + if (algorithm.family() != Algorithm::ETCHASH) { + return false; + } + + // ETChash requires at least 3GB free memory for DAG (current epoch ~5GB) + if (device.freeMemSize() < (3ULL * 1024 * 1024 * 1024)) { + return false; + } + + // Calculate intensity based on device memory and compute units + const size_t freeMem = device.freeMemSize(); + + // Reserve memory for DAG (~6GB for later epochs) and light cache + const size_t dag_size = 6ULL * 1024 * 1024 * 1024; // Max DAG size estimate + const size_t cache_size = 128 * 1024 * 1024; // Cache size estimate + const size_t available = freeMem > (dag_size + cache_size) ? freeMem - dag_size - cache_size : 0; + + // Each work item needs approximately 128 bytes of mix state + uint32_t intensity = static_cast(std::min(available / 128, static_cast(1U << 24))); + + // Round down to work group size multiple + intensity = (intensity / 128) * 128; + + // Minimum intensity + if (intensity < 128 * 1024) { + intensity = 128 * 1024; + } + + // Maximum intensity + intensity = std::min(intensity, static_cast(1 << 23)); + + // Determine optimal work size based on device type + uint32_t worksize = 128; + + // NVIDIA cards often perform better with smaller work groups + if (device.vendorId() == OCL_VENDOR_NVIDIA) { + worksize = 64; + // Reduce intensity for NVIDIA to avoid timeout + intensity = std::min(intensity, static_cast(1 << 20)); + } + + // AMD Navi architecture (gfx10xx) may need adjustment + if (device.type() >= OclDevice::Navi_10 && device.type() <= OclDevice::Navi_21) { + intensity = std::min(intensity, static_cast(1 << 21)); + } + + threads.add(OclThread(device.index(), intensity, worksize, 1)); + + return true; +} + + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/generators/ocl_generic_progpowz_generator.cpp b/miner/core/src/backend/opencl/generators/ocl_generic_progpowz_generator.cpp new file mode 100644 index 0000000..fa64baf --- /dev/null +++ b/miner/core/src/backend/opencl/generators/ocl_generic_progpowz_generator.cpp @@ -0,0 +1,87 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + + +#include "backend/opencl/OclThreads.h" +#include "backend/opencl/wrappers/OclDevice.h" +#include "base/crypto/Algorithm.h" +#include "crypto/progpowz/ProgPowZHash.h" + + +#include + + +namespace xmrig { + + +bool ocl_generic_progpowz_generator(const OclDevice &device, const Algorithm &algorithm, OclThreads &threads) +{ + if (algorithm.family() != Algorithm::PROGPOWZ) { + return false; + } + + // ProgPowZ requires at least 2GB free memory for DAG + if (device.freeMemSize() < (2ULL * 1024 * 1024 * 1024)) { + return false; + } + + // Calculate intensity based on device memory and compute units + const size_t freeMem = device.freeMemSize(); + + // Reserve memory for DAG (~4GB for later epochs) and light cache + // Use remaining memory for intensity + const size_t dag_size = 4ULL * 1024 * 1024 * 1024; // Max DAG size estimate + const size_t cache_size = 64 * 1024 * 1024; // Cache size estimate + const size_t available = freeMem - dag_size - cache_size; + + // Each work item needs approximately 128 bytes of mix registers + uint32_t intensity = static_cast(std::min(available / 128, static_cast(1U << 24))); + + // Round down to work group size multiple + intensity = (intensity / 256) * 256; + + // Minimum intensity + if (intensity < 256 * 1024) { + intensity = 256 * 1024; + } + + // Determine optimal work size based on device type + uint32_t worksize = 256; + + // NVIDIA cards often perform better with smaller work groups + if (device.vendorId() == OCL_VENDOR_NVIDIA) { + worksize = 128; + // Reduce intensity for NVIDIA to avoid timeout + intensity = std::min(intensity, static_cast(1 << 20)); + } + + // AMD Navi architecture (gfx10xx) may need adjustment + if (device.type() >= OclDevice::Navi_10 && device.type() <= OclDevice::Navi_21) { + intensity = std::min(intensity, static_cast(1 << 21)); + } + + threads.add(OclThread(device.index(), intensity, worksize, 1)); + + return true; +} + + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.cpp b/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.cpp new file mode 100644 index 0000000..c7ca03f --- /dev/null +++ b/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.cpp @@ -0,0 +1,45 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#include "Etchash_CalculateDAGKernel.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "crypto/etchash/ETCCache.h" + + +void xmrig::Etchash_CalculateDAGKernel::enqueue(cl_command_queue queue, size_t threads, size_t workgroup_size) +{ + enqueueNDRange(queue, 1, nullptr, &threads, &workgroup_size); +} + + +void xmrig::Etchash_CalculateDAGKernel::setArgs(uint32_t start, cl_mem g_light, cl_mem g_dag, uint32_t dag_words, uint32_t light_words) +{ + setArg(0, sizeof(start), &start); + setArg(1, sizeof(cl_mem), &g_light); + setArg(2, sizeof(cl_mem), &g_dag); + + const uint32_t isolate = 1; + setArg(3, sizeof(isolate), &isolate); + + setArg(4, sizeof(dag_words), &dag_words); + + uint32_t light_words4[4]; + ETCCache::calculate_fast_mod_data(light_words, light_words4[0], light_words4[1], light_words4[2]); + light_words4[3] = light_words; + + setArg(5, sizeof(light_words4), light_words4); +} diff --git a/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h b/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h new file mode 100644 index 0000000..094b554 --- /dev/null +++ b/miner/core/src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h @@ -0,0 +1,41 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#ifndef XMRIG_ETCHASH_CALCULATEDAGKERNEL_H +#define XMRIG_ETCHASH_CALCULATEDAGKERNEL_H + + +#include "backend/opencl/wrappers/OclKernel.h" + + +namespace xmrig { + + +class Etchash_CalculateDAGKernel : public OclKernel +{ +public: + inline Etchash_CalculateDAGKernel(cl_program program) : OclKernel(program, "ethash_calculate_dag_item") {} + + void enqueue(cl_command_queue queue, size_t threads, size_t workgroup_size); + void setArgs(uint32_t start, cl_mem g_light, cl_mem g_dag, uint32_t dag_words, uint32_t light_words); +}; + + +} // namespace xmrig + + +#endif /* XMRIG_ETCHASH_CALCULATEDAGKERNEL_H */ diff --git a/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.cpp b/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.cpp new file mode 100644 index 0000000..8008756 --- /dev/null +++ b/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.cpp @@ -0,0 +1,49 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#include "ProgPowZ_CalculateDAGKernel.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "crypto/progpowz/ProgPowZCache.h" + + +void xmrig::ProgPowZ_CalculateDAGKernel::enqueue(cl_command_queue queue, size_t threads, size_t workgroup_size) +{ + enqueueNDRange(queue, 1, nullptr, &threads, &workgroup_size); +} + + +void xmrig::ProgPowZ_CalculateDAGKernel::setArgs(uint32_t start, cl_mem g_light, cl_mem g_dag, uint32_t dag_words, uint32_t light_words) +{ + setArg(0, sizeof(start), &start); + setArg(1, sizeof(cl_mem), &g_light); + setArg(2, sizeof(cl_mem), &g_dag); + + const uint32_t isolate = 1; + setArg(3, sizeof(isolate), &isolate); + + setArg(4, sizeof(dag_words), &dag_words); + + uint32_t light_words4[4]; + ProgPowZCache::calculate_fast_mod_data(light_words, light_words4[0], light_words4[1], light_words4[2]); + light_words4[3] = light_words; + + setArg(5, sizeof(light_words4), light_words4); +} diff --git a/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h b/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h new file mode 100644 index 0000000..9147fa2 --- /dev/null +++ b/miner/core/src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h @@ -0,0 +1,45 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_PROGPOWZ_CALCULATEDAGKERNEL_H +#define XMRIG_PROGPOWZ_CALCULATEDAGKERNEL_H + + +#include "backend/opencl/wrappers/OclKernel.h" + + +namespace xmrig { + + +class ProgPowZ_CalculateDAGKernel : public OclKernel +{ +public: + inline ProgPowZ_CalculateDAGKernel(cl_program program) : OclKernel(program, "ethash_calculate_dag_item") {} + + void enqueue(cl_command_queue queue, size_t threads, size_t workgroup_size); + void setArgs(uint32_t start, cl_mem g_light, cl_mem g_dag, uint32_t dag_words, uint32_t light_words); +}; + + +} // namespace xmrig + + +#endif /* XMRIG_PROGPOWZ_CALCULATEDAGKERNEL_H */ diff --git a/miner/core/src/backend/opencl/opencl.cmake b/miner/core/src/backend/opencl/opencl.cmake index 6c6adc2..fc7464a 100644 --- a/miner/core/src/backend/opencl/opencl.cmake +++ b/miner/core/src/backend/opencl/opencl.cmake @@ -130,6 +130,45 @@ if (WITH_OPENCL) ) endif() + if (WITH_PROGPOWZ) + list(APPEND HEADERS_BACKEND_OPENCL + src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h + src/backend/opencl/runners/OclProgPowZRunner.h + src/backend/opencl/runners/tools/OclProgPowZ.h + ) + + list(APPEND SOURCES_BACKEND_OPENCL + src/backend/opencl/generators/ocl_generic_progpowz_generator.cpp + src/backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.cpp + src/backend/opencl/runners/OclProgPowZRunner.cpp + src/backend/opencl/runners/tools/OclProgPowZ.cpp + ) + endif() + + if (WITH_ETCHASH) + list(APPEND HEADERS_BACKEND_OPENCL + src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h + src/backend/opencl/runners/OclEtchashRunner.h + ) + + list(APPEND SOURCES_BACKEND_OPENCL + src/backend/opencl/generators/ocl_generic_etchash_generator.cpp + src/backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.cpp + src/backend/opencl/runners/OclEtchashRunner.cpp + ) + endif() + + if (WITH_BLAKE3DCR) + list(APPEND HEADERS_BACKEND_OPENCL + src/backend/opencl/runners/OclBlake3Runner.h + ) + + list(APPEND SOURCES_BACKEND_OPENCL + src/backend/opencl/generators/ocl_generic_blake3_generator.cpp + src/backend/opencl/runners/OclBlake3Runner.cpp + ) + endif() + if (WITH_STRICT_CACHE) add_definitions(/DXMRIG_STRICT_OPENCL_CACHE) else() diff --git a/miner/core/src/backend/opencl/runners/OclBlake3Runner.cpp b/miner/core/src/backend/opencl/runners/OclBlake3Runner.cpp new file mode 100644 index 0000000..87e8f34 --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclBlake3Runner.cpp @@ -0,0 +1,173 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#include + + +#include "backend/opencl/runners/OclBlake3Runner.h" +#include "backend/common/Tags.h" +#include "backend/opencl/OclLaunchData.h" +#include "base/io/log/Tags.h" +#include "backend/opencl/wrappers/OclError.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "base/io/log/Log.h" +#include "base/net/stratum/Job.h" +#include "backend/opencl/cl/blake3/blake3_cl.h" + + +namespace xmrig { + + +// Decred block header size +constexpr size_t BLOCK_HEADER_SIZE = 180; + + +OclBlake3Runner::OclBlake3Runner(size_t index, const OclLaunchData &data) : OclBaseRunner(index, data) +{ + switch (data.thread.worksize()) + { + case 64: + case 128: + case 256: + case 512: + m_workGroupSize = data.thread.worksize(); + break; + } + + if (data.device.vendorId() == OclVendor::OCL_VENDOR_NVIDIA) { + m_options += " -DPLATFORM=OPENCL_PLATFORM_NVIDIA"; + } +} + + +OclBlake3Runner::~OclBlake3Runner() +{ + OclLib::release(m_searchKernel); + OclLib::release(m_searchProgram); + OclLib::release(m_controlQueue); + OclLib::release(m_stop); +} + + +void OclBlake3Runner::run(uint32_t nonce, uint32_t /*nonce_offset*/, uint32_t *hashOutput) +{ + const size_t local_work_size = m_workGroupSize; + const size_t global_work_offset = nonce; + const size_t global_work_size = m_intensity - (m_intensity % m_workGroupSize); + + // Upload block header + enqueueWriteBuffer(m_input, CL_FALSE, 0, m_blobSize, m_blob); + + // Clear results and stop flag + const uint32_t zero[2] = {}; + enqueueWriteBuffer(m_output, CL_FALSE, 0, sizeof(uint32_t), zero); + enqueueWriteBuffer(m_stop, CL_FALSE, 0, sizeof(uint32_t), zero); + + // Run search kernel + const cl_int ret = OclLib::enqueueNDRangeKernel(m_queue, m_searchKernel, 1, &global_work_offset, &global_work_size, &local_work_size, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + LOG_ERR("%s" RED(" error ") RED_BOLD("%s") RED(" when calling ") RED_BOLD("clEnqueueNDRangeKernel") RED(" for kernel ") RED_BOLD("blake3_search"), + ocl_tag(), OclError::toString(ret)); + + throw std::runtime_error(OclError::toString(ret)); + } + + // Read results + uint32_t output[16] = {}; + enqueueReadBuffer(m_output, CL_TRUE, 0, sizeof(output), output); + + if (output[0] > 15) { + output[0] = 15; + } + + hashOutput[0xFF] = output[0]; + memcpy(hashOutput, output + 1, output[0] * sizeof(uint32_t)); +} + + +void OclBlake3Runner::set(const Job &job, uint8_t *blob) +{ + m_blob = blob; + m_blobSize = job.size(); + + if (m_blobSize > BLOCK_HEADER_SIZE) { + m_blobSize = BLOCK_HEADER_SIZE; + } + + // Update kernel arguments + const uint64_t target = job.target(); + + OclLib::setKernelArg(m_searchKernel, 0, sizeof(cl_mem), &m_input); + OclLib::setKernelArg(m_searchKernel, 1, sizeof(target), &target); + OclLib::setKernelArg(m_searchKernel, 2, sizeof(cl_mem), &m_output); + OclLib::setKernelArg(m_searchKernel, 3, sizeof(cl_mem), &m_stop); + + enqueueWriteBuffer(m_input, CL_TRUE, 0, m_blobSize, m_blob); +} + + +void OclBlake3Runner::jobEarlyNotification(const Job&) +{ + const uint32_t one = 1; + const cl_int ret = OclLib::enqueueWriteBuffer(m_controlQueue, m_stop, CL_TRUE, 0, sizeof(one), &one, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } +} + + +void OclBlake3Runner::build() +{ + OclBaseRunner::build(); + + // Build search kernel + cl_int ret = 0; + const char* source = blake3_cl; + m_searchProgram = OclLib::createProgramWithSource(m_ctx, 1, &source, nullptr, &ret); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } + + std::string options = m_options; + options += " -DGROUP_SIZE=" + std::to_string(m_workGroupSize); + + cl_device_id device = data().device.id(); + ret = OclLib::buildProgram(m_searchProgram, 1, &device, options.c_str()); + if (ret != CL_SUCCESS) { + LOG_ERR("%s" RED(" Blake3 kernel build failed: %s"), ocl_tag(), OclLib::getProgramBuildLog(m_searchProgram, device).data()); + throw std::runtime_error(OclError::toString(ret)); + } + + m_searchKernel = OclLib::createKernel(m_searchProgram, "blake3_search", &ret); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } + + LOG_INFO("%s " CYAN("Blake3") " OpenCL kernel compiled", Tags::opencl()); +} + + +void OclBlake3Runner::init() +{ + OclBaseRunner::init(); + + m_controlQueue = OclLib::createCommandQueue(m_ctx, data().device.id()); + m_stop = OclLib::createBuffer(m_ctx, CL_MEM_READ_ONLY, sizeof(uint32_t)); +} + + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/runners/OclBlake3Runner.h b/miner/core/src/backend/opencl/runners/OclBlake3Runner.h new file mode 100644 index 0000000..fe9f75c --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclBlake3Runner.h @@ -0,0 +1,61 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#ifndef XMRIG_OCLBLAKE3RUNNER_H +#define XMRIG_OCLBLAKE3RUNNER_H + + +#include "backend/opencl/runners/OclBaseRunner.h" + + +namespace xmrig { + + +class OclBlake3Runner : public OclBaseRunner +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(OclBlake3Runner) + + OclBlake3Runner(size_t index, const OclLaunchData &data); + ~OclBlake3Runner() override; + +protected: + void run(uint32_t nonce, uint32_t nonce_offset, uint32_t *hashOutput) override; + void set(const Job &job, uint8_t *blob) override; + void build() override; + void init() override; + void jobEarlyNotification(const Job& job) override; + uint32_t processedHashes() const override { return m_intensity; } + +private: + uint8_t* m_blob = nullptr; + size_t m_blobSize = 0; + + cl_kernel m_searchKernel = nullptr; + cl_program m_searchProgram = nullptr; + + size_t m_workGroupSize = 256; + + cl_command_queue m_controlQueue = nullptr; + cl_mem m_stop = nullptr; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_OCLBLAKE3RUNNER_H diff --git a/miner/core/src/backend/opencl/runners/OclEtchashRunner.cpp b/miner/core/src/backend/opencl/runners/OclEtchashRunner.cpp new file mode 100644 index 0000000..889b757 --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclEtchashRunner.cpp @@ -0,0 +1,264 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#include + + +#include "backend/opencl/runners/OclEtchashRunner.h" +#include "backend/common/Tags.h" +#include "3rdparty/libethash/ethash_internal.h" +#include "3rdparty/libethash/data_sizes.h" +#include "backend/opencl/kernels/etchash/Etchash_CalculateDAGKernel.h" +#include "backend/opencl/OclLaunchData.h" +#include "backend/opencl/wrappers/OclError.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "base/io/log/Log.h" +#include "base/io/log/Tags.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Chrono.h" +#include "crypto/common/VirtualMemory.h" +#include "crypto/etchash/ETCCache.h" +#include "crypto/etchash/ETChash.h" +#include "backend/opencl/cl/etchash/etchash_cl.h" +#include "backend/opencl/cl/etchash/etchash_dag_cl.h" + + +namespace xmrig { + + +// ETChash uses 30000 blocks per epoch (pre-ECIP-1099) +// Post ECIP-1099 (block 11700000): epoch = 390 + (block - 11700000) / 60000 +constexpr uint32_t EPOCH_LENGTH = 30000; +constexpr uint32_t ECIP1099_BLOCK = 11700000; +constexpr uint32_t ECIP1099_EPOCH = 390; +constexpr size_t BLOB_SIZE = 32; // Header hash size + + +static uint32_t calculateEpoch(uint32_t height, bool isETC) +{ + if (!isETC) { + // Standard Ethash epoch calculation + return height / EPOCH_LENGTH; + } + + // ECIP-1099 epoch calculation for ETC + if (height < ECIP1099_BLOCK) { + return height / EPOCH_LENGTH; + } + return ECIP1099_EPOCH + (height - ECIP1099_BLOCK) / 60000; +} + + +OclEtchashRunner::OclEtchashRunner(size_t index, const OclLaunchData &data) : OclBaseRunner(index, data) +{ + // Determine if this is ETC or ETH + m_isETC = (data.algorithm.id() == Algorithm::ETCHASH_ETC); + + switch (data.thread.worksize()) + { + case 64: + case 128: + case 256: + m_workGroupSize = data.thread.worksize(); + break; + } + + if (data.device.vendorId() == OclVendor::OCL_VENDOR_NVIDIA) { + m_options += " -DPLATFORM=OPENCL_PLATFORM_NVIDIA"; + m_dagWorkGroupSize = 32; + } +} + + +OclEtchashRunner::~OclEtchashRunner() +{ + OclLib::release(m_lightCache); + OclLib::release(m_dag); + + delete m_calculateDagKernel; + + OclLib::release(m_searchKernel); + OclLib::release(m_searchProgram); + + OclLib::release(m_controlQueue); + OclLib::release(m_stop); +} + + +void OclEtchashRunner::run(uint32_t nonce, uint32_t /*nonce_offset*/, uint32_t *hashOutput) +{ + const size_t local_work_size = m_workGroupSize; + const size_t global_work_offset = nonce; + const size_t global_work_size = m_intensity - (m_intensity % m_workGroupSize); + + enqueueWriteBuffer(m_input, CL_FALSE, 0, BLOB_SIZE, m_blob); + + const uint32_t zero[2] = {}; + enqueueWriteBuffer(m_output, CL_FALSE, 0, sizeof(uint32_t), zero); + enqueueWriteBuffer(m_stop, CL_FALSE, 0, sizeof(uint32_t) * 2, zero); + + m_skippedHashes = 0; + + const cl_int ret = OclLib::enqueueNDRangeKernel(m_queue, m_searchKernel, 1, &global_work_offset, &global_work_size, &local_work_size, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + LOG_ERR("%s" RED(" error ") RED_BOLD("%s") RED(" when calling ") RED_BOLD("clEnqueueNDRangeKernel") RED(" for kernel ") RED_BOLD("ethash_search"), + ocl_tag(), OclError::toString(ret)); + + throw std::runtime_error(OclError::toString(ret)); + } + + uint32_t stop[2] = {}; + enqueueReadBuffer(m_stop, CL_FALSE, 0, sizeof(stop), stop); + + uint32_t output[16] = {}; + enqueueReadBuffer(m_output, CL_TRUE, 0, sizeof(output), output); + + m_skippedHashes = stop[1] * m_workGroupSize; + + if (output[0] > 15) { + output[0] = 15; + } + + hashOutput[0xFF] = output[0]; + memcpy(hashOutput, output + 1, output[0] * sizeof(uint32_t)); +} + + +void OclEtchashRunner::set(const Job &job, uint8_t *blob) +{ + m_blockHeight = static_cast(job.height()); + + const uint32_t epoch = calculateEpoch(m_blockHeight, m_isETC); + + const uint64_t dag_size = ETCCache::dagSize(epoch); + if (dag_size > m_dagCapacity) { + OclLib::release(m_dag); + + m_dagCapacity = VirtualMemory::align(dag_size, 16 * 1024 * 1024); + m_dag = OclLib::createBuffer(m_ctx, CL_MEM_READ_WRITE, m_dagCapacity); + } + + if (epoch != m_epoch) { + m_epoch = epoch; + + { + std::lock_guard lock(ETCCache::s_cacheMutex); + + ETCCache& cache = m_isETC ? ETCCache::s_etcCache : ETCCache::s_ethCache; + cache.init(epoch, m_isETC); + + if (cache.size() > m_lightCacheCapacity) { + OclLib::release(m_lightCache); + + m_lightCacheCapacity = VirtualMemory::align(cache.size()); + m_lightCache = OclLib::createBuffer(m_ctx, CL_MEM_READ_ONLY, m_lightCacheCapacity); + } + + m_lightCacheSize = cache.size(); + enqueueWriteBuffer(m_lightCache, CL_TRUE, 0, m_lightCacheSize, cache.data()); + } + + const uint64_t start_ms = Chrono::steadyMSecs(); + + const uint32_t dag_words = dag_size / sizeof(node); + m_calculateDagKernel->setArgs(0, m_lightCache, m_dag, dag_words, m_lightCacheSize / sizeof(node)); + + constexpr uint32_t N = 1 << 18; + + for (uint32_t start = 0; start < dag_words; start += N) { + m_calculateDagKernel->setArg(0, sizeof(start), &start); + m_calculateDagKernel->enqueue(m_queue, N, m_dagWorkGroupSize); + } + + OclLib::finish(m_queue); + + const char* algoName = m_isETC ? "ETChash" : "Ethash"; + LOG_INFO("%s " CYAN("%s") " DAG for epoch " WHITE_BOLD("%u") " calculated " BLACK_BOLD("(%" PRIu64 "ms)"), Tags::opencl(), algoName, epoch, Chrono::steadyMSecs() - start_ms); + } + + // Update search kernel arguments + const uint64_t target = job.target(); + + OclLib::setKernelArg(m_searchKernel, 0, sizeof(cl_mem), &m_dag); + OclLib::setKernelArg(m_searchKernel, 1, sizeof(cl_mem), &m_input); + OclLib::setKernelArg(m_searchKernel, 2, sizeof(target), &target); + + const uint32_t hack_false = 0; + OclLib::setKernelArg(m_searchKernel, 3, sizeof(hack_false), &hack_false); + OclLib::setKernelArg(m_searchKernel, 4, sizeof(cl_mem), &m_output); + OclLib::setKernelArg(m_searchKernel, 5, sizeof(cl_mem), &m_stop); + + m_blob = blob; + enqueueWriteBuffer(m_input, CL_TRUE, 0, BLOB_SIZE, m_blob); +} + + +void OclEtchashRunner::jobEarlyNotification(const Job&) +{ + const uint32_t one = 1; + const cl_int ret = OclLib::enqueueWriteBuffer(m_controlQueue, m_stop, CL_TRUE, 0, sizeof(one), &one, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } +} + + +void xmrig::OclEtchashRunner::build() +{ + OclBaseRunner::build(); + + m_calculateDagKernel = new Etchash_CalculateDAGKernel(m_program); + + // Build search kernel + cl_int ret = 0; + const char* source = etchash_cl; + m_searchProgram = OclLib::createProgramWithSource(m_ctx, 1, &source, nullptr, &ret); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } + + // Calculate DAG elements for current epoch (use a reasonable default) + const uint32_t epoch = 0; // Will be updated in set() + const uint64_t dag_elements = dag_sizes[epoch] / 256; + + std::string options = m_options; + options += " -DPROGPOW_DAG_ELEMENTS=" + std::to_string(dag_elements); + options += " -DGROUP_SIZE=" + std::to_string(m_workGroupSize); + + cl_device_id device = data().device.id(); + ret = OclLib::buildProgram(m_searchProgram, 1, &device, options.c_str()); + if (ret != CL_SUCCESS) { + LOG_ERR("%s" RED(" ETChash kernel build failed: %s"), ocl_tag(), OclLib::getProgramBuildLog(m_searchProgram, device).data()); + throw std::runtime_error(OclError::toString(ret)); + } + + m_searchKernel = OclLib::createKernel(m_searchProgram, "ethash_search", &ret); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } +} + + +void xmrig::OclEtchashRunner::init() +{ + OclBaseRunner::init(); + + m_controlQueue = OclLib::createCommandQueue(m_ctx, data().device.id()); + m_stop = OclLib::createBuffer(m_ctx, CL_MEM_READ_ONLY, sizeof(uint32_t) * 2); +} + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/runners/OclEtchashRunner.h b/miner/core/src/backend/opencl/runners/OclEtchashRunner.h new file mode 100644 index 0000000..40d5044 --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclEtchashRunner.h @@ -0,0 +1,80 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * 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 . + */ + +#ifndef XMRIG_OCLETCHASHRUNNER_H +#define XMRIG_OCLETCHASHRUNNER_H + + +#include "backend/opencl/runners/OclBaseRunner.h" + +#include + +namespace xmrig { + + +class Etchash_CalculateDAGKernel; + + +class OclEtchashRunner : public OclBaseRunner +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(OclEtchashRunner) + + OclEtchashRunner(size_t index, const OclLaunchData &data); + ~OclEtchashRunner() override; + +protected: + void run(uint32_t nonce, uint32_t nonce_offset, uint32_t *hashOutput) override; + void set(const Job &job, uint8_t *blob) override; + void build() override; + void init() override; + void jobEarlyNotification(const Job& job) override; + uint32_t processedHashes() const override { return m_intensity - m_skippedHashes; } + +private: + uint8_t* m_blob = nullptr; + uint32_t m_skippedHashes = 0; + + uint32_t m_blockHeight = 0; + uint32_t m_epoch = 0xFFFFFFFFUL; + + cl_mem m_lightCache = nullptr; + size_t m_lightCacheSize = 0; + size_t m_lightCacheCapacity = 0; + + cl_mem m_dag = nullptr; + size_t m_dagCapacity = 0; + + Etchash_CalculateDAGKernel* m_calculateDagKernel = nullptr; + + cl_kernel m_searchKernel = nullptr; + cl_program m_searchProgram = nullptr; + + size_t m_workGroupSize = 128; + size_t m_dagWorkGroupSize = 64; + + cl_command_queue m_controlQueue = nullptr; + cl_mem m_stop = nullptr; + + bool m_isETC = true; // true for ETChash, false for Ethash +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_OCLETCHASHRUNNER_H diff --git a/miner/core/src/backend/opencl/runners/OclProgPowZRunner.cpp b/miner/core/src/backend/opencl/runners/OclProgPowZRunner.cpp new file mode 100644 index 0000000..ba0636e --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclProgPowZRunner.cpp @@ -0,0 +1,213 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#include + + +#include "backend/opencl/runners/OclProgPowZRunner.h" +#include "backend/common/Tags.h" +#include "3rdparty/libethash/ethash_internal.h" +#include "backend/opencl/kernels/progpowz/ProgPowZ_CalculateDAGKernel.h" +#include "backend/opencl/OclLaunchData.h" +#include "backend/opencl/runners/tools/OclProgPowZ.h" +#include "backend/opencl/wrappers/OclError.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "base/io/log/Log.h" +#include "base/io/log/Log.h" +#include "base/io/log/Tags.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Chrono.h" +#include "crypto/common/VirtualMemory.h" +#include "crypto/progpowz/ProgPowZHash.h" + + +namespace xmrig { + + +constexpr size_t BLOB_SIZE = 40; + + +OclProgPowZRunner::OclProgPowZRunner(size_t index, const OclLaunchData &data) : OclBaseRunner(index, data) +{ + switch (data.thread.worksize()) + { + case 64: + case 128: + case 256: + case 512: + m_workGroupSize = data.thread.worksize(); + break; + } + + if (data.device.vendorId() == OclVendor::OCL_VENDOR_NVIDIA) { + m_options += " -DPLATFORM=OPENCL_PLATFORM_NVIDIA"; + m_dagWorkGroupSize = 32; + } +} + + +OclProgPowZRunner::~OclProgPowZRunner() +{ + OclLib::release(m_lightCache); + OclLib::release(m_dag); + + delete m_calculateDagKernel; + + OclLib::release(m_controlQueue); + OclLib::release(m_stop); + + OclProgPowZ::clear(); +} + + +void OclProgPowZRunner::run(uint32_t nonce, uint32_t /*nonce_offset*/, uint32_t *hashOutput) +{ + const size_t local_work_size = m_workGroupSize; + const size_t global_work_offset = nonce; + const size_t global_work_size = m_intensity - (m_intensity % m_workGroupSize); + + enqueueWriteBuffer(m_input, CL_FALSE, 0, BLOB_SIZE, m_blob); + + const uint32_t zero[2] = {}; + enqueueWriteBuffer(m_output, CL_FALSE, 0, sizeof(uint32_t), zero); + enqueueWriteBuffer(m_stop, CL_FALSE, 0, sizeof(uint32_t) * 2, zero); + + m_skippedHashes = 0; + + const cl_int ret = OclLib::enqueueNDRangeKernel(m_queue, m_searchKernel, 1, &global_work_offset, &global_work_size, &local_work_size, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + LOG_ERR("%s" RED(" error ") RED_BOLD("%s") RED(" when calling ") RED_BOLD("clEnqueueNDRangeKernel") RED(" for kernel ") RED_BOLD("progpow_search"), + ocl_tag(), OclError::toString(ret)); + + throw std::runtime_error(OclError::toString(ret)); + } + + uint32_t stop[2] = {}; + enqueueReadBuffer(m_stop, CL_FALSE, 0, sizeof(stop), stop); + + uint32_t output[16] = {}; + enqueueReadBuffer(m_output, CL_TRUE, 0, sizeof(output), output); + + m_skippedHashes = stop[1] * m_workGroupSize; + + if (output[0] > 15) { + output[0] = 15; + } + + hashOutput[0xFF] = output[0]; + memcpy(hashOutput, output + 1, output[0] * sizeof(uint32_t)); +} + + +void OclProgPowZRunner::set(const Job &job, uint8_t *blob) +{ + m_blockHeight = static_cast(job.height()); + m_searchKernel = OclProgPowZ::get(*this, m_blockHeight, m_workGroupSize); + + // ProgPowZ uses standard Ethash epoch length (30000 blocks) + const uint32_t epoch = m_blockHeight / ProgPowZHash::EPOCH_LENGTH; + + const uint64_t dag_size = ProgPowZCache::dag_size(epoch); + if (dag_size > m_dagCapacity) { + OclLib::release(m_dag); + + m_dagCapacity = VirtualMemory::align(dag_size, 16 * 1024 * 1024); + m_dag = OclLib::createBuffer(m_ctx, CL_MEM_READ_WRITE, m_dagCapacity); + } + + if (epoch != m_epoch) { + m_epoch = epoch; + + { + std::lock_guard lock(ProgPowZCache::s_cacheMutex); + + ProgPowZCache::s_cache.init(epoch); + + if (ProgPowZCache::s_cache.size() > m_lightCacheCapacity) { + OclLib::release(m_lightCache); + + m_lightCacheCapacity = VirtualMemory::align(ProgPowZCache::s_cache.size()); + m_lightCache = OclLib::createBuffer(m_ctx, CL_MEM_READ_ONLY, m_lightCacheCapacity); + } + + m_lightCacheSize = ProgPowZCache::s_cache.size(); + enqueueWriteBuffer(m_lightCache, CL_TRUE, 0, m_lightCacheSize, ProgPowZCache::s_cache.data()); + } + + const uint64_t start_ms = Chrono::steadyMSecs(); + + const uint32_t dag_words = dag_size / sizeof(node); + m_calculateDagKernel->setArgs(0, m_lightCache, m_dag, dag_words, m_lightCacheSize / sizeof(node)); + + constexpr uint32_t N = 1 << 18; + + for (uint32_t start = 0; start < dag_words; start += N) { + m_calculateDagKernel->setArg(0, sizeof(start), &start); + m_calculateDagKernel->enqueue(m_queue, N, m_dagWorkGroupSize); + } + + OclLib::finish(m_queue); + + LOG_INFO("%s " CYAN("ProgPowZ") " DAG for epoch " WHITE_BOLD("%u") " calculated " BLACK_BOLD("(%" PRIu64 "ms)"), Tags::opencl(), epoch, Chrono::steadyMSecs() - start_ms); + } + + const uint64_t target = job.target(); + const uint32_t hack_false = 0; + + OclLib::setKernelArg(m_searchKernel, 0, sizeof(cl_mem), &m_dag); + OclLib::setKernelArg(m_searchKernel, 1, sizeof(cl_mem), &m_input); + OclLib::setKernelArg(m_searchKernel, 2, sizeof(target), &target); + OclLib::setKernelArg(m_searchKernel, 3, sizeof(hack_false), &hack_false); + OclLib::setKernelArg(m_searchKernel, 4, sizeof(cl_mem), &m_output); + OclLib::setKernelArg(m_searchKernel, 5, sizeof(cl_mem), &m_stop); + + m_blob = blob; + enqueueWriteBuffer(m_input, CL_TRUE, 0, BLOB_SIZE, m_blob); +} + + +void OclProgPowZRunner::jobEarlyNotification(const Job&) +{ + const uint32_t one = 1; + const cl_int ret = OclLib::enqueueWriteBuffer(m_controlQueue, m_stop, CL_TRUE, 0, sizeof(one), &one, 0, nullptr, nullptr); + if (ret != CL_SUCCESS) { + throw std::runtime_error(OclError::toString(ret)); + } +} + + +void xmrig::OclProgPowZRunner::build() +{ + OclBaseRunner::build(); + + m_calculateDagKernel = new ProgPowZ_CalculateDAGKernel(m_program); +} + + +void xmrig::OclProgPowZRunner::init() +{ + OclBaseRunner::init(); + + m_controlQueue = OclLib::createCommandQueue(m_ctx, data().device.id()); + m_stop = OclLib::createBuffer(m_ctx, CL_MEM_READ_ONLY, sizeof(uint32_t) * 2); +} + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/runners/OclProgPowZRunner.h b/miner/core/src/backend/opencl/runners/OclProgPowZRunner.h new file mode 100644 index 0000000..569dfb5 --- /dev/null +++ b/miner/core/src/backend/opencl/runners/OclProgPowZRunner.h @@ -0,0 +1,82 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_OCLPROGPOWZRUNNER_H +#define XMRIG_OCLPROGPOWZRUNNER_H + + +#include "backend/opencl/runners/OclBaseRunner.h" +#include "crypto/progpowz/ProgPowZCache.h" + +#include + +namespace xmrig { + + +class ProgPowZ_CalculateDAGKernel; + + +class OclProgPowZRunner : public OclBaseRunner +{ +public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(OclProgPowZRunner) + + OclProgPowZRunner(size_t index, const OclLaunchData &data); + ~OclProgPowZRunner() override; + +protected: + void run(uint32_t nonce, uint32_t nonce_offset, uint32_t *hashOutput) override; + void set(const Job &job, uint8_t *blob) override; + void build() override; + void init() override; + void jobEarlyNotification(const Job& job) override; + uint32_t processedHashes() const override { return m_intensity - m_skippedHashes; } + +private: + uint8_t* m_blob = nullptr; + uint32_t m_skippedHashes = 0; + + uint32_t m_blockHeight = 0; + uint32_t m_epoch = 0xFFFFFFFFUL; + + cl_mem m_lightCache = nullptr; + size_t m_lightCacheSize = 0; + size_t m_lightCacheCapacity = 0; + + cl_mem m_dag = nullptr; + size_t m_dagCapacity = 0; + + ProgPowZ_CalculateDAGKernel* m_calculateDagKernel = nullptr; + + cl_kernel m_searchKernel = nullptr; + + size_t m_workGroupSize = 256; + size_t m_dagWorkGroupSize = 64; + + cl_command_queue m_controlQueue = nullptr; + cl_mem m_stop = nullptr; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_OCLPROGPOWZRUNNER_H diff --git a/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.cpp b/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.cpp new file mode 100644 index 0000000..11a41a2 --- /dev/null +++ b/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.cpp @@ -0,0 +1,487 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#include "backend/opencl/runners/tools/OclProgPowZ.h" +#include "3rdparty/libethash/data_sizes.h" +#include "3rdparty/libethash/ethash_internal.h" +#include "backend/opencl/cl/progpowz/progpowz_cl.h" +#include "backend/opencl/interfaces/IOclRunner.h" +#include "backend/opencl/OclCache.h" +#include "backend/opencl/OclLaunchData.h" +#include "backend/opencl/OclThread.h" +#include "backend/opencl/wrappers/OclError.h" +#include "backend/opencl/wrappers/OclLib.h" +#include "base/io/log/Log.h" +#include "base/io/log/Tags.h" +#include "base/tools/Baton.h" +#include "base/tools/Chrono.h" +#include "crypto/progpowz/ProgPowZHash.h" + + +#include +#include +#include +#include +#include +#include +#include + + +namespace xmrig { + + +class ProgPowZCacheEntry +{ +public: + inline ProgPowZCacheEntry(const Algorithm &algo, uint64_t period, uint32_t worksize, uint32_t index, cl_program program, cl_kernel kernel) : + program(program), + kernel(kernel), + m_algo(algo), + m_index(index), + m_period(period), + m_worksize(worksize) + {} + + inline bool isExpired(uint64_t period) const { return m_period + 1 < period; } + inline bool match(const Algorithm &algo, uint64_t period, uint32_t worksize, uint32_t index) const { return m_algo == algo && m_period == period && m_worksize == worksize && m_index == index; } + inline bool match(const IOclRunner &runner, uint64_t period, uint32_t worksize) const { return match(runner.algorithm(), period, worksize, runner.deviceIndex()); } + inline void release() const { OclLib::release(kernel); OclLib::release(program); } + + cl_program program; + cl_kernel kernel; + +private: + Algorithm m_algo; + uint32_t m_index; + uint64_t m_period; + uint32_t m_worksize; +}; + + +class ProgPowZCache +{ +public: + ProgPowZCache() = default; + + inline cl_kernel search(const IOclRunner &runner, uint64_t period, uint32_t worksize) { return search(runner.algorithm(), period, worksize, runner.deviceIndex()); } + + + inline cl_kernel search(const Algorithm &algo, uint64_t period, uint32_t worksize, uint32_t index) + { + std::lock_guard lock(m_mutex); + + for (const auto &entry : m_data) { + if (entry.match(algo, period, worksize, index)) { + return entry.kernel; + } + } + + return nullptr; + } + + + void add(const Algorithm &algo, uint64_t period, uint32_t worksize, uint32_t index, cl_program program, cl_kernel kernel) + { + if (search(algo, period, worksize, index)) { + OclLib::release(kernel); + OclLib::release(program); + return; + } + + std::lock_guard lock(m_mutex); + + gc(period); + m_data.emplace_back(algo, period, worksize, index, program, kernel); + } + + + void clear() + { + std::lock_guard lock(m_mutex); + + for (auto &entry : m_data) { + entry.release(); + } + + m_data.clear(); + } + + +private: + void gc(uint64_t period) + { + for (size_t i = 0; i < m_data.size();) { + auto& entry = m_data[i]; + + if (entry.isExpired(period)) { + entry.release(); + entry = m_data.back(); + m_data.pop_back(); + } + else { + ++i; + } + } + } + + + std::mutex m_mutex; + std::vector m_data; +}; + + +static ProgPowZCache cache; + + +#define rnd() (kiss99(rnd_state)) +#define mix_src() ("mix[" + std::to_string(rnd() % ProgPowZHash::REGS) + "]") +#define mix_dst() ("mix[" + std::to_string(mix_seq_dst[(mix_seq_dst_cnt++) % ProgPowZHash::REGS]) + "]") +#define mix_cache() ("mix[" + std::to_string(mix_seq_cache[(mix_seq_cache_cnt++) % ProgPowZHash::REGS]) + "]") + +class ProgPowZBaton : public Baton +{ +public: + inline ProgPowZBaton(const IOclRunner& runner, uint64_t period, uint32_t worksize) : + runner(runner), + period(period), + worksize(worksize) + {} + + const IOclRunner& runner; + const uint64_t period; + const uint32_t worksize; +}; + + +class ProgPowZBuilder +{ +public: + ~ProgPowZBuilder() + { + if (m_loop) { + uv_async_send(&m_shutdownAsync); + uv_thread_join(&m_loopThread); + delete m_loop; + } + } + + void build_async(const IOclRunner& runner, uint64_t period, uint32_t worksize); + + cl_kernel build(const IOclRunner &runner, uint64_t period, uint32_t worksize) + { + std::lock_guard lock(m_mutex); + + const uint64_t ts = Chrono::steadyMSecs(); + + cl_kernel kernel = cache.search(runner, period, worksize); + if (kernel) { + return kernel; + } + + cl_int ret = 0; + const std::string source = getSource(period); + cl_device_id device = runner.data().device.id(); + const char *s = source.c_str(); + + cl_program program = OclLib::createProgramWithSource(runner.ctx(), 1, &s, nullptr, &ret); + if (ret != CL_SUCCESS) { + return nullptr; + } + + std::string options = " -DPROGPOW_DAG_ELEMENTS="; + + // ProgPowZ uses standard Ethash epoch length (30000 blocks) + const uint64_t epoch = (period * ProgPowZHash::PERIOD_LENGTH) / ProgPowZHash::EPOCH_LENGTH; + const uint64_t dag_elements = dag_sizes[epoch] / 256; + + options += std::to_string(dag_elements); + + options += " -DGROUP_SIZE="; + options += std::to_string(worksize); + + options += runner.buildOptions(); + + if (OclLib::buildProgram(program, 1, &device, options.c_str()) != CL_SUCCESS) { + printf("BUILD LOG:\n%s\n", OclLib::getProgramBuildLog(program, device).data()); + + OclLib::release(program); + return nullptr; + } + + kernel = OclLib::createKernel(program, "progpow_search", &ret); + if (ret != CL_SUCCESS) { + OclLib::release(program); + return nullptr; + } + + LOG_INFO("%s " CYAN("ProgPowZ") " program for period " WHITE_BOLD("%" PRIu64) " compiled " BLACK_BOLD("(%" PRIu64 "ms)"), Tags::opencl(), period, Chrono::steadyMSecs() - ts); + + cache.add(runner.algorithm(), period, worksize, runner.deviceIndex(), program, kernel); + + return kernel; + } + + +private: + std::mutex m_mutex; + + typedef struct { + uint32_t z, w, jsr, jcong; + } kiss99_t; + + + static std::string getSource(uint64_t prog_seed) + { + std::stringstream ret; + + uint32_t seed0 = static_cast(prog_seed); + uint32_t seed1 = static_cast(prog_seed >> 32); + + kiss99_t rnd_state; + uint32_t fnv_hash = 0x811c9dc5; + rnd_state.z = fnv1a(fnv_hash, seed0); + rnd_state.w = fnv1a(fnv_hash, seed1); + rnd_state.jsr = fnv1a(fnv_hash, seed0); + rnd_state.jcong = fnv1a(fnv_hash, seed1); + + // Create a random sequence of mix destinations and cache sources + // Merge is a read-modify-write, guaranteeing every mix element is modified every loop + // Guarantee no cache load is duplicated and can be optimized away + int mix_seq_dst[ProgPowZHash::REGS]; + int mix_seq_cache[ProgPowZHash::REGS]; + int mix_seq_dst_cnt = 0; + int mix_seq_cache_cnt = 0; + + for (uint32_t i = 0; i < ProgPowZHash::REGS; i++) { + mix_seq_dst[i] = i; + mix_seq_cache[i] = i; + } + + for (int i = ProgPowZHash::REGS - 1; i > 0; i--) { + int j = 0; + j = rnd() % (i + 1); + std::swap(mix_seq_dst[i], mix_seq_dst[j]); + j = rnd() % (i + 1); + std::swap(mix_seq_cache[i], mix_seq_cache[j]); + } + + // ProgPowZ uses CNT_CACHE=12 and CNT_MATH=20 (vs KawPow's 11 and 18) + for (int i = 0; (i < ProgPowZHash::CNT_CACHE) || (i < ProgPowZHash::CNT_MATH); ++i) { + if (i < ProgPowZHash::CNT_CACHE) { + // Cached memory access + // lanes access random locations + std::string src = mix_cache(); + std::string dest = mix_dst(); + uint32_t r = rnd(); + ret << "offset = " << src << " % PROGPOW_CACHE_WORDS;\n"; + ret << "data = c_dag[offset];\n"; + ret << merge(dest, "data", r); + } + + if (i < ProgPowZHash::CNT_MATH) { + // Random Math + // Generate 2 unique sources + int src_rnd = rnd() % ((ProgPowZHash::REGS - 1) * ProgPowZHash::REGS); + int src1 = src_rnd % ProgPowZHash::REGS; // 0 <= src1 < ProgPowZHash::REGS + int src2 = src_rnd / ProgPowZHash::REGS; // 0 <= src2 < ProgPowZHash::REGS - 1 + + if (src2 >= src1) { + ++src2; // src2 is now any reg other than src1 + } + + std::string src1_str = "mix[" + std::to_string(src1) + "]"; + std::string src2_str = "mix[" + std::to_string(src2) + "]"; + uint32_t r1 = rnd(); + std::string dest = mix_dst(); + uint32_t r2 = rnd(); + ret << math("data", src1_str, src2_str, r1); + ret << merge(dest, "data", r2); + } + } + + std::string kernel = std::regex_replace(std::string(progpowz_cl), std::regex("XMRIG_INCLUDE_PROGPOW_RANDOM_MATH"), ret.str()); + ret.str(std::string()); + + ret << merge("mix[0]", "data_dag.s[0]", rnd()); + + constexpr size_t num_words_per_lane = 256 / (sizeof(uint32_t) * ProgPowZHash::LANES); + for (size_t i = 1; i < num_words_per_lane; i++) + { + std::string dest = mix_dst(); + uint32_t r = rnd(); + ret << merge(dest, "data_dag.s[" + std::to_string(i) + "]", r); + } + + kernel = std::regex_replace(kernel, std::regex("XMRIG_INCLUDE_PROGPOW_DATA_LOADS"), ret.str()); + return kernel; + } + + + static std::string merge(const std::string& a, const std::string& b, uint32_t r) + { + switch (r % 4) + { + case 0: + return a + " = (" + a + " * 33) + " + b + ";\n"; + case 1: + return a + " = (" + a + " ^ " + b + ") * 33;\n"; + case 2: + return a + " = ROTL32(" + a + ", " + std::to_string(((r >> 16) % 31) + 1) + ") ^ " + b + ";\n"; + case 3: + return a + " = ROTR32(" + a + ", " + std::to_string(((r >> 16) % 31) + 1) + ") ^ " + b + ";\n"; + } + return "#error\n"; + } + + + static std::string math(const std::string& d, const std::string& a, const std::string& b, uint32_t r) + { + switch (r % 11) + { + case 0: + return d + " = " + a + " + " + b + ";\n"; + case 1: + return d + " = " + a + " * " + b + ";\n"; + case 2: + return d + " = mul_hi(" + a + ", " + b + ");\n"; + case 3: + return d + " = min(" + a + ", " + b + ");\n"; + case 4: + return d + " = ROTL32(" + a + ", " + b + " % 32);\n"; + case 5: + return d + " = ROTR32(" + a + ", " + b + " % 32);\n"; + case 6: + return d + " = " + a + " & " + b + ";\n"; + case 7: + return d + " = " + a + " | " + b + ";\n"; + case 8: + return d + " = " + a + " ^ " + b + ";\n"; + case 9: + return d + " = clz(" + a + ") + clz(" + b + ");\n"; + case 10: + return d + " = popcount(" + a + ") + popcount(" + b + ");\n"; + } + return "#error\n"; + } + + + static uint32_t fnv1a(uint32_t& h, uint32_t d) + { + return h = (h ^ d) * 0x1000193; + } + + static uint32_t kiss99(kiss99_t& st) + { + st.z = 36969 * (st.z & 65535) + (st.z >> 16); + st.w = 18000 * (st.w & 65535) + (st.w >> 16); + uint32_t MWC = ((st.z << 16) + st.w); + st.jsr ^= (st.jsr << 17); + st.jsr ^= (st.jsr >> 13); + st.jsr ^= (st.jsr << 5); + st.jcong = 69069 * st.jcong + 1234567; + return ((MWC ^ st.jcong) + st.jsr); + } + +private: + uv_loop_t* m_loop = nullptr; + uv_thread_t m_loopThread = {}; + uv_async_t m_shutdownAsync = {}; + uv_async_t m_batonAsync = {}; + + std::vector m_batons; + + static void loop(void* data) + { + ProgPowZBuilder* builder = static_cast(data); + uv_run(builder->m_loop, UV_RUN_DEFAULT); + uv_loop_close(builder->m_loop); + } +}; + + +static ProgPowZBuilder builder; + + +void ProgPowZBuilder::build_async(const IOclRunner& runner, uint64_t period, uint32_t worksize) +{ + std::lock_guard lock(m_mutex); + + if (!m_loop) { + m_loop = new uv_loop_t{}; + uv_loop_init(m_loop); + + uv_async_init(m_loop, &m_shutdownAsync, [](uv_async_t* handle) + { + ProgPowZBuilder* builder = reinterpret_cast(handle->data); + uv_close(reinterpret_cast(&builder->m_shutdownAsync), nullptr); + uv_close(reinterpret_cast(&builder->m_batonAsync), nullptr); + }); + + uv_async_init(m_loop, &m_batonAsync, [](uv_async_t* handle) + { + std::vector batons; + { + ProgPowZBuilder* b = reinterpret_cast(handle->data); + + std::lock_guard lock(b->m_mutex); + batons = std::move(b->m_batons); + } + + for (const ProgPowZBaton& baton : batons) { + builder.build(baton.runner, baton.period, baton.worksize); + } + }); + + m_shutdownAsync.data = this; + m_batonAsync.data = this; + + uv_thread_create(&m_loopThread, loop, this); + } + + m_batons.emplace_back(runner, period, worksize); + uv_async_send(&m_batonAsync); +} + + +cl_kernel OclProgPowZ::get(const IOclRunner &runner, uint64_t height, uint32_t worksize) +{ + // ProgPowZ uses period length of 50 (vs KawPow's 3) + const uint64_t period = height / ProgPowZHash::PERIOD_LENGTH; + + if (!cache.search(runner, period + 1, worksize)) { + builder.build_async(runner, period + 1, worksize); + } + + cl_kernel kernel = cache.search(runner, period, worksize); + if (kernel) { + return kernel; + } + + return builder.build(runner, period, worksize); +} + + +void OclProgPowZ::clear() +{ + cache.clear(); +} + +} // namespace xmrig diff --git a/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.h b/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.h new file mode 100644 index 0000000..479dddd --- /dev/null +++ b/miner/core/src/backend/opencl/runners/tools/OclProgPowZ.h @@ -0,0 +1,50 @@ +/* Miner + * Copyright (c) 2025 Lethean + * + * Based on XMRig KawPow implementation + * Copyright 2018-2021 SChernykh + * Copyright 2016-2021 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_OCLPROGPOWZ_H +#define XMRIG_OCLPROGPOWZ_H + + +#include +#include + + +using cl_kernel = struct _cl_kernel *; + + +namespace xmrig { + + +class IOclRunner; + + +class OclProgPowZ +{ +public: + static cl_kernel get(const IOclRunner &runner, uint64_t height, uint32_t worksize); + static void clear(); +}; + + +} // namespace xmrig + + +#endif /* XMRIG_OCLPROGPOWZ_H */ diff --git a/miner/core/src/backend/opencl/wrappers/OclDevice.cpp b/miner/core/src/backend/opencl/wrappers/OclDevice.cpp index 846b597..984ac74 100644 --- a/miner/core/src/backend/opencl/wrappers/OclDevice.cpp +++ b/miner/core/src/backend/opencl/wrappers/OclDevice.cpp @@ -55,6 +55,18 @@ extern bool ocl_generic_rx_generator(const OclDevice &device, const Algorithm &a extern bool ocl_generic_kawpow_generator(const OclDevice& device, const Algorithm& algorithm, OclThreads& threads); #endif +#ifdef XMRIG_ALGO_PROGPOWZ +extern bool ocl_generic_progpowz_generator(const OclDevice& device, const Algorithm& algorithm, OclThreads& threads); +#endif + +#ifdef XMRIG_ALGO_ETCHASH +extern bool ocl_generic_etchash_generator(const OclDevice& device, const Algorithm& algorithm, OclThreads& threads); +#endif + +#ifdef XMRIG_ALGO_BLAKE3DCR +extern bool ocl_generic_blake3_generator(const OclDevice& device, const Algorithm& algorithm, OclThreads& threads); +#endif + extern bool ocl_vega_cn_generator(const OclDevice &device, const Algorithm &algorithm, OclThreads &threads); extern bool ocl_generic_cn_generator(const OclDevice &device, const Algorithm &algorithm, OclThreads &threads); @@ -65,6 +77,15 @@ static ocl_gen_config_fun generators[] = { # endif # ifdef XMRIG_ALGO_KAWPOW ocl_generic_kawpow_generator, +# endif +# ifdef XMRIG_ALGO_PROGPOWZ + ocl_generic_progpowz_generator, +# endif +# ifdef XMRIG_ALGO_ETCHASH + ocl_generic_etchash_generator, +# endif +# ifdef XMRIG_ALGO_BLAKE3DCR + ocl_generic_blake3_generator, # endif ocl_vega_cn_generator, ocl_generic_cn_generator diff --git a/miner/core/src/crypto/etchash/ETCCache.cpp b/miner/core/src/crypto/etchash/ETCCache.cpp index 45e18ab..be77f56 100644 --- a/miner/core/src/crypto/etchash/ETCCache.cpp +++ b/miner/core/src/crypto/etchash/ETCCache.cpp @@ -131,4 +131,42 @@ void ETCCache::seedHash(uint32_t epoch, uint8_t (&seed)[32]) } +static inline uint32_t clz(uint32_t x) +{ +#ifdef _MSC_VER + unsigned long index; + _BitScanReverse(&index, x); + return 31 - index; +#else + return __builtin_clz(x); +#endif +} + + +void ETCCache::calculate_fast_mod_data(uint32_t divisor, uint32_t& reciprocal, uint32_t& increment, uint32_t& shift) +{ + if ((divisor & (divisor - 1)) == 0) { + reciprocal = 1; + increment = 0; + shift = 31U - clz(divisor); + } + else { + shift = 63U - clz(divisor); + const uint64_t N = 1ULL << shift; + const uint64_t q = N / divisor; + const uint64_t r = N - q * divisor; + if (r * 2 < divisor) + { + reciprocal = static_cast(q); + increment = 1; + } + else + { + reciprocal = static_cast(q + 1); + increment = 0; + } + } +} + + } // namespace xmrig diff --git a/miner/core/src/crypto/etchash/ETCCache.h b/miner/core/src/crypto/etchash/ETCCache.h index 6355299..c664cf2 100644 --- a/miner/core/src/crypto/etchash/ETCCache.h +++ b/miner/core/src/crypto/etchash/ETCCache.h @@ -59,6 +59,9 @@ public: // Get seed hash for epoch static void seedHash(uint32_t epoch, uint8_t (&seed)[32]); + // Calculate fast modulo data for GPU kernel + static void calculate_fast_mod_data(uint32_t divisor, uint32_t& reciprocal, uint32_t& increment, uint32_t& shift); + // Singleton instances static std::mutex s_cacheMutex; static ETCCache s_etcCache; // For ETC (ETChash) diff --git a/pkg/mining/xmrig_gpu_test.go b/pkg/mining/xmrig_gpu_test.go index 3341ea0..bd4e987 100644 --- a/pkg/mining/xmrig_gpu_test.go +++ b/pkg/mining/xmrig_gpu_test.go @@ -38,12 +38,14 @@ func TestXMRigDualMiningConfig(t *testing.T) { CPUMaxThreadsHint: 50, // GPU config - separate pool and algo + // MUST specify Devices explicitly - no auto-picking! GPUEnabled: true, GPUPool: "stratum+tcp://ravencoin.pool.com:3333", GPUWallet: "gpu_wallet_address", GPUAlgo: "kawpow", - CUDA: true, // NVIDIA + CUDA: true, // NVIDIA OpenCL: false, + Devices: "0", // Explicit device selection required } err := miner.createConfig(config) @@ -139,14 +141,16 @@ func TestXMRigGPUOnlyConfig(t *testing.T) { defer func() { getXMRigConfigPath = origGetPath }() // GPU-only config using same pool for simplicity + // MUST specify Devices explicitly - no auto-picking! config := &Config{ Pool: "stratum+tcp://pool.supportxmr.com:3333", Wallet: "test_wallet", Algo: "rx/0", - NoCPU: true, // Disable CPU + NoCPU: true, // Disable CPU GPUEnabled: true, - OpenCL: true, // AMD GPU - CUDA: true, // Also NVIDIA + OpenCL: true, // AMD GPU + CUDA: true, // Also NVIDIA + Devices: "0,1", // Explicit device selection required } err := miner.createConfig(config)