feat: Add OpenCL GPU support for ProgPowZ, ETChash, and Blake3DCR

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 <noreply@anthropic.com>
This commit is contained in:
snider 2025-12-30 23:01:16 +00:00
parent b1aced8341
commit bec2accf1a
32 changed files with 6883 additions and 13 deletions

View file

@ -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 <cassert>
#include <thread>
@ -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

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
//
// 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;
}
}

View file

@ -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

View file

@ -0,0 +1,279 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow OpenCL implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//
// 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);
}

View file

@ -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

View file

@ -0,0 +1,65 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow OpenCL implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -0,0 +1,310 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow OpenCL implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "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;
}
}

View file

@ -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

View file

@ -0,0 +1,304 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow OpenCL implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "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);
}

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "backend/opencl/OclThreads.h"
#include "backend/opencl/wrappers/OclDevice.h"
#include "base/crypto/Algorithm.h"
#include <algorithm>
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<uint32_t>(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<uint32_t>(1 << 24));
threads.add(OclThread(device.index(), intensity, worksize, 1));
return true;
}
} // namespace xmrig

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "backend/opencl/OclThreads.h"
#include "backend/opencl/wrappers/OclDevice.h"
#include "base/crypto/Algorithm.h"
#include <algorithm>
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<uint32_t>(std::min(available / 128, static_cast<size_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(1 << 21));
}
threads.add(OclThread(device.index(), intensity, worksize, 1));
return true;
}
} // namespace xmrig

View file

@ -0,0 +1,87 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "backend/opencl/OclThreads.h"
#include "backend/opencl/wrappers/OclDevice.h"
#include "base/crypto/Algorithm.h"
#include "crypto/progpowz/ProgPowZHash.h"
#include <algorithm>
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<uint32_t>(std::min(available / 128, static_cast<size_t>(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<uint32_t>(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<uint32_t>(1 << 21));
}
threads.add(OclThread(device.index(), intensity, worksize, 1));
return true;
}
} // namespace xmrig

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 */

View file

@ -0,0 +1,49 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "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);
}

View file

@ -0,0 +1,45 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_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 */

View file

@ -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()

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#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<uint32_t>(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<std::mutex> 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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_OCLETCHASHRUNNER_H
#define XMRIG_OCLETCHASHRUNNER_H
#include "backend/opencl/runners/OclBaseRunner.h"
#include <mutex>
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

View file

@ -0,0 +1,213 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdexcept>
#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<uint32_t>(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<std::mutex> 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

View file

@ -0,0 +1,82 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_OCLPROGPOWZRUNNER_H
#define XMRIG_OCLPROGPOWZRUNNER_H
#include "backend/opencl/runners/OclBaseRunner.h"
#include "crypto/progpowz/ProgPowZCache.h"
#include <mutex>
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

View file

@ -0,0 +1,487 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "backend/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 <cstring>
#include <mutex>
#include <regex>
#include <sstream>
#include <string>
#include <thread>
#include <uv.h>
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<std::mutex> 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<std::mutex> lock(m_mutex);
gc(period);
m_data.emplace_back(algo, period, worksize, index, program, kernel);
}
void clear()
{
std::lock_guard<std::mutex> 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<ProgPowZCacheEntry> 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<uv_work_t>
{
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<std::mutex> 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<uint32_t>(prog_seed);
uint32_t seed1 = static_cast<uint32_t>(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<ProgPowZBaton> m_batons;
static void loop(void* data)
{
ProgPowZBuilder* builder = static_cast<ProgPowZBuilder*>(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<std::mutex> 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<ProgPowZBuilder*>(handle->data);
uv_close(reinterpret_cast<uv_handle_t*>(&builder->m_shutdownAsync), nullptr);
uv_close(reinterpret_cast<uv_handle_t*>(&builder->m_batonAsync), nullptr);
});
uv_async_init(m_loop, &m_batonAsync, [](uv_async_t* handle)
{
std::vector<ProgPowZBaton> batons;
{
ProgPowZBuilder* b = reinterpret_cast<ProgPowZBuilder*>(handle->data);
std::lock_guard<std::mutex> 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

View file

@ -0,0 +1,50 @@
/* Miner
* Copyright (c) 2025 Lethean
*
* Based on XMRig KawPow implementation
* Copyright 2018-2021 SChernykh <https://github.com/SChernykh>
* Copyright 2016-2021 XMRig <https://github.com/xmrig>, <support@xmrig.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XMRIG_OCLPROGPOWZ_H
#define XMRIG_OCLPROGPOWZ_H
#include <cstddef>
#include <cstdint>
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 */

View file

@ -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

View file

@ -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<uint32_t>(q);
increment = 1;
}
else
{
reciprocal = static_cast<uint32_t>(q + 1);
increment = 0;
}
}
}
} // namespace xmrig

View file

@ -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)

View file

@ -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)