diff --git a/README.md b/README.md index 8558c1ef9fc..e0a2e5fdfc3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zcash 1.0.0 +Zcash 1.0.0 (This is not up to date with zcash master) =========== What is Zcash? diff --git a/configure.ac b/configure.ac index 6d6918ec27f..8eef199b10b 100644 --- a/configure.ac +++ b/configure.ac @@ -152,7 +152,7 @@ AC_ARG_ENABLE([glibc-back-compat], AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) -# Enable debug +# Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use debug compiler flags and macros (default is no)])], @@ -163,11 +163,11 @@ if test "x$enable_debug" = xyes; then if test "x$GCC" = xyes; then CFLAGS="-g3 -O0 -DDEBUG" fi - + if test "x$GXX" = xyes; then CXXFLAGS="-g3 -O0 -DDEBUG" fi -fi +fi ## TODO: Remove these hard-coded paths and flags. They are here for the sake of ## compatibility with the legacy buildsystem. @@ -731,6 +731,7 @@ AC_CHECK_HEADER([libsnark/gadgetlib1/gadget.hpp],,AC_MSG_ERROR(libsnark headers AC_CHECK_LIB([snark],[main],LIBSNARK_LIBS=-lsnark, [AC_MSG_ERROR(libsnark missing)], [-lgmpxx]) LIBZCASH_LIBS="-lsnark -lgmp -lgmpxx -lboost_system-mt -lcrypto -lsodium -fopenmp" +LIBGPUSOLVER_LIBS="-lOpenCL" CXXFLAGS_TEMP="$CXXFLAGS" LIBS_TEMP="$LIBS" @@ -917,6 +918,8 @@ AC_SUBST(GMP_LIBS) AC_SUBST(GMPXX_LIBS) AC_SUBST(LIBSNARK_LIBS) AC_SUBST(LIBZCASH_LIBS) +AC_SUBST(LIBGPUSOLVER_LIBS) + AC_CONFIG_FILES([Makefile src/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) diff --git a/src/Makefile.am b/src/Makefile.am index 8ded268cfde..1ae96b16362 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,7 @@ LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la LIBZCASH=libzcash.a +LIBGPUSOLVER=libgpusolver.a $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) @@ -45,7 +46,9 @@ EXTRA_LIBRARIES = \ univalue/libbitcoin_univalue.a \ libbitcoin_server.a \ libbitcoin_cli.a \ - libzcash.a + libzcash.a \ + libgpusolver.a + if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += libbitcoin_wallet.a @@ -80,6 +83,15 @@ LIBZCASH_H = \ zcash/util.h \ zcash/Zcash.h +LIBGPUSOLVER_H = \ + libgpusolver/libgpusolver.h \ + libgpusolver/gpuconfig.h \ + libgpusolver/kernels/silentarmy.h \ + libgpusolver/libclwrapper.h \ + libgpusolver/cl.hpp \ + libgpusolver/blake.h \ + libgpusolver/param.h + .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ @@ -226,7 +238,8 @@ libbitcoin_server_a_SOURCES = \ validationinterface.cpp \ $(JSON_H) \ $(BITCOIN_CORE_H) \ - $(LIBZCASH_H) + $(LIBZCASH_H) \ + $(LIBGPUSOLVER_H) # wallet: shared between bitcoind and bitcoin-qt, but only linked # when wallet enabled @@ -364,7 +377,8 @@ zcashd_LDADD = \ $(LIBZCASH) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ - $(LIBSECP256K1) + $(LIBSECP256K1) \ + $(LIBGPUSOLVER) if ENABLE_WALLET zcashd_LDADD += libbitcoin_wallet.a @@ -378,7 +392,9 @@ zcashd_LDADD += \ $(MINIUPNPC_LIBS) \ $(LIBZCASH) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBGPUSOLVER) \ + $(LIBGPUSOLVER_LIBS) # # bitcoin-cli binary # @@ -448,6 +464,20 @@ libzcash_a_LDFLAGS = $(HARDENED_LDFLAGS) libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT +libgpusolver_a_SOURCES = \ + libgpusolver/libgpusolver.cpp \ + libgpusolver/libclwrapper.cpp \ + libgpusolver/blake.cpp + +libgpusolver_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden + +libgpusolver_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing + +libgpusolver_a_LDFLAGS = $(HARDENED_LDFLAGS) -lOpenCL + +libgpusolver_a_CPPFLAGS += -DMONTGOMERY_OUTPUT + + # zcashconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/zcashconsensus.h diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 8c41ef7833c..ea21342650c 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -36,7 +36,7 @@ if ENABLE_WALLET zcash_gtest_LDADD += $(LIBBITCOIN_WALLET) endif -zcash_gtest_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS) +zcash_gtest_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS) $(LIBGPUSOLVER) $(LIBGPUSOLVER_LIBS) zcash_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static diff --git a/src/Makefile.test.include b/src/Makefile.test.include index e296428054b..ac9f4885a22 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -103,7 +103,7 @@ if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif -test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS) +test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS) $(LIBGPUSOLVER) $(LIBGPUSOLVER_LIBS) $(LIBGPUSOLVER) $(LIBGPUSOLVER_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include index 341ba273621..546dd7944be 100644 --- a/src/Makefile.zcash.include +++ b/src/Makefile.zcash.include @@ -1,5 +1,5 @@ bin_PROGRAMS += \ - zcash/GenerateParams + zcash/GenerateParams # tool for generating our public parameters zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp @@ -9,3 +9,23 @@ zcash_GenerateParams_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) + +zcash_miner_SOURCES = \ + libstratum/StratumClient.cpp \ + libstratum/StratumClient.h \ + libstratum/ZcashStratum.cpp \ + libstratum/ZcashStratum.h \ + standaloneminer.cpp +zcash_miner_CPPFLAGS = $(BITCOIN_INCLUDES) +zcash_miner_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +zcash_miner_LDADD = \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_UTIL) \ + $(LIBSECP256K1) \ + $(BOOST_LIBS) \ + $(CRYPTO_LIBS) \ + $(LIBZCASH) \ + $(LIBZCASH_LIBS) \ + $(LIBGPUSOLVER) \ + $(LIBGPUSOLVER_LIBS) diff --git a/src/init.cpp b/src/init.cpp index 384a82bba82..483976c2b44 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -49,6 +49,7 @@ #include #include "libsnark/common/profiling.hpp" +#include "libgpusolver/gpuconfig.h" using namespace std; @@ -361,7 +362,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - + #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); @@ -385,6 +386,10 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0)); strUsage += HelpMessageOpt("-genproclimit=", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1)); strUsage += HelpMessageOpt("-equihashsolver=", _("Specify the Equihash solver to be used if enabled (default: \"default\")")); + strUsage += HelpMessageOpt("-G", _("Enable GPU mining (default: false)")); + strUsage += HelpMessageOpt("-device=", _("If -G is enabled this specifies the GPU device number to use (default: 0)")); + strUsage += HelpMessageOpt("-allgpu", _("If -G is enabled this will mine on all available GPU devices (default: false)")); + strUsage += HelpMessageOpt("-forcenolimit", _("Do not limit thread count per GPU by memory limits. (default: false)")); #endif strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), 0)); @@ -1014,15 +1019,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) std::string warningString; std::string errorString; - + if (!CWallet::Verify(strWalletFile, warningString, errorString)) return false; - + if (!warningString.empty()) InitWarning(warningString); if (!errorString.empty()) return InitError(warningString); - + } // (!fDisableWallet) #endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization @@ -1492,8 +1497,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifdef ENABLE_WALLET // Generate coins in the background - if (pwalletMain) - GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); + if (pwalletMain){ + GPUConfig conf; + conf.useGPU = GetBoolArg("-G", false) || GetBoolArg("-GPU", false); + conf.selGPU = GetArg("-deviceid", 0); + conf.allGPU = GetBoolArg("-allgpu", 0); + conf.forceGenProcLimit = GetBoolArg("-forcenolimit", false); + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1),conf); + } #endif // ********************************************************* Step 11: finished diff --git a/src/libgpusolver/blake.cpp b/src/libgpusolver/blake.cpp new file mode 100644 index 00000000000..c19c6ebaf3b --- /dev/null +++ b/src/libgpusolver/blake.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include "blake.h" + +static const uint32_t blake2b_block_len = 128; +static const uint32_t blake2b_rounds = 12; +static const uint64_t blake2b_iv[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL, +}; +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, +}; + +/* +** Init the state according to Zcash parameters. +*/ +void zcash_blake2b_init(blake2b_state_t *st, uint8_t hash_len, + uint32_t n, uint32_t k) +{ + assert(n > k); + assert(hash_len <= 64); + st->h[0] = blake2b_iv[0] ^ (0x01010000 | hash_len); + for (uint32_t i = 1; i <= 5; i++) + st->h[i] = blake2b_iv[i]; + st->h[6] = blake2b_iv[6] ^ *(uint64_t *)"ZcashPoW"; + st->h[7] = blake2b_iv[7] ^ (((uint64_t)k << 32) | n); + st->bytes = 0; +} + +static uint64_t rotr64(uint64_t a, uint8_t bits) +{ + return (a >> bits) | (a << (64 - bits)); +} + +static void mix(uint64_t *va, uint64_t *vb, uint64_t *vc, uint64_t *vd, + uint64_t x, uint64_t y) +{ + *va = (*va + *vb + x); + *vd = rotr64(*vd ^ *va, 32); + *vc = (*vc + *vd); + *vb = rotr64(*vb ^ *vc, 24); + *va = (*va + *vb + y); + *vd = rotr64(*vd ^ *va, 16); + *vc = (*vc + *vd); + *vb = rotr64(*vb ^ *vc, 63); +} + +/* +** Process either a full message block or the final partial block. +** Note that v[13] is not XOR'd because st->bytes is assumed to never overflow. +** +** _msg pointer to message (must be zero-padded to 128 bytes if final block) +** msg_len must be 128 (<= 128 allowed only for final partial block) +** is_final indicate if this is the final block +*/ +void zcash_blake2b_update(blake2b_state_t *st, const uint8_t *_msg, + uint32_t msg_len, uint32_t is_final) +{ + const uint64_t *m = (const uint64_t *)_msg; + uint64_t v[16]; + assert(msg_len <= 128); + assert(st->bytes <= UINT64_MAX - msg_len); + memcpy(v + 0, st->h, 8 * sizeof (*v)); + memcpy(v + 8, blake2b_iv, 8 * sizeof (*v)); + v[12] ^= (st->bytes += msg_len); + v[14] ^= is_final ? -1 : 0; + for (uint32_t round = 0; round < blake2b_rounds; round++) + { + const uint8_t *s = blake2b_sigma[round]; + mix(v + 0, v + 4, v + 8, v + 12, m[s[0]], m[s[1]]); + mix(v + 1, v + 5, v + 9, v + 13, m[s[2]], m[s[3]]); + mix(v + 2, v + 6, v + 10, v + 14, m[s[4]], m[s[5]]); + mix(v + 3, v + 7, v + 11, v + 15, m[s[6]], m[s[7]]); + mix(v + 0, v + 5, v + 10, v + 15, m[s[8]], m[s[9]]); + mix(v + 1, v + 6, v + 11, v + 12, m[s[10]], m[s[11]]); + mix(v + 2, v + 7, v + 8, v + 13, m[s[12]], m[s[13]]); + mix(v + 3, v + 4, v + 9, v + 14, m[s[14]], m[s[15]]); + } + for (uint32_t i = 0; i < 8; i++) + st->h[i] ^= v[i] ^ v[i + 8]; +} + +void zcash_blake2b_final(blake2b_state_t *st, uint8_t *out, uint8_t outlen) +{ + assert(outlen <= 64); + memcpy(out, st->h, outlen); +} diff --git a/src/libgpusolver/blake.h b/src/libgpusolver/blake.h new file mode 100644 index 00000000000..40721e8d44e --- /dev/null +++ b/src/libgpusolver/blake.h @@ -0,0 +1,10 @@ +typedef struct blake2b_state_s +{ + uint64_t h[8]; + uint64_t bytes; +} blake2b_state_t; +void zcash_blake2b_init(blake2b_state_t *st, uint8_t hash_len, + uint32_t n, uint32_t k); +void zcash_blake2b_update(blake2b_state_t *st, const uint8_t *_msg, + uint32_t msg_len, uint32_t is_final); +void zcash_blake2b_final(blake2b_state_t *st, uint8_t *out, uint8_t outlen); diff --git a/src/libgpusolver/cl.hpp b/src/libgpusolver/cl.hpp new file mode 100644 index 00000000000..7b62e1ae693 --- /dev/null +++ b/src/libgpusolver/cl.hpp @@ -0,0 +1,4037 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#if __GNUC__ >= 6 + #pragma GCC diagnostic ignored "-Wignored-attributes" +#endif + +/*! \file + * + * \brief C++ bindings for OpenCL 1.0 (rev 48) and OpenCL 1.1 (rev 33) + * \author Benedict R. Gaster and Laurent Morichetti + * + * Additions and fixes from Brian Cole, March 3rd 2010. + * + * \version 1.1 + * \date June 2010 + * + * Optional extension support + * + * cl + * cl_ext_device_fission + * #define USE_CL_DEVICE_FISSION + */ + +/*! \mainpage + * \section intro Introduction + * For many large applications C++ is the language of choice and so it seems + * reasonable to define C++ bindings for OpenCL. + * + * + * The interface is contained with a single C++ header file \em cl.hpp and all + * definitions are contained within the namespace \em cl. There is no additional + * requirement to include \em cl.h and to use either the C++ or original C + * bindings it is enough to simply include \em cl.hpp. + * + * The bindings themselves are lightweight and correspond closely to the + * underlying C API. Using the C++ bindings introduces no additional execution + * overhead. + * + * For detail documentation on the bindings see: + * + * The OpenCL C++ Wrapper API 1.1 (revision 04) + * http://www.khronos.org/registry/cl/specs/opencl-cplusplus-1.1.pdf + * + * \section example Example + * + * The following example shows a general use case for the C++ + * bindings, including support for the optional exception feature and + * also the supplied vector and string classes, see following sections for + * decriptions of these features. + * + * \code + * #define __CL_ENABLE_EXCEPTIONS + * + * #if defined(__APPLE__) || defined(__MACOSX) + * #include + * #else + * #include + * #endif + * #include + * #include + * #include + * + * const char * helloStr = "__kernel void " + * "hello(void) " + * "{ " + * " " + * "} "; + * + * int + * main(void) + * { + * cl_int err = CL_SUCCESS; + * try { + * + * std::vector platforms; + * cl::Platform::get(&platforms); + * if (platforms.size() == 0) { + * std::cout << "Platform size 0\n"; + * return -1; + * } + * + * cl_context_properties properties[] = + * { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; + * cl::Context context(CL_DEVICE_TYPE_CPU, properties); + * + * std::vector devices = context.getInfo(); + * + * cl::Program::Sources source(1, + * std::make_pair(helloStr,strlen(helloStr))); + * cl::Program program_ = cl::Program(context, source); + * program_.build(devices); + * + * cl::Kernel kernel(program_, "hello", &err); + * + * cl::Event event; + * cl::CommandQueue queue(context, devices[0], 0, &err); + * queue.enqueueNDRangeKernel( + * kernel, + * cl::NullRange, + * cl::NDRange(4,4), + * cl::NullRange, + * NULL, + * &event); + * + * event.wait(); + * } + * catch (cl::Error err) { + * std::cerr + * << "ERROR: " + * << err.what() + * << "(" + * << err.err() + * << ")" + * << std::endl; + * } + * + * return EXIT_SUCCESS; + * } + * + * \endcode + * + */ +#ifndef CL_HPP_ +#define CL_HPP_ + +#ifdef _WIN32 +#include +#include +#if defined(USE_DX_INTEROP) +#include +#endif +#endif // _WIN32 + +// +#if defined(USE_CL_DEVICE_FISSION) +#include +#endif + +#if defined(__APPLE__) || defined(__MACOSX) +#include +#include +#else +#include +#include +#endif // !__APPLE__ + +#if !defined(CL_CALLBACK) +#define CL_CALLBACK +#endif //CL_CALLBACK + +#include + +#if !defined(__NO_STD_VECTOR) +#include +#endif + +#if !defined(__NO_STD_STRING) +#include +#endif + +#if defined(linux) || defined(__APPLE__) || defined(__MACOSX) +# include +#endif // linux + +#include + +/*! \namespace cl + * + * \brief The OpenCL C++ bindings are defined within this namespace. + * + */ +namespace cl { + +#define __INIT_CL_EXT_FCN_PTR(name) \ + if(!pfn_##name) { \ + pfn_##name = (PFN_##name) \ + clGetExtensionFunctionAddress(#name); \ + if(!pfn_##name) { \ + } \ + } + +class Program; +class Device; +class Context; +class CommandQueue; +class Memory; + +#if defined(__CL_ENABLE_EXCEPTIONS) +#include +/*! \class Error + * \brief Exception class + */ +class Error : public std::exception +{ +private: + cl_int err_; + const char * errStr_; +public: + /*! Create a new CL error exception for a given error code + * and corresponding message. + */ + Error(cl_int err, const char * errStr = NULL) : err_(err), errStr_(errStr) + {} + + ~Error() throw() {} + + /*! \brief Get error string associated with exception + * + * \return A memory pointer to the error message string. + */ + virtual const char * what() const throw () + { + if (errStr_ == NULL) { + return "empty"; + } + else { + return errStr_; + } + } + + /*! \brief Get error code associated with exception + * + * \return The error code. + */ + cl_int err(void) const { return err_; } +}; + +#define __ERR_STR(x) #x +#else +#define __ERR_STR(x) NULL +#endif // __CL_ENABLE_EXCEPTIONS + +//! \cond DOXYGEN_DETAIL +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#define __GET_DEVICE_INFO_ERR __ERR_STR(clgetDeviceInfo) +#define __GET_PLATFORM_INFO_ERR __ERR_STR(clGetPlatformInfo) +#define __GET_DEVICE_IDS_ERR __ERR_STR(clGetDeviceIDs) +#define __GET_PLATFORM_IDS_ERR __ERR_STR(clGetPlatformIDs) +#define __GET_CONTEXT_INFO_ERR __ERR_STR(clGetContextInfo) +#define __GET_EVENT_INFO_ERR __ERR_STR(clGetEventInfo) +#define __GET_EVENT_PROFILE_INFO_ERR __ERR_STR(clGetEventProfileInfo) +#define __GET_MEM_OBJECT_INFO_ERR __ERR_STR(clGetMemObjectInfo) +#define __GET_IMAGE_INFO_ERR __ERR_STR(clGetImageInfo) +#define __GET_SAMPLER_INFO_ERR __ERR_STR(clGetSamplerInfo) +#define __GET_KERNEL_INFO_ERR __ERR_STR(clGetKernelInfo) +#define __GET_KERNEL_WORK_GROUP_INFO_ERR __ERR_STR(clGetKernelWorkGroupInfo) +#define __GET_PROGRAM_INFO_ERR __ERR_STR(clGetProgramInfo) +#define __GET_PROGRAM_BUILD_INFO_ERR __ERR_STR(clGetProgramBuildInfo) +#define __GET_COMMAND_QUEUE_INFO_ERR __ERR_STR(clGetCommandQueueInfo) + +#define __CREATE_CONTEXT_FROM_TYPE_ERR __ERR_STR(clCreateContextFromType) +#define __GET_SUPPORTED_IMAGE_FORMATS_ERR __ERR_STR(clGetSupportedImageFormats) + +#define __CREATE_BUFFER_ERR __ERR_STR(clCreateBuffer) +#define __CREATE_SUBBUFFER_ERR __ERR_STR(clCreateSubBuffer) +#define __CREATE_GL_BUFFER_ERR __ERR_STR(clCreateFromGLBuffer) +#define __GET_GL_OBJECT_INFO_ERR __ERR_STR(clGetGLObjectInfo) +#define __CREATE_IMAGE2D_ERR __ERR_STR(clCreateImage2D) +#define __CREATE_IMAGE3D_ERR __ERR_STR(clCreateImage3D) +#define __CREATE_SAMPLER_ERR __ERR_STR(clCreateSampler) +#define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR __ERR_STR(clSetMemObjectDestructorCallback) + +#define __CREATE_USER_EVENT_ERR __ERR_STR(clCreateUserEvent) +#define __SET_USER_EVENT_STATUS_ERR __ERR_STR(clSetUserEventStatus) +#define __SET_EVENT_CALLBACK_ERR __ERR_STR(clSetEventCallback) +#define __WAIT_FOR_EVENTS_ERR __ERR_STR(clWaitForEvents) + +#define __CREATE_KERNEL_ERR __ERR_STR(clCreateKernel) +#define __SET_KERNEL_ARGS_ERR __ERR_STR(clSetKernelArg) +#define __CREATE_PROGRAM_WITH_SOURCE_ERR __ERR_STR(clCreateProgramWithSource) +#define __CREATE_PROGRAM_WITH_BINARY_ERR __ERR_STR(clCreateProgramWithBinary) +#define __BUILD_PROGRAM_ERR __ERR_STR(clBuildProgram) +#define __CREATE_KERNELS_IN_PROGRAM_ERR __ERR_STR(clCreateKernelsInProgram) + +#define __CREATE_COMMAND_QUEUE_ERR __ERR_STR(clCreateCommandQueue) +#define __SET_COMMAND_QUEUE_PROPERTY_ERR __ERR_STR(clSetCommandQueueProperty) +#define __ENQUEUE_READ_BUFFER_ERR __ERR_STR(clEnqueueReadBuffer) +#define __ENQUEUE_READ_BUFFER_RECT_ERR __ERR_STR(clEnqueueReadBufferRect) +#define __ENQUEUE_WRITE_BUFFER_ERR __ERR_STR(clEnqueueWriteBuffer) +#define __ENQUEUE_FILL_BUFFER_ERR __ERR_STR(clEnqueueFillBuffer) +#define __ENQUEUE_WRITE_BUFFER_RECT_ERR __ERR_STR(clEnqueueWriteBufferRect) +#define __ENQEUE_COPY_BUFFER_ERR __ERR_STR(clEnqueueCopyBuffer) +#define __ENQEUE_COPY_BUFFER_RECT_ERR __ERR_STR(clEnqueueCopyBufferRect) +#define __ENQUEUE_READ_IMAGE_ERR __ERR_STR(clEnqueueReadImage) +#define __ENQUEUE_WRITE_IMAGE_ERR __ERR_STR(clEnqueueWriteImage) +#define __ENQUEUE_COPY_IMAGE_ERR __ERR_STR(clEnqueueCopyImage) +#define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR __ERR_STR(clEnqueueCopyImageToBuffer) +#define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR __ERR_STR(clEnqueueCopyBufferToImage) +#define __ENQUEUE_MAP_BUFFER_ERR __ERR_STR(clEnqueueMapBuffer) +#define __ENQUEUE_MAP_IMAGE_ERR __ERR_STR(clEnqueueMapImage) +#define __ENQUEUE_UNMAP_MEM_OBJECT_ERR __ERR_STR(clEnqueueUnMapMemObject) +#define __ENQUEUE_NDRANGE_KERNEL_ERR __ERR_STR(clEnqueueNDRangeKernel) +#define __ENQUEUE_TASK_ERR __ERR_STR(clEnqueueTask) +#define __ENQUEUE_NATIVE_KERNEL __ERR_STR(clEnqueueNativeKernel) +#define __ENQUEUE_MARKER_ERR __ERR_STR(clEnqueueMarker) +#define __ENQUEUE_WAIT_FOR_EVENTS_ERR __ERR_STR(clEnqueueWaitForEvents) +#define __ENQUEUE_BARRIER_ERR __ERR_STR(clEnqueueBarrier) + +#define __ENQUEUE_ACQUIRE_GL_ERR __ERR_STR(clEnqueueAcquireGLObjects) +#define __ENQUEUE_RELEASE_GL_ERR __ERR_STR(clEnqueueReleaseGLObjects) + +#define __UNLOAD_COMPILER_ERR __ERR_STR(clUnloadCompiler) + +#define __FLUSH_ERR __ERR_STR(clFlush) +#define __FINISH_ERR __ERR_STR(clFinish) + +#define __CREATE_SUB_DEVICES __ERR_STR(clCreateSubDevicesEXT) +#endif // __CL_USER_OVERRIDE_ERROR_STRINGS +//! \endcond + +/*! \class string + * \brief Simple string class, that provides a limited subset of std::string + * functionality but avoids many of the issues that come with that class. + */ +class string +{ +private: + ::size_t size_; + char * str_; +public: + string(void) : size_(0), str_(NULL) + { + } + + string(char * str, ::size_t size) : + size_(size), + str_(NULL) + { + str_ = new char[size_+1]; + if (str_ != NULL) { + memcpy(str_, str, size_ * sizeof(char)); + str_[size_] = '\0'; + } + else { + size_ = 0; + } + } + + string(char * str) : + str_(NULL) + { + size_= ::strlen(str); + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, str, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + string& operator=(const string& rhs) + { + if (this == &rhs) { + return *this; + } + + if (rhs.size_ == 0 || rhs.str_ == NULL) { + size_ = 0; + str_ = NULL; + } + else { + size_ = rhs.size_; + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, rhs.str_, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + return *this; + } + + string(const string& rhs) + { + *this = rhs; + } + + ~string() + { + if (str_ != NULL) { + delete[] str_; + } + } + + ::size_t size(void) const { return size_; } + ::size_t length(void) const { return size(); } + + const char * c_str(void) const { return (str_) ? str_ : "";} +}; + +#if !defined(__USE_DEV_STRING) && !defined(__NO_STD_STRING) +#include +typedef std::string STRING_CLASS; +#elif !defined(__USE_DEV_STRING) +typedef cl::string STRING_CLASS; +#endif + +#if !defined(__USE_DEV_VECTOR) && !defined(__NO_STD_VECTOR) +#include +#define VECTOR_CLASS std::vector +#elif !defined(__USE_DEV_VECTOR) +#define VECTOR_CLASS cl::vector +#endif + +#if !defined(__MAX_DEFAULT_VECTOR_SIZE) +#define __MAX_DEFAULT_VECTOR_SIZE 10 +#endif + +/*! \class vector + * \brief Fixed sized vector implementation that mirroring + * std::vector functionality. + */ +template +class vector +{ +private: + T data_[N]; + unsigned int size_; + bool empty_; +public: + vector() : + size_(-1), + empty_(true) + {} + + ~vector() {} + + unsigned int size(void) const + { + return size_ + 1; + } + + void clear() + { + size_ = -1; + empty_ = true; + } + + void push_back (const T& x) + { + if (size() < N) { + size_++; + data_[size_] = x; + empty_ = false; + } + } + + void pop_back(void) + { + if (!empty_) { + data_[size_].~T(); + size_--; + if (size_ == -1) { + empty_ = true; + } + } + } + + vector(const vector& vec) : + size_(vec.size_), + empty_(vec.empty_) + { + if (!empty_) { + memcpy(&data_[0], &vec.data_[0], size() * sizeof(T)); + } + } + + vector(unsigned int size, const T& val = T()) : + size_(-1), + empty_(true) + { + for (unsigned int i = 0; i < size; i++) { + push_back(val); + } + } + + vector& operator=(const vector& rhs) + { + if (this == &rhs) { + return *this; + } + + size_ = rhs.size_; + empty_ = rhs.empty_; + + if (!empty_) { + memcpy(&data_[0], &rhs.data_[0], size() * sizeof(T)); + } + + return *this; + } + + bool operator==(vector &vec) + { + if (empty_ && vec.empty_) { + return true; + } + + if (size() != vec.size()) { + return false; + } + + return memcmp(&data_[0], &vec.data_[0], size() * sizeof(T)) == 0 ? true : false; + } + + operator T* () { return data_; } + operator const T* () const { return data_; } + + bool empty (void) const + { + return empty_; + } + + unsigned int max_size (void) const + { + return N; + } + + unsigned int capacity () const + { + return sizeof(T) * N; + } + + T& operator[](int index) + { + return data_[index]; + } + + T operator[](int index) const + { + return data_[index]; + } + + template + void assign(I start, I end) + { + clear(); + while(start < end) { + push_back(*start); + start++; + } + } + + /*! \class iterator + * \brief Iterator class for vectors + */ + class iterator + { + private: + vector vec_; + int index_; + bool initialized_; + public: + iterator(void) : + index_(-1), + initialized_(false) + { + index_ = -1; + initialized_ = false; + } + + ~iterator(void) {} + + static iterator begin(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = 0; + } + + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + static iterator end(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = vec.size(); + } + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + bool operator==(iterator i) + { + return ((vec_ == i.vec_) && + (index_ == i.index_) && + (initialized_ == i.initialized_)); + } + + bool operator!=(iterator i) + { + return (!(*this==i)); + } + + void operator++() + { + index_++; + } + + void operator++(int x) + { + index_ += x; + } + + void operator--() + { + index_--; + } + + void operator--(int x) + { + index_ -= x; + } + + T operator *() + { + return vec_[index_]; + } + }; + + iterator begin(void) + { + return iterator::begin(*this); + } + + iterator end(void) + { + return iterator::end(*this); + } + + T& front(void) + { + return data_[0]; + } + + T& back(void) + { + return data_[size_]; + } + + const T& front(void) const + { + return data_[0]; + } + + const T& back(void) const + { + return data_[size_]; + } +}; + +/*! + * \brief size_t class used to interface between C++ and + * OpenCL C calls that require arrays of size_t values, who's + * size is known statically. + */ +template +struct size_t : public cl::vector< ::size_t, N> { }; + +namespace detail { + +// GetInfo help struct +template +struct GetInfoHelper +{ + static cl_int + get(Functor f, cl_uint name, T* param) + { + return f(name, sizeof(T), param, NULL); + } +}; + +// Specialized GetInfoHelper for VECTOR_CLASS params +template +struct GetInfoHelper > +{ + static cl_int get(Func f, cl_uint name, VECTOR_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + T* value = (T*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + param->assign(&value[0], &value[required/sizeof(T)]); + return CL_SUCCESS; + } +}; + +// Specialized for getInfo +template +struct GetInfoHelper > +{ + static cl_int + get(Func f, cl_uint name, VECTOR_CLASS* param) + { + cl_uint err = f(name, param->size() * sizeof(char *), &(*param)[0], NULL); + if (err != CL_SUCCESS) { + return err; + } + + return CL_SUCCESS; + } +}; + +// Specialized GetInfoHelper for STRING_CLASS params +template +struct GetInfoHelper +{ + static cl_int get(Func f, cl_uint name, STRING_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + char* value = (char*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + *param = value; + return CL_SUCCESS; + } +}; + +#define __GET_INFO_HELPER_WITH_RETAIN(CPP_TYPE) \ +namespace detail { \ +template \ +struct GetInfoHelper \ +{ \ + static cl_int get(Func f, cl_uint name, CPP_TYPE* param) \ + { \ + cl_uint err = f(name, sizeof(CPP_TYPE), param, NULL); \ + if (err != CL_SUCCESS) { \ + return err; \ + } \ + \ + return ReferenceHandler::retain((*param)()); \ + } \ +}; \ +} + + +#define __PARAM_NAME_INFO_1_0(F) \ + F(cl_platform_info, CL_PLATFORM_PROFILE, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VERSION, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_NAME, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VENDOR, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ + F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ + F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_bitfield) \ + F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ + F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ + F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint)\ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ + F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, ::size_t) \ + F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ + F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) \ + F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ + F(cl_device_info, CL_DEVICE_NAME, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VENDOR, STRING_CLASS) \ + F(cl_device_info, CL_DRIVER_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_PROFILE, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ + F(cl_context_info, CL_CONTEXT_DEVICES, VECTOR_CLASS) \ + F(cl_context_info, CL_CONTEXT_PROPERTIES, VECTOR_CLASS) \ + \ + F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ + F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ + F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ + F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_uint) \ + \ + F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ + \ + F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ + F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ + F(cl_mem_info, CL_MEM_SIZE, ::size_t) \ + F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ + F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ + \ + F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ + F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, ::size_t) \ + F(cl_image_info, CL_IMAGE_ROW_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_SLICE_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_WIDTH, ::size_t) \ + F(cl_image_info, CL_IMAGE_HEIGHT, ::size_t) \ + F(cl_image_info, CL_IMAGE_DEPTH, ::size_t) \ + \ + F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ + F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ + F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_addressing_mode) \ + F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_filter_mode) \ + F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_bool) \ + \ + F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ + F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ + F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ + F(cl_program_info, CL_PROGRAM_DEVICES, VECTOR_CLASS) \ + F(cl_program_info, CL_PROGRAM_SOURCE, STRING_CLASS) \ + F(cl_program_info, CL_PROGRAM_BINARY_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_program_info, CL_PROGRAM_BINARIES, VECTOR_CLASS) \ + \ + F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, STRING_CLASS) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, STRING_CLASS) \ + \ + F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, STRING_CLASS) \ + F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ + F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::size_t<3>) \ + F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ + \ + F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ + F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ + F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ + F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) + +#if defined(CL_VERSION_1_1) +#define __PARAM_NAME_INFO_1_1(F) \ + F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint)\ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) \ + \ + F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ + F(cl_mem_info, CL_MEM_OFFSET, ::size_t) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ + \ + F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +#define __PARAM_NAME_DEVICE_FISSION(F) \ + F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl_device_id) \ + F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT , cl_uint) \ + F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, VECTOR_CLASS) +#endif // USE_CL_DEVICE_FISSION + +template +struct param_traits {}; + +#define __DECLARE_PARAM_TRAITS(token, param_name, T) \ +struct token; \ +template<> \ +struct param_traits \ +{ \ + enum { value = param_name }; \ + typedef T param_type; \ +}; + +__PARAM_NAME_INFO_1_0(__DECLARE_PARAM_TRAITS) +#if defined(CL_VERSION_1_1) +__PARAM_NAME_INFO_1_1(__DECLARE_PARAM_TRAITS) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +__PARAM_NAME_DEVICE_FISSION(__DECLARE_PARAM_TRAITS); +#endif // USE_CL_DEVICE_FISSION + +#undef __DECLARE_PARAM_TRAITS + +// Convenience functions + +template +inline cl_int +getInfo(Func f, cl_uint name, T* param) +{ + return GetInfoHelper::get(f, name, param); +} + +template +struct GetInfoFunctor0 +{ + Func f_; const Arg0& arg0_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, param, size, value, size_ret); } +}; + +template +struct GetInfoFunctor1 +{ + Func f_; const Arg0& arg0_; const Arg1& arg1_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, arg1_, param, size, value, size_ret); } +}; + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) +{ + GetInfoFunctor0 f0 = { f, arg0 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) +{ + GetInfoFunctor1 f0 = { f, arg0, arg1 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +struct ReferenceHandler +{ }; + +template <> +struct ReferenceHandler +{ + // cl_device_id does not have retain(). + static cl_int retain(cl_device_id) + { return CL_INVALID_DEVICE; } + // cl_device_id does not have release(). + static cl_int release(cl_device_id) + { return CL_INVALID_DEVICE; } +}; + +template <> +struct ReferenceHandler +{ + // cl_platform_id does not have retain(). + static cl_int retain(cl_platform_id) + { return CL_INVALID_PLATFORM; } + // cl_platform_id does not have release(). + static cl_int release(cl_platform_id) + { return CL_INVALID_PLATFORM; } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_context context) + { return ::clRetainContext(context); } + static cl_int release(cl_context context) + { return ::clReleaseContext(context); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_command_queue queue) + { return ::clRetainCommandQueue(queue); } + static cl_int release(cl_command_queue queue) + { return ::clReleaseCommandQueue(queue); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_mem memory) + { return ::clRetainMemObject(memory); } + static cl_int release(cl_mem memory) + { return ::clReleaseMemObject(memory); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_sampler sampler) + { return ::clRetainSampler(sampler); } + static cl_int release(cl_sampler sampler) + { return ::clReleaseSampler(sampler); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_program program) + { return ::clRetainProgram(program); } + static cl_int release(cl_program program) + { return ::clReleaseProgram(program); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_kernel kernel) + { return ::clRetainKernel(kernel); } + static cl_int release(cl_kernel kernel) + { return ::clReleaseKernel(kernel); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_event event) + { return ::clRetainEvent(event); } + static cl_int release(cl_event event) + { return ::clReleaseEvent(event); } +}; + +template +class Wrapper +{ +public: + typedef T cl_type; + +protected: + cl_type object_; + +public: + Wrapper() : object_(NULL) { } + + ~Wrapper() + { + if (object_ != NULL) { release(); } + } + + Wrapper(const Wrapper& rhs) + { + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + } + + Wrapper& operator = (const Wrapper& rhs) + { + if (object_ != NULL) { release(); } + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + return *this; + } + + cl_type operator ()() const { return object_; } + + cl_type& operator ()() { return object_; } + +protected: + + cl_int retain() const + { + return ReferenceHandler::retain(object_); + } + + cl_int release() const + { + return ReferenceHandler::release(object_); + } +}; + +#if defined(__CL_ENABLE_EXCEPTIONS) +static inline cl_int errHandler ( + cl_int err, + const char * errStr = NULL) throw(Error) +{ + if (err != CL_SUCCESS) { + throw Error(err, errStr); + } + return err; +} +#else +static inline cl_int errHandler (cl_int err, const char * errStr = NULL) +{ + return err; +} +#endif // __CL_ENABLE_EXCEPTIONS + +} // namespace detail +//! \endcond + +/*! \stuct ImageFormat + * \brief ImageFormat interface fro cl_image_format. + */ +struct ImageFormat : public cl_image_format +{ + ImageFormat(){} + + ImageFormat(cl_channel_order order, cl_channel_type type) + { + image_channel_order = order; + image_channel_data_type = type; + } + + ImageFormat& operator = (const ImageFormat& rhs) + { + if (this != &rhs) { + this->image_channel_data_type = rhs.image_channel_data_type; + this->image_channel_order = rhs.image_channel_order; + } + return *this; + } +}; + +/*! \class Device + * \brief Device interface for cl_device_id. + */ +class Device : public detail::Wrapper +{ +public: + Device(cl_device_id device) { object_ = device; } + + Device() : detail::Wrapper() { } + + Device(const Device& device) : detail::Wrapper(device) { } + + Device& operator = (const Device& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_device_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetDeviceInfo, object_, name, param), + __GET_DEVICE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_device_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(USE_CL_DEVICE_FISSION) + cl_int createSubDevices( + const cl_device_partition_property_ext * properties, + VECTOR_CLASS* devices) + { + typedef CL_API_ENTRY cl_int + ( CL_API_CALL * PFN_clCreateSubDevicesEXT)( + cl_device_id /*in_device*/, + const cl_device_partition_property_ext * /* properties */, + cl_uint /*num_entries*/, + cl_device_id * /*out_devices*/, + cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateSubDevicesEXT); + + cl_uint n = 0; + cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif +}; + +/*! \class Platform + * \brief Platform interface. + */ +class Platform : public detail::Wrapper +{ +public: + static const Platform null(); + + Platform(cl_platform_id platform) { object_ = platform; } + + Platform() : detail::Wrapper() { } + + Platform(const Platform& platform) : detail::Wrapper(platform) { } + + Platform& operator = (const Platform& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int getInfo(cl_platform_info name, STRING_CLASS* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetPlatformInfo, object_, name, param), + __GET_PLATFORM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_platform_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getDevices( + cl_device_type type, + VECTOR_CLASS* devices) const + { + cl_uint n = 0; + cl_int err = ::clGetDeviceIDs(object_, type, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = ::clGetDeviceIDs(object_, type, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } + +#if defined(USE_DX_INTEROP) + /*! \brief Get the list of available D3D10 devices. + * + * \param d3d_device_source. + * + * \param d3d_object. + * + * \param d3d_device_set. + * + * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device + * values returned in devices can be used to identify a specific OpenCL + * device. If \a devices argument is NULL, this argument is ignored. + * + * \return One of the following values: + * - CL_SUCCESS if the function is executed successfully. + * + * The application can query specific capabilities of the OpenCL device(s) + * returned by cl::getDevices. This can be used by the application to + * determine which device(s) to use. + * + * \note In the case that exceptions are enabled and a return value + * other than CL_SUCCESS is generated, then cl::Error exception is + * generated. + */ + cl_int getDevices( + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + VECTOR_CLASS* devices) const + { + typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clGetDeviceIDsFromD3D10KHR)( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id * devices, + cl_uint* num_devices); + + static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = NULL; + __INIT_CL_EXT_FCN_PTR(clGetDeviceIDsFromD3D10KHR); + + cl_uint n = 0; + cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + 0, + NULL, + &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + n, + ids, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif + + static cl_int get( + VECTOR_CLASS* platforms) + { + cl_uint n = 0; + cl_int err = ::clGetPlatformIDs(0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + cl_platform_id* ids = (cl_platform_id*) alloca( + n * sizeof(cl_platform_id)); + err = ::clGetPlatformIDs(n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + platforms->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +}; + +static inline cl_int +UnloadCompiler() +{ + return ::clUnloadCompiler(); +} + +class Context : public detail::Wrapper +{ +public: + Context( + const VECTOR_CLASS& devices, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContext( + properties, (cl_uint) devices.size(), + (cl_device_id*) &devices.front(), + notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context( + cl_device_type type, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContextFromType( + properties, type, notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context() : detail::Wrapper() { } + + Context(const Context& context) : detail::Wrapper(context) { } + + Context& operator = (const Context& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_context_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetContextInfo, object_, name, param), + __GET_CONTEXT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_context_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getSupportedImageFormats( + cl_mem_flags flags, + cl_mem_object_type type, + VECTOR_CLASS* formats) const + { + cl_uint numEntries; + cl_int err = ::clGetSupportedImageFormats( + object_, + flags, + type, + 0, + NULL, + &numEntries); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + ImageFormat* value = (ImageFormat*) + alloca(numEntries * sizeof(ImageFormat)); + err = ::clGetSupportedImageFormats( + object_, + flags, + type, + numEntries, + (cl_image_format*) value, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + formats->assign(&value[0], &value[numEntries]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Context) + +/*! \class Event + * \brief Event interface for cl_event. + */ +class Event : public detail::Wrapper +{ +public: + Event() : detail::Wrapper() { } + + Event(const Event& event) : detail::Wrapper(event) { } + + Event& operator = (const Event& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_event_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetEventInfo, object_, name, param), + __GET_EVENT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_event_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getProfilingInfo(cl_profiling_info name, T* param) const + { + return detail::errHandler(detail::getInfo( + &::clGetEventProfilingInfo, object_, name, param), + __GET_EVENT_PROFILE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getProfilingInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_profiling_info, name>::param_type param; + cl_int result = getProfilingInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int wait() const + { + return detail::errHandler( + ::clWaitForEvents(1, &object_), + __WAIT_FOR_EVENTS_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int setCallback( + cl_int type, + void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetEventCallback( + object_, + type, + pfn_notify, + user_data), + __SET_EVENT_CALLBACK_ERR); + } +#endif + + static cl_int + waitForEvents(const VECTOR_CLASS& events) + { + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Event) + +#if defined(CL_VERSION_1_1) +/*! \class UserEvent + * \brief User event interface for cl_event. + */ +class UserEvent : public Event +{ +public: + UserEvent( + const Context& context, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateUserEvent( + context(), + &error); + + detail::errHandler(error, __CREATE_USER_EVENT_ERR); + if (err != NULL) { + *err = error; + } + } + + UserEvent() : Event() { } + + UserEvent(const UserEvent& event) : Event(event) { } + + UserEvent& operator = (const UserEvent& rhs) + { + if (this != &rhs) { + Event::operator=(rhs); + } + return *this; + } + + cl_int setStatus(cl_int status) + { + return detail::errHandler( + ::clSetUserEventStatus(object_,status), + __SET_USER_EVENT_STATUS_ERR); + } +}; +#endif + +inline static cl_int +WaitForEvents(const VECTOR_CLASS& events) +{ + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); +} + +/*! \class Memory + * \brief Memory interface for cl_mem. + */ +class Memory : public detail::Wrapper +{ +public: + Memory() : detail::Wrapper() { } + + Memory(const Memory& memory) : detail::Wrapper(memory) { } + + Memory& operator = (const Memory& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_mem_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetMemObjectInfo, object_, name, param), + __GET_MEM_OBJECT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_mem_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(CL_VERSION_1_1) + cl_int setDestructorCallback( + void (CL_CALLBACK * pfn_notify)(cl_mem, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetMemObjectDestructorCallback( + object_, + pfn_notify, + user_data), + __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); + } +#endif + +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Memory) + +/*! \class Buffer + * \brief Memory buffer interface. + */ +class Buffer : public Memory +{ +public: + Buffer( + const Context& context, + cl_mem_flags flags, + ::size_t size, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); + + detail::errHandler(error, __CREATE_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Buffer() : Memory() { } + + Buffer(const Buffer& buffer) : Memory(buffer) { } + + Buffer& operator = (const Buffer& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } + +#if defined(CL_VERSION_1_1) + Buffer createSubBuffer( + cl_mem_flags flags, + cl_buffer_create_type buffer_create_type, + const void * buffer_create_info, + cl_int * err = NULL) + { + Buffer result; + cl_int error; + result.object_ = ::clCreateSubBuffer( + object_, + flags, + buffer_create_type, + buffer_create_info, + &error); + + detail::errHandler(error, __CREATE_SUBBUFFER_ERR); + if (err != NULL) { + *err = error; + } + + return result; + } +#endif +}; + +#if defined (USE_DX_INTEROP) +class BufferD3D10 : public Buffer +{ +public: + typedef CL_API_ENTRY cl_mem (CL_API_CALL *PFN_clCreateFromD3D10BufferKHR)( + cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, + cl_int* errcode_ret); + + BufferD3D10( + const Context& context, + cl_mem_flags flags, + ID3D10Buffer* bufobj, + cl_int * err = NULL) + { + static PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateFromD3D10BufferKHR); + + cl_int error; + object_ = pfn_clCreateFromD3D10BufferKHR( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferD3D10() : Buffer() { } + + BufferD3D10(const BufferD3D10& buffer) : Buffer(buffer) { } + + BufferD3D10& operator = (const BufferD3D10& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } +}; +#endif + +/*! \class BufferGL + * \brief Memory buffer interface for GL interop. + */ +class BufferGL : public Buffer +{ +public: + BufferGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLBuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferGL() : Buffer() { } + + BufferGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferGL& operator = (const BufferGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class BufferRenderGL + * \brief Memory buffer interface for GL interop with renderbuffer. + */ +class BufferRenderGL : public Buffer +{ +public: + BufferRenderGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLRenderbuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferRenderGL() : Buffer() { } + + BufferRenderGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferRenderGL& operator = (const BufferRenderGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class Image + * \brief Base class interface for all images. + */ +class Image : public Memory +{ +protected: + Image() : Memory() { } + + Image(const Image& image) : Memory(image) { } + + Image& operator = (const Image& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } +public: + template + cl_int getImageInfo(cl_image_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetImageInfo, object_, name, param), + __GET_IMAGE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getImageInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_image_info, name>::param_type param; + cl_int result = getImageInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +/*! \class Image2D + * \brief Image interface for 2D images. + */ +class Image2D : public Image +{ +public: + Image2D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t row_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage2D( + context(), flags,&format, width, height, row_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE2D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2D() { } + + Image2D(const Image2D& image2D) : Image(image2D) { } + + Image2D& operator = (const Image2D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image2DGL : public Image2D +{ +public: + Image2DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture2D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2DGL() : Image2D() { } + + Image2DGL(const Image2DGL& image) : Image2D(image) { } + + Image2DGL& operator = (const Image2DGL& rhs) + { + if (this != &rhs) { + Image2D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image3D + * \brief Image interface for 3D images. + */ +class Image3D : public Image +{ +public: + Image3D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t depth, + ::size_t row_pitch = 0, + ::size_t slice_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage3D( + context(), flags, &format, width, height, depth, row_pitch, + slice_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE3D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3D() { } + + Image3D(const Image3D& image3D) : Image(image3D) { } + + Image3D& operator = (const Image3D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image3DGL : public Image3D +{ +public: + Image3DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture3D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3DGL() : Image3D() { } + + Image3DGL(const Image3DGL& image) : Image3D(image) { } + + Image3DGL& operator = (const Image3DGL& rhs) + { + if (this != &rhs) { + Image3D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Sampler + * \brief Sampler interface for cl_sampler. + */ +class Sampler : public detail::Wrapper +{ +public: + Sampler() { } + + Sampler( + const Context& context, + cl_bool normalized_coords, + cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateSampler( + context(), + normalized_coords, + addressing_mode, + filter_mode, + &error); + + detail::errHandler(error, __CREATE_SAMPLER_ERR); + if (err != NULL) { + *err = error; + } + } + + Sampler(const Sampler& sampler) : detail::Wrapper(sampler) { } + + Sampler& operator = (const Sampler& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_sampler_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetSamplerInfo, object_, name, param), + __GET_SAMPLER_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_sampler_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Sampler) + +class Program; +class CommandQueue; +class Kernel; + +/*! \class NDRange + * \brief NDRange interface + */ +class NDRange +{ +private: + size_t<3> sizes_; + cl_uint dimensions_; + +public: + NDRange() + : dimensions_(0) + { } + + NDRange(::size_t size0) + : dimensions_(1) + { + sizes_.push_back(size0); + } + + NDRange(::size_t size0, ::size_t size1) + : dimensions_(2) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + } + + NDRange(::size_t size0, ::size_t size1, ::size_t size2) + : dimensions_(3) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + sizes_.push_back(size2); + } + + operator const ::size_t*() const { return (const ::size_t*) sizes_; } + ::size_t dimensions() const { return dimensions_; } +}; + +static const NDRange NullRange; + +/*! + * \struct LocalSpaceArg + * \brief Local address raper for use with Kernel::setArg + */ +struct LocalSpaceArg +{ + ::size_t size_; +}; + +namespace detail { + +template +struct KernelArgumentHandler +{ + static ::size_t size(const T&) { return sizeof(T); } + static T* ptr(T& value) { return &value; } +}; + +template <> +struct KernelArgumentHandler +{ + static ::size_t size(const LocalSpaceArg& value) { return value.size_; } + static void* ptr(LocalSpaceArg&) { return NULL; } +}; + +} +//! \endcond + +inline LocalSpaceArg +__local(::size_t size) +{ + LocalSpaceArg ret = { size }; + return ret; +} + +class KernelFunctor; + +/*! \class Kernel + * \brief Kernel interface that implements cl_kernel + */ +class Kernel : public detail::Wrapper +{ +public: + inline Kernel(const Program& program, const char* name, cl_int* err = NULL); + + Kernel() { } + + Kernel(const Kernel& kernel) : detail::Wrapper(kernel) { } + + Kernel& operator = (const Kernel& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_kernel_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetKernelInfo, object_, name, param), + __GET_KERNEL_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getWorkGroupInfo( + const Device& device, cl_kernel_work_group_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetKernelWorkGroupInfo, object_, device(), name, param), + __GET_KERNEL_WORK_GROUP_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getWorkGroupInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_work_group_info, name>::param_type param; + cl_int result = getWorkGroupInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int setArg(cl_uint index, T value) + { + return detail::errHandler( + ::clSetKernelArg( + object_, + index, + detail::KernelArgumentHandler::size(value), + detail::KernelArgumentHandler::ptr(value)), + __SET_KERNEL_ARGS_ERR); + } + + cl_int setArg(cl_uint index, ::size_t size, void* argPtr) + { + return detail::errHandler( + ::clSetKernelArg(object_, index, size, argPtr), + __SET_KERNEL_ARGS_ERR); + } + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local); + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local); +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Kernel) + +/*! \class Program + * \brief Program interface that implements cl_program. + */ +class Program : public detail::Wrapper +{ +public: + typedef VECTOR_CLASS > Binaries; + typedef VECTOR_CLASS > Sources; + + Program( + const Context& context, + const Sources& sources, + cl_int* err = NULL) + { + cl_int error; + + const ::size_t n = (::size_t)sources.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const char** strings = (const char**) alloca(n * sizeof(const char*)); + + for (::size_t i = 0; i < n; ++i) { + strings[i] = sources[(int)i].first; + lengths[i] = sources[(int)i].second; + } + + object_ = ::clCreateProgramWithSource( + context(), (cl_uint)n, strings, lengths, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); + if (err != NULL) { + *err = error; + } + } + + Program( + const Context& context, + const VECTOR_CLASS& devices, + const Binaries& binaries, + VECTOR_CLASS* binaryStatus = NULL, + cl_int* err = NULL) + { + cl_int error; + const ::size_t n = binaries.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const unsigned char** images = (const unsigned char**) alloca(n * sizeof(const void*)); + + for (::size_t i = 0; i < n; ++i) { + images[i] = (const unsigned char*)binaries[(int)i].first; + lengths[i] = binaries[(int)i].second; + } + + object_ = ::clCreateProgramWithBinary( + context(), (cl_uint) devices.size(), + (cl_device_id*)&devices.front(), + lengths, images, binaryStatus != NULL + ? (cl_int*) &binaryStatus->front() + : NULL, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); + if (err != NULL) { + *err = error; + } + } + + Program() { } + + Program(const Program& program) : detail::Wrapper(program) { } + + Program& operator = (const Program& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int build( + const VECTOR_CLASS& devices, + const char* options = NULL, + void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, + void* data = NULL) const + { + return detail::errHandler( + ::clBuildProgram( + object_, + (cl_uint) + devices.size(), + (cl_device_id*)&devices.front(), + options, + notifyFptr, + data), + __BUILD_PROGRAM_ERR); + } + + template + cl_int getInfo(cl_program_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetProgramInfo, object_, name, param), + __GET_PROGRAM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getBuildInfo( + const Device& device, cl_program_build_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetProgramBuildInfo, object_, device(), name, param), + __GET_PROGRAM_BUILD_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getBuildInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_build_info, name>::param_type param; + cl_int result = getBuildInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int createKernels(VECTOR_CLASS* kernels) + { + cl_uint numKernels; + cl_int err = ::clCreateKernelsInProgram(object_, 0, NULL, &numKernels); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + Kernel* value = (Kernel*) alloca(numKernels * sizeof(Kernel)); + err = ::clCreateKernelsInProgram( + object_, numKernels, (cl_kernel*) value, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + kernels->assign(&value[0], &value[numKernels]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Program) + +inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) +{ + cl_int error; + + object_ = ::clCreateKernel(program(), name, &error); + detail::errHandler(error, __CREATE_KERNEL_ERR); + + if (err != NULL) { + *err = error; + } + +} + +/*! \class CommandQueue + * \brief CommandQueue interface for cl_command_queue. + */ +class CommandQueue : public detail::Wrapper +{ +public: + CommandQueue( + const Context& context, + const Device& device, + cl_command_queue_properties properties = 0, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateCommandQueue( + context(), device(), properties, &error); + + detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); + if (err != NULL) { + *err = error; + } + } + + CommandQueue() { } + + CommandQueue(const CommandQueue& commandQueue) : detail::Wrapper(commandQueue) { } + + CommandQueue& operator = (const CommandQueue& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_command_queue_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetCommandQueueInfo, object_, name, param), + __GET_COMMAND_QUEUE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_command_queue_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int enqueueReadBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_ERR); + } + + cl_int enqueueWriteBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + const void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_ERR); + } + + cl_int enqueueFillBuffer( + const Buffer& buffer, + const void* ptr, + ::size_t pattern_size, + ::size_t offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueFillBuffer( + object_, buffer(), ptr, pattern_size, offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_FILL_BUFFER_ERR); + } + + cl_int enqueueCopyBuffer( + const Buffer& src, + const Buffer& dst, + ::size_t src_offset, + ::size_t dst_offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBuffer( + object_, src(), dst(), src_offset, dst_offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int enqueueReadBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_RECT_ERR); + } + + + cl_int enqueueWriteBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_RECT_ERR); + } + + cl_int enqueueCopyBufferRect( + const Buffer& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + ::size_t src_row_pitch, + ::size_t src_slice_pitch, + ::size_t dst_row_pitch, + ::size_t dst_slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferRect( + object_, + src(), + dst(), + (const ::size_t *)src_origin, + (const ::size_t *)dst_origin, + (const ::size_t *)region, + src_row_pitch, + src_slice_pitch, + dst_row_pitch, + dst_slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_RECT_ERR); + } +#endif + + cl_int enqueueReadImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_IMAGE_ERR); + } + + cl_int enqueueWriteImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_IMAGE_ERR); + } + + cl_int enqueueCopyImage( + const Image& src, + const Image& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImage( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *)dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_ERR); + } + + cl_int enqueueCopyImageToBuffer( + const Image& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& region, + ::size_t dst_offset, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImageToBuffer( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *) region, dst_offset, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); + } + + cl_int enqueueCopyBufferToImage( + const Buffer& src, + const Image& dst, + ::size_t src_offset, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferToImage( + object_, src(), dst(), src_offset, + (const ::size_t *) dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); + } + + void* enqueueMapBuffer( + const Buffer& buffer, + cl_bool blocking, + cl_map_flags flags, + ::size_t offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapBuffer( + object_, buffer(), blocking, flags, offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + void* enqueueMapImage( + const Image& buffer, + cl_bool blocking, + cl_map_flags flags, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t * row_pitch, + ::size_t * slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapImage( + object_, buffer(), blocking, flags, + (const ::size_t *) origin, (const ::size_t *) region, + row_pitch, slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + cl_int enqueueUnmapMemObject( + const Memory& memory, + void* mapped_ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueUnmapMemObject( + object_, memory(), mapped_ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_UNMAP_MEM_OBJECT_ERR); + } + + cl_int enqueueNDRangeKernel( + const Kernel& kernel, + const NDRange& offset, + const NDRange& global, + const NDRange& local, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueNDRangeKernel( + object_, kernel(), (cl_uint) global.dimensions(), + offset.dimensions() != 0 ? (const ::size_t*) offset : NULL, + (const ::size_t*) global, + local.dimensions() != 0 ? (const ::size_t*) local : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NDRANGE_KERNEL_ERR); + } + + cl_int enqueueTask( + const Kernel& kernel, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueTask( + object_, kernel(), + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_TASK_ERR); + } + + cl_int enqueueNativeKernel( + void (*userFptr)(void *), + std::pair args, + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* mem_locs = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + cl_mem * mems = (mem_objects != NULL && mem_objects->size() > 0) + ? (cl_mem*) alloca(mem_objects->size() * sizeof(cl_mem)) + : NULL; + + if (mems != NULL) { + for (unsigned int i = 0; i < mem_objects->size(); i++) { + mems[i] = ((*mem_objects)[i])(); + } + } + + return detail::errHandler( + ::clEnqueueNativeKernel( + object_, userFptr, args.first, args.second, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + mems, + (mem_locs != NULL) ? (const void **) &mem_locs->front() : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NATIVE_KERNEL); + } + + cl_int enqueueMarker(Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueMarker(object_, (cl_event*) event), + __ENQUEUE_MARKER_ERR); + } + + cl_int enqueueWaitForEvents(const VECTOR_CLASS& events) const + { + return detail::errHandler( + ::clEnqueueWaitForEvents( + object_, + (cl_uint) events.size(), + (const cl_event*) &events.front()), + __ENQUEUE_WAIT_FOR_EVENTS_ERR); + } + + cl_int enqueueAcquireGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueAcquireGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReleaseGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } + +#if defined (USE_DX_INTEROP) +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueAcquireD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueReleaseD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); + + cl_int enqueueAcquireD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueAcquireD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueAcquireD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueReleaseD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueReleaseD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } +#endif + + cl_int enqueueBarrier() const + { + return detail::errHandler( + ::clEnqueueBarrier(object_), + __ENQUEUE_BARRIER_ERR); + } + + cl_int flush() const + { + return detail::errHandler(::clFlush(object_), __FLUSH_ERR); + } + + cl_int finish() const + { + return detail::errHandler(::clFinish(object_), __FINISH_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::CommandQueue) + +/*! \class KernelFunctor + * \brief Kernel functor interface + * + * \note Currently only functors of zero to ten arguments are supported. It + * is straightforward to add more and a more general solution, similar to + * Boost.Lambda could be followed if required in the future. + */ +class KernelFunctor +{ +private: + Kernel kernel_; + CommandQueue queue_; + NDRange offset_; + NDRange global_; + NDRange local_; + + cl_int err_; +public: + KernelFunctor() { } + + KernelFunctor( + const Kernel& kernel, + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) : + kernel_(kernel), + queue_(queue), + offset_(offset), + global_(global), + local_(local), + err_(CL_SUCCESS) + {} + + KernelFunctor& operator=(const KernelFunctor& rhs); + + KernelFunctor(const KernelFunctor& rhs); + + cl_int getError() { return err_; } + + inline Event operator()(const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events = NULL); +}; + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,offset,global,local); +} + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,NullRange,global,local); +} + +inline KernelFunctor& KernelFunctor::operator=(const KernelFunctor& rhs) +{ + if (this == &rhs) { + return *this; + } + + kernel_ = rhs.kernel_; + queue_ = rhs.queue_; + offset_ = rhs.offset_; + global_ = rhs.global_; + local_ = rhs.local_; + + return *this; +} + +inline KernelFunctor::KernelFunctor(const KernelFunctor& rhs) : + kernel_(rhs.kernel_), + queue_(rhs.queue_), + offset_(rhs.offset_), + global_(rhs.global_), + local_(rhs.local_) +{ +} + +Event KernelFunctor::operator()(const VECTOR_CLASS* events) +{ + (void)events; + Event event; + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + kernel_.setArg(14,a15); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +#undef __ERR_STR +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#undef __GET_DEVICE_INFO_ERR +#undef __GET_PLATFORM_INFO_ERR +#undef __GET_DEVICE_IDS_ERR +#undef __GET_CONTEXT_INFO_ERR +#undef __GET_EVENT_INFO_ERR +#undef __GET_EVENT_PROFILE_INFO_ERR +#undef __GET_MEM_OBJECT_INFO_ERR +#undef __GET_IMAGE_INFO_ERR +#undef __GET_SAMPLER_INFO_ERR +#undef __GET_KERNEL_INFO_ERR +#undef __GET_KERNEL_WORK_GROUP_INFO_ERR +#undef __GET_PROGRAM_INFO_ERR +#undef __GET_PROGRAM_BUILD_INFO_ERR +#undef __GET_COMMAND_QUEUE_INFO_ERR + +#undef __CREATE_CONTEXT_FROM_TYPE_ERR +#undef __GET_SUPPORTED_IMAGE_FORMATS_ERR + +#undef __CREATE_BUFFER_ERR +#undef __CREATE_SUBBUFFER_ERR +#undef __CREATE_IMAGE2D_ERR +#undef __CREATE_IMAGE3D_ERR +#undef __CREATE_SAMPLER_ERR +#undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR + +#undef __CREATE_USER_EVENT_ERR +#undef __SET_USER_EVENT_STATUS_ERR +#undef __SET_EVENT_CALLBACK_ERR + +#undef __WAIT_FOR_EVENTS_ERR + +#undef __CREATE_KERNEL_ERR +#undef __SET_KERNEL_ARGS_ERR +#undef __CREATE_PROGRAM_WITH_SOURCE_ERR +#undef __CREATE_PROGRAM_WITH_BINARY_ERR +#undef __BUILD_PROGRAM_ERR +#undef __CREATE_KERNELS_IN_PROGRAM_ERR + +#undef __CREATE_COMMAND_QUEUE_ERR +#undef __SET_COMMAND_QUEUE_PROPERTY_ERR +#undef __ENQUEUE_READ_BUFFER_ERR +#undef __ENQUEUE_WRITE_BUFFER_ERR +#undef __ENQUEUE_READ_BUFFER_RECT_ERR +#undef __ENQUEUE_WRITE_BUFFER_RECT_ERR +#undef __ENQEUE_COPY_BUFFER_ERR +#undef __ENQEUE_COPY_BUFFER_RECT_ERR +#undef __ENQUEUE_READ_IMAGE_ERR +#undef __ENQUEUE_WRITE_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR +#undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR +#undef __ENQUEUE_MAP_BUFFER_ERR +#undef __ENQUEUE_MAP_IMAGE_ERR +#undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR +#undef __ENQUEUE_NDRANGE_KERNEL_ERR +#undef __ENQUEUE_TASK_ERR +#undef __ENQUEUE_NATIVE_KERNEL + +#undef __UNLOAD_COMPILER_ERR +#endif //__CL_USER_OVERRIDE_ERROR_STRINGS + +#undef __GET_INFO_HELPER_WITH_RETAIN + +// Extensions +#undef __INIT_CL_EXT_FCN_PTR +#undef __CREATE_SUB_DEVICES + +#if defined(USE_CL_DEVICE_FISSION) +#undef __PARAM_NAME_DEVICE_FISSION +#endif // USE_CL_DEVICE_FISSION + +} // namespace cl + +#endif // CL_HPP_ diff --git a/src/libgpusolver/gpuconfig.h b/src/libgpusolver/gpuconfig.h new file mode 100644 index 00000000000..ea4922881e0 --- /dev/null +++ b/src/libgpusolver/gpuconfig.h @@ -0,0 +1,44 @@ +/* MIT License + * + * Copyright (c) 2016 Omar Alvarez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GPU_CONFIG_H +#define __GPU_CONFIG_H + +class GPUConfig { + +public: + //GPUConfig(); + //~GPUConfig(); + + bool useGPU; + unsigned selGPU; + bool allGPU; + bool forceGenProcLimit; + unsigned currentPlatform; + unsigned currentDevice; + unsigned globalWorkSize; + unsigned workgroupSize; + +}; + +#endif // __GPU_CONFIG_H diff --git a/src/libgpusolver/kernels/silentarmy.cl b/src/libgpusolver/kernels/silentarmy.cl new file mode 100644 index 00000000000..02ffd133358 --- /dev/null +++ b/src/libgpusolver/kernels/silentarmy.cl @@ -0,0 +1,762 @@ +#define PARAM_N 200 +#define PARAM_K 9 +#define PREFIX (PARAM_N / (PARAM_K + 1)) +#define NR_INPUTS (1 << PREFIX) +// Approximate log base 2 of number of elements in hash tables +#define APX_NR_ELMS_LOG (PREFIX + 1) +// Number of rows and slots is affected by this. 20 offers the best performance +// but occasionally misses ~1% of solutions. +#define NR_ROWS_LOG 20 + +// Make hash tables OVERHEAD times larger than necessary to store the average +// number of elements per row. The ideal value is as small as possible to +// reduce memory usage, but not too small or else elements are dropped from the +// hash tables. +// +// The actual number of elements per row is closer to the theoretical average +// (less variance) when NR_ROWS_LOG is small. So accordingly OVERHEAD can be +// smaller. +// +// Even (as opposed to odd) values of OVERHEAD sometimes significantly decrease +// performance as they cause VRAM channel conflicts. +#if NR_ROWS_LOG == 16 +#define OVERHEAD 3 +#elif NR_ROWS_LOG == 18 +#define OVERHEAD 5 +#elif NR_ROWS_LOG == 19 +#define OVERHEAD 9 +#elif NR_ROWS_LOG == 20 +#define OVERHEAD 13 +#endif + +#define NR_ROWS (1 << NR_ROWS_LOG) +#define NR_SLOTS ((1 << (APX_NR_ELMS_LOG - NR_ROWS_LOG)) * OVERHEAD) +// Length of 1 element (slot) in bytes +#define SLOT_LEN 32 +// Total size of hash table +#define HT_SIZE (NR_ROWS * NR_SLOTS * SLOT_LEN) +// Length of Zcash block header and nonce +#define ZCASH_BLOCK_HEADER_LEN 140 +#define ZCASH_NONCE_LEN 32 +// Number of bytes Zcash needs out of Blake +#define ZCASH_HASH_LEN 50 +// Number of wavefronts per SIMD for the Blake kernel. +// Blake is ALU-bound (beside the atomic counter being incremented) so we need +// at least 2 wavefronts per SIMD to hide the 2-clock latency of integer +// instructions. 10 is the max supported by the hw. +#define BLAKE_WPS 10 +#define MAX_SOLS 2000 + +// Optional features +#undef ENABLE_DEBUG + +/* +** Return the offset of Xi in bytes from the beginning of the slot. +*/ +#define xi_offset_for_round(round) (8 + ((round) / 2) * 4) + +// An (uncompressed) solution stores (1 << PARAM_K) 32-bit values +#define SOL_SIZE ((1 << PARAM_K) * 4) +typedef struct sols_s +{ + uint nr; + uint likely_invalids; + uchar valid[MAX_SOLS]; + uint values[MAX_SOLS][(1 << PARAM_K)]; +} sols_t; + +/* +** Assuming NR_ROWS_LOG == 16, the hash table slots have this layout (length in +** bytes in parens): +** +** round 0, table 0: cnt(4) i(4) pad(0) Xi(23.0) pad(1) +** round 1, table 1: cnt(4) i(4) pad(0.5) Xi(20.5) pad(3) +** round 2, table 0: cnt(4) i(4) i(4) pad(0) Xi(18.0) pad(2) +** round 3, table 1: cnt(4) i(4) i(4) pad(0.5) Xi(15.5) pad(4) +** round 4, table 0: cnt(4) i(4) i(4) i(4) pad(0) Xi(13.0) pad(3) +** round 5, table 1: cnt(4) i(4) i(4) i(4) pad(0.5) Xi(10.5) pad(5) +** round 6, table 0: cnt(4) i(4) i(4) i(4) i(4) pad(0) Xi( 8.0) pad(4) +** round 7, table 1: cnt(4) i(4) i(4) i(4) i(4) pad(0.5) Xi( 5.5) pad(6) +** round 8, table 0: cnt(4) i(4) i(4) i(4) i(4) i(4) pad(0) Xi( 3.0) pad(5) +** +** If the first byte of Xi is 0xAB then: +** - on even rounds, 'A' is part of the colliding PREFIX, 'B' is part of Xi +** - on odd rounds, 'A' and 'B' are both part of the colliding PREFIX, but +** 'A' is considered redundant padding as it was used to compute the row # +** +** - cnt is an atomic counter keeping track of the number of used slots. +** it is used in the first slot only; subsequent slots replace it with +** 4 padding bytes +** - i encodes either the 21-bit input value (round 0) or a reference to two +** inputs from the previous round +** +** Formula for Xi length and pad length above: +** > for i in range(9): +** > xi=(200-20*i-NR_ROWS_LOG)/8.; ci=8+4*((i)/2); print xi,32-ci-xi +** +** Note that the fractional .5-byte/4-bit padding following Xi for odd rounds +** is the 4 most significant bits of the last byte of Xi. +*/ + +__constant ulong blake_iv[] = +{ + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, +}; + +/* +** Reset counters in hash table. +*/ +__kernel +void kernel_init_ht(__global char *ht) +{ + uint tid = get_global_id(0); + *(__global uint *)(ht + tid * NR_SLOTS * SLOT_LEN) = 0; +} + +/* +** If xi0,xi1,xi2,xi3 are stored consecutively in little endian then they +** represent (hex notation, group of 5 hex digits are a group of PREFIX bits): +** aa aa ab bb bb cc cc cd dd... [round 0] +** -------------------- +** ...ab bb bb cc cc cd dd... [odd round] +** -------------- +** ...cc cc cd dd... [next even round] +** ----- +** Bytes underlined are going to be stored in the slot. Preceding bytes +** (and possibly part of the underlined bytes, depending on NR_ROWS_LOG) are +** used to compute the row number. +** +** Round 0: xi0,xi1,xi2,xi3 is a 25-byte Xi (xi3: only the low byte matter) +** Round 1: xi0,xi1,xi2 is a 23-byte Xi (incl. the colliding PREFIX nibble) +** TODO: update lines below with padding nibbles +** Round 2: xi0,xi1,xi2 is a 20-byte Xi (xi2: only the low 4 bytes matter) +** Round 3: xi0,xi1,xi2 is a 17.5-byte Xi (xi2: only the low 1.5 bytes matter) +** Round 4: xi0,xi1 is a 15-byte Xi (xi1: only the low 7 bytes matter) +** Round 5: xi0,xi1 is a 12.5-byte Xi (xi1: only the low 4.5 bytes matter) +** Round 6: xi0,xi1 is a 10-byte Xi (xi1: only the low 2 bytes matter) +** Round 7: xi0 is a 7.5-byte Xi (xi0: only the low 7.5 bytes matter) +** Round 8: xi0 is a 5-byte Xi (xi0: only the low 5 bytes matter) +** +** Return 0 if successfully stored, or 1 if the row overflowed. +*/ +uint ht_store(uint round, __global char *ht, uint i, + ulong xi0, ulong xi1, ulong xi2, ulong xi3) +{ + uint row; + __global char *p; + uint cnt; +#if NR_ROWS_LOG == 16 + if (!(round % 2)) + row = (xi0 & 0xffff); + else + // if we have in hex: "ab cd ef..." (little endian xi0) then this + // formula computes the row as 0xdebc. it skips the 'a' nibble as it + // is part of the PREFIX. The Xi will be stored starting with "ef..."; + // 'e' will be considered padding and 'f' is part of the current PREFIX + row = ((xi0 & 0xf00) << 4) | ((xi0 & 0xf00000) >> 12) | + ((xi0 & 0xf) << 4) | ((xi0 & 0xf000) >> 12); +#elif NR_ROWS_LOG == 18 + if (!(round % 2)) + row = (xi0 & 0xffff) | ((xi0 & 0xc00000) >> 6); + else + row = ((xi0 & 0xc0000) >> 2) | + ((xi0 & 0xf00) << 4) | ((xi0 & 0xf00000) >> 12) | + ((xi0 & 0xf) << 4) | ((xi0 & 0xf000) >> 12); +#elif NR_ROWS_LOG == 19 + if (!(round % 2)) + row = (xi0 & 0xffff) | ((xi0 & 0xe00000) >> 5); + else + row = ((xi0 & 0xe0000) >> 1) | + ((xi0 & 0xf00) << 4) | ((xi0 & 0xf00000) >> 12) | + ((xi0 & 0xf) << 4) | ((xi0 & 0xf000) >> 12); +#elif NR_ROWS_LOG == 20 + if (!(round % 2)) + row = (xi0 & 0xffff) | ((xi0 & 0xf00000) >> 4); + else + row = ((xi0 & 0xf0000) >> 0) | + ((xi0 & 0xf00) << 4) | ((xi0 & 0xf00000) >> 12) | + ((xi0 & 0xf) << 4) | ((xi0 & 0xf000) >> 12); +#else +#error "unsupported NR_ROWS_LOG" +#endif + xi0 = (xi0 >> 16) | (xi1 << (64 - 16)); + xi1 = (xi1 >> 16) | (xi2 << (64 - 16)); + xi2 = (xi2 >> 16) | (xi3 << (64 - 16)); + p = ht + row * NR_SLOTS * SLOT_LEN; + cnt = atomic_inc((__global uint *)p); + if (cnt >= NR_SLOTS) + return 1; + p += cnt * SLOT_LEN + xi_offset_for_round(round); + // store "i" (always 4 bytes before Xi) + *(__global uint *)(p - 4) = i; + if (round == 0 || round == 1) + { + // store 24 bytes + *(__global ulong *)(p + 0) = xi0; + *(__global ulong *)(p + 8) = xi1; + *(__global ulong *)(p + 16) = xi2; + } + else if (round == 2) + { + // store 20 bytes + *(__global ulong *)(p + 0) = xi0; + *(__global ulong *)(p + 8) = xi1; + *(__global uint *)(p + 16) = xi2; + } + else if (round == 3 || round == 4) + { + // store 16 bytes + *(__global ulong *)(p + 0) = xi0; + *(__global ulong *)(p + 8) = xi1; + + } + else if (round == 5) + { + // store 12 bytes + *(__global ulong *)(p + 0) = xi0; + *(__global uint *)(p + 8) = xi1; + } + else if (round == 6 || round == 7) + { + // store 8 bytes + *(__global ulong *)(p + 0) = xi0; + } + else if (round == 8) + { + // store 4 bytes + *(__global uint *)(p + 0) = xi0; + } + return 0; +} + +#define mix(va, vb, vc, vd, x, y) \ + va = (va + vb + x); \ + vd = rotate((vd ^ va), (ulong)64 - 32); \ + vc = (vc + vd); \ + vb = rotate((vb ^ vc), (ulong)64 - 24); \ + va = (va + vb + y); \ + vd = rotate((vd ^ va), (ulong)64 - 16); \ + vc = (vc + vd); \ + vb = rotate((vb ^ vc), (ulong)64 - 63); + +/* +** Execute round 0 (blake). +** +** Note: making the work group size less than or equal to the wavefront size +** allows the OpenCL compiler to remove the barrier() calls, see "2.2 Local +** Memory (LDS) Optimization 2-10" in: +** http://developer.amd.com/tools-and-sdks/opencl-zone/amd-accelerated-parallel-processing-app-sdk/opencl-optimization-guide/ +*/ +__kernel __attribute__((reqd_work_group_size(64, 1, 1))) +void kernel_round0(__global ulong *blake_state, __global char *ht, + __global uint *debug) +{ + uint tid = get_global_id(0); + ulong v[16]; + uint inputs_per_thread = NR_INPUTS / get_global_size(0); + uint input = tid * inputs_per_thread; + uint input_end = (tid + 1) * inputs_per_thread; + uint dropped = 0; + while (input < input_end) + { + // shift "i" to occupy the high 32 bits of the second ulong word in the + // message block + ulong word1 = (ulong)input << 32; + // init vector v + v[0] = blake_state[0]; + v[1] = blake_state[1]; + v[2] = blake_state[2]; + v[3] = blake_state[3]; + v[4] = blake_state[4]; + v[5] = blake_state[5]; + v[6] = blake_state[6]; + v[7] = blake_state[7]; + v[8] = blake_iv[0]; + v[9] = blake_iv[1]; + v[10] = blake_iv[2]; + v[11] = blake_iv[3]; + v[12] = blake_iv[4]; + v[13] = blake_iv[5]; + v[14] = blake_iv[6]; + v[15] = blake_iv[7]; + // mix in length of data + v[12] ^= ZCASH_BLOCK_HEADER_LEN + 4 /* length of "i" */; + // last block + v[14] ^= -1; + + // round 1 + mix(v[0], v[4], v[8], v[12], 0, word1); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 2 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], word1, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 3 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, word1); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 4 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, word1); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 5 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, word1); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 6 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], word1, 0); + // round 7 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], word1, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 8 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, word1); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 9 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], word1, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 10 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], word1, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 11 + mix(v[0], v[4], v[8], v[12], 0, word1); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], 0, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + // round 12 + mix(v[0], v[4], v[8], v[12], 0, 0); + mix(v[1], v[5], v[9], v[13], 0, 0); + mix(v[2], v[6], v[10], v[14], 0, 0); + mix(v[3], v[7], v[11], v[15], 0, 0); + mix(v[0], v[5], v[10], v[15], word1, 0); + mix(v[1], v[6], v[11], v[12], 0, 0); + mix(v[2], v[7], v[8], v[13], 0, 0); + mix(v[3], v[4], v[9], v[14], 0, 0); + + // compress v into the blake state; this produces the 50-byte hash + // (two Xi values) + ulong h[7]; + h[0] = blake_state[0] ^ v[0] ^ v[8]; + h[1] = blake_state[1] ^ v[1] ^ v[9]; + h[2] = blake_state[2] ^ v[2] ^ v[10]; + h[3] = blake_state[3] ^ v[3] ^ v[11]; + h[4] = blake_state[4] ^ v[4] ^ v[12]; + h[5] = blake_state[5] ^ v[5] ^ v[13]; + h[6] = (blake_state[6] ^ v[6] ^ v[14]) & 0xffff; + + // store the two Xi values in the hash table +#if ZCASH_HASH_LEN == 50 + dropped += ht_store(0, ht, input * 2, + h[0], + h[1], + h[2], + h[3]); + dropped += ht_store(0, ht, input * 2 + 1, + (h[3] >> 8) | (h[4] << (64 - 8)), + (h[4] >> 8) | (h[5] << (64 - 8)), + (h[5] >> 8) | (h[6] << (64 - 8)), + (h[6] >> 8)); +#else +#error "unsupported ZCASH_HASH_LEN" +#endif + + input++; + } +#ifdef ENABLE_DEBUG + debug[tid * 2] = 0; + debug[tid * 2 + 1] = dropped; +#endif +} + +#if NR_ROWS_LOG <= 16 && NR_SLOTS <= (1 << 8) + +#define ENCODE_INPUTS(row, slot0, slot1) \ + ((row << 16) | ((slot1 & 0xff) << 8) | (slot0 & 0xff)) +#define DECODE_ROW(REF) (REF >> 16) +#define DECODE_SLOT1(REF) ((REF >> 8) & 0xff) +#define DECODE_SLOT0(REF) (REF & 0xff) + +#elif NR_ROWS_LOG == 18 && NR_SLOTS <= (1 << 7) + +#define ENCODE_INPUTS(row, slot0, slot1) \ + ((row << 14) | ((slot1 & 0x7f) << 7) | (slot0 & 0x7f)) +#define DECODE_ROW(REF) (REF >> 14) +#define DECODE_SLOT1(REF) ((REF >> 7) & 0x7f) +#define DECODE_SLOT0(REF) (REF & 0x7f) + +#elif NR_ROWS_LOG == 19 && NR_SLOTS <= (1 << 6) + +#define ENCODE_INPUTS(row, slot0, slot1) \ + ((row << 13) | ((slot1 & 0x3f) << 6) | (slot0 & 0x3f)) /* 1 spare bit */ +#define DECODE_ROW(REF) (REF >> 13) +#define DECODE_SLOT1(REF) ((REF >> 6) & 0x3f) +#define DECODE_SLOT0(REF) (REF & 0x3f) + +#elif NR_ROWS_LOG == 20 && NR_SLOTS <= (1 << 6) + +#define ENCODE_INPUTS(row, slot0, slot1) \ + ((row << 12) | ((slot1 & 0x3f) << 6) | (slot0 & 0x3f)) +#define DECODE_ROW(REF) (REF >> 12) +#define DECODE_SLOT1(REF) ((REF >> 6) & 0x3f) +#define DECODE_SLOT0(REF) (REF & 0x3f) + +#else +#error "unsupported NR_ROWS_LOG" +#endif + +/* +** XOR a pair of Xi values computed at "round - 1" and store the result in the +** hash table being built for "round". Note that when building the table for +** even rounds we need to skip 1 padding byte present in the "round - 1" table +** (the "0xAB" byte mentioned in the description at the top of this file.) But +** also note we can't load data directly past this byte because this would +** cause an unaligned memory access which is undefined per the OpenCL spec. +** +** Return 0 if successfully stored, or 1 if the row overflowed. +*/ +uint xor_and_store(uint round, __global char *ht_dst, uint row, + uint slot_a, uint slot_b, __global ulong *a, __global ulong *b) +{ + ulong xi0, xi1, xi2; +#if NR_ROWS_LOG >= 16 && NR_ROWS_LOG <= 20 + // Note: for NR_ROWS_LOG == 20, for odd rounds, we could optimize by not + // storing the byte containing bits from the previous PREFIX block for + if (round == 1 || round == 2) + { + // xor 24 bytes + xi0 = *(a++) ^ *(b++); + xi1 = *(a++) ^ *(b++); + xi2 = *a ^ *b; + if (round == 2) + { + // skip padding byte + xi0 = (xi0 >> 8) | (xi1 << (64 - 8)); + xi1 = (xi1 >> 8) | (xi2 << (64 - 8)); + xi2 = (xi2 >> 8); + } + } + else if (round == 3) + { + // xor 20 bytes + xi0 = *a++ ^ *b++; + xi1 = *a++ ^ *b++; + xi2 = *(__global uint *)a ^ *(__global uint *)b; + } + else if (round == 4 || round == 5) + { + // xor 16 bytes + xi0 = *a++ ^ *b++; + xi1 = *a ^ *b; + xi2 = 0; + if (round == 4) + { + // skip padding byte + xi0 = (xi0 >> 8) | (xi1 << (64 - 8)); + xi1 = (xi1 >> 8); + } + } + else if (round == 6) + { + // xor 12 bytes + xi0 = *a++ ^ *b++; + xi1 = *(__global uint *)a ^ *(__global uint *)b; + xi2 = 0; + if (round == 6) + { + // skip padding byte + xi0 = (xi0 >> 8) | (xi1 << (64 - 8)); + xi1 = (xi1 >> 8); + } + } + else if (round == 7 || round == 8) + { + // xor 8 bytes + xi0 = *a ^ *b; + xi1 = 0; + xi2 = 0; + if (round == 8) + { + // skip padding byte + xi0 = (xi0 >> 8); + } + } + // invalid solutions (which start happenning in round 5) have duplicate + // inputs and xor to zero, so discard them + if (!xi0 && !xi1) + return 0; +#else +#error "unsupported NR_ROWS_LOG" +#endif + return ht_store(round, ht_dst, ENCODE_INPUTS(row, slot_a, slot_b), + xi0, xi1, xi2, 0); +} + +/* +** Execute one Equihash round. Read from ht_src, XOR colliding pairs of Xi, +** store them in ht_dst. +*/ +void equihash_round(uint round, __global char *ht_src, __global char *ht_dst, + __global uint *debug) +{ + uint tid = get_global_id(0); + uint tlid = get_local_id(0); + __global char *p; + uint cnt; + uchar first_words[NR_SLOTS]; + uchar mask; + uint i, j; + // NR_SLOTS is already oversized (by a factor of OVERHEAD), but we want to + // make it twice larger + ushort collisions[NR_SLOTS * 2]; + uint nr_coll = 0; + uint n; + uint dropped_coll, dropped_stor; + __global ulong *a, *b; + uint xi_offset; + // read first words of Xi from the previous (round - 1) hash table + xi_offset = xi_offset_for_round(round - 1); + // the mask is also computed to read data from the previous round +#if NR_ROWS_LOG == 16 + mask = ((!(round % 2)) ? 0x0f : 0xf0); +#elif NR_ROWS_LOG == 18 + mask = ((!(round % 2)) ? 0x03 : 0x30); +#elif NR_ROWS_LOG == 19 + mask = ((!(round % 2)) ? 0x01 : 0x10); +#elif NR_ROWS_LOG == 20 + mask = 0; /* we can vastly simplify the code below */ +#else +#error "unsupported NR_ROWS_LOG" +#endif + p = (ht_src + tid * NR_SLOTS * SLOT_LEN); + cnt = *(__global uint *)p; + cnt = min(cnt, (uint)NR_SLOTS); // handle possible overflow in prev. round + p += xi_offset; + for (i = 0; i < cnt; i++, p += SLOT_LEN) + first_words[i] = *(__global uchar *)p; + // find collisions + nr_coll = 0; + dropped_coll = 0; + for (i = 0; i < cnt; i++) + for (j = i + 1; j < cnt; j++) + if ((first_words[i] & mask) == + (first_words[j] & mask)) + { + // collision! + if (nr_coll >= sizeof (collisions) / sizeof (*collisions)) + dropped_coll++; + else +#if NR_SLOTS <= (1 << 8) + // note: this assumes slots can be encoded in 8 bits + collisions[nr_coll++] = + ((ushort)j << 8) | ((ushort)i & 0xff); +#else +#error "unsupported NR_SLOTS" +#endif + } + // XOR colliding pairs of Xi + dropped_stor = 0; + for (n = 0; n < nr_coll; n++) + { + i = collisions[n] & 0xff; + j = collisions[n] >> 8; + a = (__global ulong *) + (ht_src + tid * NR_SLOTS * SLOT_LEN + i * SLOT_LEN + xi_offset); + b = (__global ulong *) + (ht_src + tid * NR_SLOTS * SLOT_LEN + j * SLOT_LEN + xi_offset); + dropped_stor += xor_and_store(round, ht_dst, tid, i, j, a, b); + } + if (round < 8) + // reset the counter in preparation of the next round + *(__global uint *)(ht_src + tid * NR_SLOTS * SLOT_LEN) = 0; +#ifdef ENABLE_DEBUG + debug[tid * 2] = dropped_coll; + debug[tid * 2 + 1] = dropped_stor; +#endif +} + +/* +** This defines kernel_round1, kernel_round2, ..., kernel_round8. +*/ +#define KERNEL_ROUND(N) \ +__kernel __attribute__((reqd_work_group_size(64, 1, 1))) \ +void kernel_round ## N(__global char *ht_src, __global char *ht_dst, \ + __global uint *debug) \ +{ \ + equihash_round(N, ht_src, ht_dst, debug); \ +} +KERNEL_ROUND(1) +KERNEL_ROUND(2) +KERNEL_ROUND(3) +KERNEL_ROUND(4) +KERNEL_ROUND(5) +KERNEL_ROUND(6) +KERNEL_ROUND(7) +KERNEL_ROUND(8) + +uint expand_ref(__global char *ht, uint xi_offset, uint row, uint slot) +{ + return *(__global uint *)(ht + row * NR_SLOTS * SLOT_LEN + + slot * SLOT_LEN + xi_offset - 4); +} + +void expand_refs(__global uint *ins, uint nr_inputs, __global char **htabs, + uint round) +{ + __global char *ht = htabs[round % 2]; + uint i = nr_inputs - 1; + uint j = nr_inputs * 2 - 1; + uint xi_offset = xi_offset_for_round(round); + do + { + ins[j] = expand_ref(ht, xi_offset, + DECODE_ROW(ins[i]), DECODE_SLOT1(ins[i])); + ins[j - 1] = expand_ref(ht, xi_offset, + DECODE_ROW(ins[i]), DECODE_SLOT0(ins[i])); + if (!i) + break ; + i--; + j -= 2; + } + while (1); +} + +/* +** Verify if a potential solution is in fact valid. +*/ +void potential_sol(__global char **htabs, __global sols_t *sols, + uint ref0, uint ref1) +{ + uint sol_i; + uint nr_values; + sol_i = atomic_inc(&sols->nr); + if (sol_i >= MAX_SOLS) + return ; + sols->valid[sol_i] = 0; + nr_values = 0; + sols->values[sol_i][nr_values++] = ref0; + sols->values[sol_i][nr_values++] = ref1; + uint round = PARAM_K - 1; + do + { + round--; + expand_refs(&(sols->values[sol_i][0]), nr_values, htabs, round); + nr_values *= 2; + } + while (round > 0); + sols->valid[sol_i] = 1; +} + +/* +** Scan the hash tables to find Equihash solutions. +*/ +__kernel +void kernel_sols(__global char *ht0, __global char *ht1, __global sols_t *sols) +{ + uint tid = get_global_id(0); + __global char *htabs[2] = { ht0, ht1 }; + uint ht_i = (PARAM_K - 1) % 2; // table filled at last round + uint cnt; + uint xi_offset = xi_offset_for_round(PARAM_K - 1); + uint i, j; + __global char *a, *b; + uint ref_i, ref_j; + // it's ok for the collisions array to be so small, as if it fills up + // the potential solutions are likely invalid (many duplicate inputs) + ulong collisions[5]; + uint coll; +#if NR_ROWS_LOG >= 16 && NR_ROWS_LOG <= 20 + // in the final hash table, we are looking for a match on both the bits + // part of the previous PREFIX colliding bits, and the last PREFIX bits. + uint mask = 0xffffff; +#else +#error "unsupported NR_ROWS_LOG" +#endif + if (tid == 0) + sols->nr = sols->likely_invalids = 0; + mem_fence(CLK_GLOBAL_MEM_FENCE); // for tid 0 initializing struct above + a = htabs[ht_i] + tid * NR_SLOTS * SLOT_LEN; + cnt = *(__global uint *)a; + cnt = min(cnt, (uint)NR_SLOTS); // handle possible overflow in last round + coll = 0; + a += xi_offset; + for (i = 0; i < cnt; i++, a += SLOT_LEN) + for (j = i + 1, b = a + SLOT_LEN; j < cnt; j++, b += SLOT_LEN) + if (((*(__global uint *)a) & mask) == + ((*(__global uint *)b) & mask)) + { + ref_i = *(__global uint *)(a - 4); + ref_j = *(__global uint *)(b - 4); + if (coll < sizeof (collisions) / sizeof (*collisions)) + collisions[coll++] = ((ulong)ref_i << 32) | ref_j; + else + atomic_inc(&sols->likely_invalids); + } + if (!coll) + return ; + for (i = 0; i < coll; i++) + potential_sol(htabs, sols, collisions[i] >> 32, + collisions[i] & 0xffffffff); +} diff --git a/src/libgpusolver/kernels/silentarmy.h b/src/libgpusolver/kernels/silentarmy.h new file mode 100644 index 00000000000..0213fd1f5d9 --- /dev/null +++ b/src/libgpusolver/kernels/silentarmy.h @@ -0,0 +1,2096 @@ +const unsigned char CL_MINER_KERNEL[] = { + 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x50, 0x41, 0x52, 0x41, + 0x4d, 0x5f, 0x4e, 0x09, 0x09, 0x09, 0x09, 0x32, 0x30, 0x30, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x50, 0x41, 0x52, 0x41, 0x4d, + 0x5f, 0x4b, 0x09, 0x09, 0x09, 0x09, 0x39, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 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, + 0x28, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4e, 0x20, 0x2f, 0x20, 0x28, + 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4b, 0x20, 0x2b, 0x20, 0x31, 0x29, + 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x52, + 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, + 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x29, 0x0a, 0x2f, 0x2f, 0x20, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x20, 0x6c, + 0x6f, 0x67, 0x20, 0x62, 0x61, 0x73, 0x65, 0x20, 0x32, 0x20, 0x6f, 0x66, + 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x68, + 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x50, 0x58, 0x5f, 0x4e, + 0x52, 0x5f, 0x45, 0x4c, 0x4d, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x20, 0x2b, + 0x20, 0x31, 0x29, 0x0a, 0x2f, 0x2f, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x6f, 0x77, 0x73, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, + 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x20, 0x32, 0x30, 0x20, 0x6f, 0x66, 0x66, 0x65, + 0x72, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x20, + 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x0a, + 0x2f, 0x2f, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6f, 0x63, 0x63, 0x61, 0x73, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x6d, 0x69, 0x73, 0x73, + 0x65, 0x73, 0x20, 0x7e, 0x31, 0x25, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x6f, + 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, + 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x32, 0x30, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x4d, 0x61, 0x6b, 0x65, + 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x20, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, + 0x68, 0x61, 0x6e, 0x20, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x72, + 0x79, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x2f, + 0x2f, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x65, 0x72, + 0x20, 0x72, 0x6f, 0x77, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x64, + 0x65, 0x61, 0x6c, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x73, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, + 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, + 0x0a, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x20, 0x6d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x20, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2c, + 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x6f, 0x6f, + 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x6c, + 0x73, 0x65, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2f, 0x2f, 0x20, + 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x2e, + 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, + 0x63, 0x74, 0x75, 0x61, 0x6c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x70, 0x65, 0x72, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x74, 0x68, 0x65, 0x6f, 0x72, 0x65, 0x74, 0x69, 0x63, 0x61, + 0x6c, 0x20, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x2f, 0x2f, + 0x20, 0x28, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x76, 0x61, 0x72, 0x69, 0x61, + 0x6e, 0x63, 0x65, 0x29, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x4e, 0x52, + 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x69, 0x73, + 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2e, 0x20, 0x53, 0x6f, 0x20, 0x61, + 0x63, 0x63, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x6c, 0x79, 0x20, 0x4f, + 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x20, 0x63, 0x61, 0x6e, 0x20, + 0x62, 0x65, 0x0a, 0x2f, 0x2f, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x45, 0x76, 0x65, + 0x6e, 0x20, 0x28, 0x61, 0x73, 0x20, 0x6f, 0x70, 0x70, 0x6f, 0x73, 0x65, + 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x64, 0x64, 0x29, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x4f, 0x56, 0x45, 0x52, + 0x48, 0x45, 0x41, 0x44, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x6e, 0x74, 0x6c, 0x79, 0x20, 0x64, 0x65, 0x63, 0x72, 0x65, 0x61, 0x73, + 0x65, 0x0a, 0x2f, 0x2f, 0x20, 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x79, + 0x20, 0x63, 0x61, 0x75, 0x73, 0x65, 0x20, 0x56, 0x52, 0x41, 0x4d, 0x20, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, + 0x6c, 0x69, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x4e, + 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, + 0x3d, 0x20, 0x31, 0x36, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x33, 0x0a, 0x23, + 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, + 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x38, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, + 0x45, 0x41, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x35, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, + 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, + 0x3d, 0x20, 0x31, 0x39, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x39, 0x0a, 0x23, + 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, + 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x30, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, + 0x45, 0x41, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x31, 0x33, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, + 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x52, + 0x5f, 0x52, 0x4f, 0x57, 0x53, 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, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, + 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x29, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x52, 0x5f, + 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, + 0x28, 0x41, 0x50, 0x58, 0x5f, 0x4e, 0x52, 0x5f, 0x45, 0x4c, 0x4d, 0x53, + 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x2d, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, + 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x4f, + 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x29, 0x0a, 0x2f, 0x2f, 0x20, + 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x31, 0x20, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x73, 0x6c, 0x6f, + 0x74, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, + 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x53, 0x4c, 0x4f, 0x54, + 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x33, 0x32, 0x0a, 0x2f, 0x2f, 0x20, 0x54, 0x6f, + 0x74, 0x61, 0x6c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x48, 0x54, 0x5f, 0x53, 0x49, + 0x5a, 0x45, 0x09, 0x09, 0x09, 0x09, 0x28, 0x4e, 0x52, 0x5f, 0x52, 0x4f, + 0x57, 0x53, 0x20, 0x2a, 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, + 0x53, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, + 0x29, 0x0a, 0x2f, 0x2f, 0x20, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, + 0x6f, 0x66, 0x20, 0x5a, 0x63, 0x61, 0x73, 0x68, 0x20, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, 0x42, 0x4c, + 0x4f, 0x43, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x5f, 0x4c, + 0x45, 0x4e, 0x09, 0x09, 0x31, 0x34, 0x30, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, 0x4e, 0x4f, + 0x4e, 0x43, 0x45, 0x5f, 0x4c, 0x45, 0x4e, 0x09, 0x09, 0x09, 0x33, 0x32, + 0x0a, 0x2f, 0x2f, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, + 0x66, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x5a, 0x63, 0x61, 0x73, + 0x68, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x20, 0x6f, 0x75, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x42, 0x6c, 0x61, 0x6b, 0x65, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, 0x48, + 0x41, 0x53, 0x48, 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x35, 0x30, 0x0a, 0x2f, 0x2f, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x20, 0x6f, 0x66, 0x20, 0x77, 0x61, 0x76, 0x65, 0x66, 0x72, 0x6f, + 0x6e, 0x74, 0x73, 0x20, 0x70, 0x65, 0x72, 0x20, 0x53, 0x49, 0x4d, 0x44, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x42, 0x6c, 0x61, + 0x6b, 0x65, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x2e, 0x0a, 0x2f, + 0x2f, 0x20, 0x42, 0x6c, 0x61, 0x6b, 0x65, 0x20, 0x69, 0x73, 0x20, 0x41, + 0x4c, 0x55, 0x2d, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x28, 0x62, 0x65, + 0x73, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x6f, + 0x6d, 0x69, 0x63, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, + 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x65, 0x64, 0x29, 0x20, 0x73, 0x6f, 0x20, 0x77, 0x65, + 0x20, 0x6e, 0x65, 0x65, 0x64, 0x0a, 0x2f, 0x2f, 0x20, 0x61, 0x74, 0x20, + 0x6c, 0x65, 0x61, 0x73, 0x74, 0x20, 0x32, 0x20, 0x77, 0x61, 0x76, 0x65, + 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x65, 0x72, 0x20, 0x53, + 0x49, 0x4d, 0x44, 0x20, 0x74, 0x6f, 0x20, 0x68, 0x69, 0x64, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x32, 0x2d, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, + 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x69, + 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x0a, 0x2f, 0x2f, 0x20, 0x69, 0x6e, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, + 0x31, 0x30, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, + 0x78, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x77, 0x2e, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x42, 0x4c, 0x41, 0x4b, 0x45, + 0x5f, 0x57, 0x50, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x31, 0x30, 0x0a, 0x23, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x53, + 0x4f, 0x4c, 0x53, 0x09, 0x09, 0x09, 0x32, 0x30, 0x30, 0x30, 0x0a, 0x0a, + 0x2f, 0x2f, 0x20, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x23, 0x75, 0x6e, + 0x64, 0x65, 0x66, 0x20, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x44, + 0x45, 0x42, 0x55, 0x47, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x58, 0x69, 0x20, + 0x69, 0x6e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x6e, + 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x6c, 0x6f, 0x74, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x09, 0x28, 0x38, 0x20, 0x2b, 0x20, + 0x28, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x20, 0x2f, 0x20, 0x32, + 0x29, 0x20, 0x2a, 0x20, 0x34, 0x29, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x41, + 0x6e, 0x20, 0x28, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x64, 0x29, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x28, 0x31, 0x20, + 0x3c, 0x3c, 0x20, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4b, 0x29, 0x20, + 0x33, 0x32, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x53, 0x4f, + 0x4c, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x09, 0x09, 0x09, 0x28, 0x28, 0x31, + 0x20, 0x3c, 0x3c, 0x20, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4b, 0x29, + 0x20, 0x2a, 0x20, 0x34, 0x29, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, + 0x66, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x09, 0x73, 0x6f, 0x6c, + 0x73, 0x5f, 0x73, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x09, 0x6e, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x09, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x5f, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x73, 0x73, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x09, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x5b, 0x4d, 0x41, 0x58, 0x5f, 0x53, 0x4f, 0x4c, 0x53, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x5b, 0x4d, 0x41, 0x58, 0x5f, 0x53, 0x4f, 0x4c, + 0x53, 0x5d, 0x5b, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x50, 0x41, 0x52, + 0x41, 0x4d, 0x5f, 0x4b, 0x29, 0x5d, 0x3b, 0x0a, 0x7d, 0x09, 0x09, 0x73, + 0x6f, 0x6c, 0x73, 0x5f, 0x74, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, + 0x2a, 0x20, 0x41, 0x73, 0x73, 0x75, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x4e, + 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, + 0x3d, 0x20, 0x31, 0x36, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x6c, 0x6f, + 0x74, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x20, 0x28, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x0a, 0x2a, 0x2a, 0x20, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x73, 0x29, 0x3a, 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x30, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x30, 0x3a, 0x20, 0x63, 0x6e, 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, + 0x28, 0x34, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x30, 0x29, 0x20, 0x20, 0x20, 0x58, 0x69, 0x28, + 0x32, 0x33, 0x2e, 0x30, 0x29, 0x20, 0x70, 0x61, 0x64, 0x28, 0x31, 0x29, + 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x31, 0x2c, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x31, 0x3a, 0x20, 0x63, 0x6e, + 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x28, 0x30, 0x2e, + 0x35, 0x29, 0x20, 0x58, 0x69, 0x28, 0x32, 0x30, 0x2e, 0x35, 0x29, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x33, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x32, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x30, 0x3a, 0x20, 0x63, 0x6e, 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, + 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x30, 0x29, 0x20, 0x20, 0x20, 0x58, 0x69, 0x28, + 0x31, 0x38, 0x2e, 0x30, 0x29, 0x20, 0x70, 0x61, 0x64, 0x28, 0x32, 0x29, + 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x33, 0x2c, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x31, 0x3a, 0x20, 0x63, 0x6e, + 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, + 0x34, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x28, 0x30, 0x2e, + 0x35, 0x29, 0x20, 0x58, 0x69, 0x28, 0x31, 0x35, 0x2e, 0x35, 0x29, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x34, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x34, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x30, 0x3a, 0x20, 0x63, 0x6e, 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, + 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, + 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x30, 0x29, 0x20, 0x20, 0x20, 0x58, 0x69, 0x28, + 0x31, 0x33, 0x2e, 0x30, 0x29, 0x20, 0x70, 0x61, 0x64, 0x28, 0x33, 0x29, + 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x35, 0x2c, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x31, 0x3a, 0x20, 0x63, 0x6e, + 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, + 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x28, 0x30, 0x2e, + 0x35, 0x29, 0x20, 0x58, 0x69, 0x28, 0x31, 0x30, 0x2e, 0x35, 0x29, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x35, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x36, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x30, 0x3a, 0x20, 0x63, 0x6e, 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, + 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, + 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x30, 0x29, 0x20, 0x20, 0x20, 0x58, 0x69, 0x28, + 0x20, 0x38, 0x2e, 0x30, 0x29, 0x20, 0x70, 0x61, 0x64, 0x28, 0x34, 0x29, + 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x37, 0x2c, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x31, 0x3a, 0x20, 0x63, 0x6e, + 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, + 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x64, 0x28, 0x30, 0x2e, + 0x35, 0x29, 0x20, 0x58, 0x69, 0x28, 0x20, 0x35, 0x2e, 0x35, 0x29, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x36, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x38, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x30, 0x3a, 0x20, 0x63, 0x6e, 0x74, 0x28, 0x34, 0x29, 0x20, 0x69, + 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, + 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, 0x69, 0x28, 0x34, 0x29, 0x20, + 0x70, 0x61, 0x64, 0x28, 0x30, 0x29, 0x20, 0x20, 0x20, 0x58, 0x69, 0x28, + 0x20, 0x33, 0x2e, 0x30, 0x29, 0x20, 0x70, 0x61, 0x64, 0x28, 0x35, 0x29, + 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x2d, 0x20, 0x63, 0x6e, 0x74, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, + 0x63, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x6b, 0x65, + 0x65, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x73, 0x6c, + 0x6f, 0x74, 0x73, 0x2e, 0x0a, 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x69, 0x74, + 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x73, 0x6c, + 0x6f, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x3b, 0x20, 0x73, 0x75, 0x62, + 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x6c, 0x6f, 0x74, + 0x73, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x69, 0x74, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x34, + 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x0a, 0x2a, 0x2a, 0x20, 0x2d, 0x20, 0x69, 0x20, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x32, 0x31, 0x2d, 0x62, 0x69, 0x74, 0x20, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x30, 0x29, 0x20, 0x6f, 0x72, + 0x20, 0x61, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x77, 0x6f, 0x0a, 0x2a, 0x2a, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, + 0x73, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, + 0x2a, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x58, + 0x69, 0x20, 0x69, 0x73, 0x20, 0x30, 0x78, 0x41, 0x42, 0x20, 0x74, 0x68, + 0x65, 0x6e, 0x3a, 0x0a, 0x2a, 0x2a, 0x20, 0x2d, 0x20, 0x6f, 0x6e, 0x20, + 0x65, 0x76, 0x65, 0x6e, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x2c, + 0x20, 0x27, 0x41, 0x27, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, + 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, + 0x2c, 0x20, 0x27, 0x42, 0x27, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x58, 0x69, 0x0a, 0x2a, 0x2a, 0x20, 0x2d, + 0x20, 0x6f, 0x6e, 0x20, 0x6f, 0x64, 0x64, 0x20, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x73, 0x2c, 0x20, 0x27, 0x41, 0x27, 0x20, 0x69, 0x73, 0x20, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x27, 0x42, 0x27, 0x20, + 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x64, 0x69, 0x6e, 0x67, + 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, + 0x2a, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x58, 0x69, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x64, 0x20, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x20, 0x61, 0x62, 0x6f, 0x76, 0x65, 0x3a, 0x0a, 0x2a, 0x2a, + 0x20, 0x3e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x69, 0x6e, 0x20, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x28, 0x39, 0x29, 0x3a, 0x0a, 0x2a, 0x2a, + 0x20, 0x3e, 0x20, 0x20, 0x20, 0x78, 0x69, 0x3d, 0x28, 0x32, 0x30, 0x30, + 0x2d, 0x32, 0x30, 0x2a, 0x69, 0x2d, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, + 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x29, 0x2f, 0x38, 0x2e, 0x3b, 0x20, 0x63, + 0x69, 0x3d, 0x38, 0x2b, 0x34, 0x2a, 0x28, 0x28, 0x69, 0x29, 0x2f, 0x32, + 0x29, 0x3b, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x69, 0x2c, + 0x33, 0x32, 0x2d, 0x63, 0x69, 0x2d, 0x78, 0x69, 0x0a, 0x2a, 0x2a, 0x0a, + 0x2a, 0x2a, 0x20, 0x4e, 0x6f, 0x74, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x20, 0x2e, 0x35, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x2f, + 0x34, 0x2d, 0x62, 0x69, 0x74, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, + 0x67, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, + 0x58, 0x69, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x64, 0x64, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x0a, 0x2a, 0x2a, 0x20, 0x69, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x34, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x69, 0x66, 0x69, 0x63, 0x61, 0x6e, 0x74, 0x20, 0x62, + 0x69, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, + 0x61, 0x73, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x58, 0x69, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x0a, 0x5f, 0x5f, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, 0x5d, 0x20, + 0x3d, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x36, 0x61, + 0x30, 0x39, 0x65, 0x36, 0x36, 0x37, 0x66, 0x33, 0x62, 0x63, 0x63, 0x39, + 0x30, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x62, 0x62, 0x36, 0x37, 0x61, 0x65, + 0x38, 0x35, 0x38, 0x34, 0x63, 0x61, 0x61, 0x37, 0x33, 0x62, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x33, 0x63, 0x36, 0x65, 0x66, 0x33, + 0x37, 0x32, 0x66, 0x65, 0x39, 0x34, 0x66, 0x38, 0x32, 0x62, 0x2c, 0x20, + 0x30, 0x78, 0x61, 0x35, 0x34, 0x66, 0x66, 0x35, 0x33, 0x61, 0x35, 0x66, + 0x31, 0x64, 0x33, 0x36, 0x66, 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x78, 0x35, 0x31, 0x30, 0x65, 0x35, 0x32, 0x37, 0x66, 0x61, 0x64, + 0x65, 0x36, 0x38, 0x32, 0x64, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x39, 0x62, + 0x30, 0x35, 0x36, 0x38, 0x38, 0x63, 0x32, 0x62, 0x33, 0x65, 0x36, 0x63, + 0x31, 0x66, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x30, 0x78, 0x31, 0x66, + 0x38, 0x33, 0x64, 0x39, 0x61, 0x62, 0x66, 0x62, 0x34, 0x31, 0x62, 0x64, + 0x36, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x35, 0x62, 0x65, 0x30, 0x63, 0x64, + 0x31, 0x39, 0x31, 0x33, 0x37, 0x65, 0x32, 0x31, 0x37, 0x39, 0x2c, 0x0a, + 0x7d, 0x3b, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x52, 0x65, + 0x73, 0x65, 0x74, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, + 0x6e, 0x65, 0x6c, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x72, + 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x68, 0x74, 0x28, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x20, 0x2a, 0x68, 0x74, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x69, 0x64, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x5f, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x28, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, + 0x68, 0x74, 0x20, 0x2b, 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x4e, + 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, 0x53, 0x4c, + 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x29, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x49, 0x66, + 0x20, 0x78, 0x69, 0x30, 0x2c, 0x78, 0x69, 0x31, 0x2c, 0x78, 0x69, 0x32, + 0x2c, 0x78, 0x69, 0x33, 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x76, 0x65, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x74, + 0x74, 0x6c, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x79, 0x0a, 0x2a, 0x2a, 0x20, + 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x28, 0x68, + 0x65, 0x78, 0x20, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6f, 0x66, 0x20, 0x35, 0x20, + 0x68, 0x65, 0x78, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6f, + 0x66, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x20, 0x62, 0x69, 0x74, + 0x73, 0x29, 0x3a, 0x0a, 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x61, 0x61, 0x20, + 0x61, 0x61, 0x20, 0x61, 0x62, 0x20, 0x62, 0x62, 0x20, 0x62, 0x62, 0x20, + 0x63, 0x63, 0x20, 0x63, 0x63, 0x20, 0x63, 0x64, 0x20, 0x64, 0x64, 0x2e, + 0x2e, 0x2e, 0x20, 0x20, 0x5b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x30, + 0x5d, 0x0a, 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2a, 0x2a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, 0x2e, 0x2e, 0x61, 0x62, 0x20, + 0x62, 0x62, 0x20, 0x62, 0x62, 0x20, 0x63, 0x63, 0x20, 0x63, 0x63, 0x20, + 0x63, 0x64, 0x20, 0x64, 0x64, 0x2e, 0x2e, 0x2e, 0x20, 0x20, 0x5b, 0x6f, + 0x64, 0x64, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5d, 0x0a, 0x2a, 0x2a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2e, + 0x2e, 0x2e, 0x63, 0x63, 0x20, 0x63, 0x63, 0x20, 0x63, 0x64, 0x20, 0x64, + 0x64, 0x2e, 0x2e, 0x2e, 0x20, 0x20, 0x5b, 0x6e, 0x65, 0x78, 0x74, 0x20, + 0x65, 0x76, 0x65, 0x6e, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5d, 0x0a, + 0x2a, 0x2a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x2a, 0x2a, 0x20, 0x42, + 0x79, 0x74, 0x65, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x69, + 0x6e, 0x65, 0x64, 0x20, 0x61, 0x72, 0x65, 0x20, 0x67, 0x6f, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6c, + 0x6f, 0x74, 0x2e, 0x20, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x69, 0x6e, + 0x67, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x2a, 0x2a, 0x20, 0x28, + 0x61, 0x6e, 0x64, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79, + 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x69, 0x6e, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x2c, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x4e, 0x52, 0x5f, 0x52, + 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x29, 0x20, 0x61, 0x72, 0x65, + 0x0a, 0x2a, 0x2a, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x0a, + 0x2a, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x30, 0x3a, 0x20, 0x78, 0x69, 0x30, 0x2c, 0x78, 0x69, 0x31, 0x2c, 0x78, + 0x69, 0x32, 0x2c, 0x78, 0x69, 0x33, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, + 0x32, 0x35, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, + 0x78, 0x69, 0x33, 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x6d, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x31, 0x3a, 0x20, 0x78, 0x69, 0x30, 0x2c, 0x78, + 0x69, 0x31, 0x2c, 0x78, 0x69, 0x32, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, + 0x32, 0x33, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, + 0x69, 0x6e, 0x63, 0x6c, 0x2e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6c, 0x6c, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x52, 0x45, 0x46, + 0x49, 0x58, 0x20, 0x6e, 0x69, 0x62, 0x62, 0x6c, 0x65, 0x29, 0x0a, 0x2a, + 0x2a, 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x62, 0x65, 0x6c, + 0x6f, 0x77, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x61, 0x64, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x69, 0x62, 0x62, 0x6c, 0x65, 0x73, 0x0a, + 0x2a, 0x2a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x32, 0x3a, 0x20, + 0x78, 0x69, 0x30, 0x2c, 0x78, 0x69, 0x31, 0x2c, 0x78, 0x69, 0x32, 0x20, + 0x69, 0x73, 0x20, 0x61, 0x20, 0x32, 0x30, 0x2d, 0x62, 0x79, 0x74, 0x65, + 0x20, 0x58, 0x69, 0x20, 0x28, 0x78, 0x69, 0x32, 0x3a, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x34, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x29, 0x0a, 0x2a, 0x2a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x33, 0x3a, 0x20, 0x78, 0x69, 0x30, 0x2c, 0x78, 0x69, 0x31, 0x2c, 0x78, + 0x69, 0x32, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x31, 0x37, 0x2e, 0x35, + 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, 0x78, 0x69, + 0x32, 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6c, 0x6f, 0x77, 0x20, 0x31, 0x2e, 0x35, 0x20, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, 0x2a, 0x2a, + 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x34, 0x3a, 0x20, 0x78, 0x69, + 0x30, 0x2c, 0x78, 0x69, 0x31, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x31, + 0x35, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, 0x78, + 0x69, 0x31, 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x37, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, 0x2a, 0x2a, 0x20, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x35, 0x3a, 0x20, 0x78, 0x69, 0x30, + 0x2c, 0x78, 0x69, 0x31, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x31, 0x32, + 0x2e, 0x35, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, + 0x78, 0x69, 0x31, 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x34, 0x2e, 0x35, 0x20, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, + 0x2a, 0x2a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x36, 0x3a, 0x20, + 0x78, 0x69, 0x30, 0x2c, 0x78, 0x69, 0x31, 0x20, 0x69, 0x73, 0x20, 0x61, + 0x20, 0x31, 0x30, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, + 0x28, 0x78, 0x69, 0x31, 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x32, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, 0x2a, + 0x2a, 0x20, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x37, 0x3a, 0x20, 0x78, + 0x69, 0x30, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x37, 0x2e, 0x35, 0x2d, + 0x62, 0x79, 0x74, 0x65, 0x20, 0x58, 0x69, 0x20, 0x28, 0x78, 0x69, 0x30, + 0x3a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, + 0x6f, 0x77, 0x20, 0x37, 0x2e, 0x35, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x29, 0x0a, 0x2a, 0x2a, 0x20, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x38, 0x3a, 0x20, 0x78, 0x69, 0x30, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x35, 0x2d, 0x62, 0x79, 0x74, 0x65, + 0x20, 0x58, 0x69, 0x20, 0x28, 0x78, 0x69, 0x30, 0x3a, 0x20, 0x6f, 0x6e, + 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x77, 0x20, 0x35, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x74, 0x65, + 0x72, 0x29, 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x30, 0x20, 0x69, 0x66, 0x20, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x20, 0x69, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x6f, 0x76, + 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x2e, 0x0a, 0x2a, 0x2f, + 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x74, 0x5f, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x2c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x69, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x78, 0x69, 0x30, 0x2c, + 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x78, 0x69, 0x31, 0x2c, 0x20, + 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x78, 0x69, 0x32, 0x2c, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x78, 0x69, 0x33, 0x29, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, 0x72, 0x6f, + 0x77, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x2a, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x0a, + 0x23, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, + 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x36, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x0a, 0x09, 0x72, 0x6f, 0x77, + 0x20, 0x3d, 0x20, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x66, 0x66, 0x66, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x69, 0x66, 0x20, 0x77, + 0x65, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x68, 0x65, + 0x78, 0x3a, 0x20, 0x22, 0x61, 0x62, 0x20, 0x63, 0x64, 0x20, 0x65, 0x66, + 0x2e, 0x2e, 0x2e, 0x22, 0x20, 0x28, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, + 0x20, 0x65, 0x6e, 0x64, 0x69, 0x61, 0x6e, 0x20, 0x78, 0x69, 0x30, 0x29, + 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a, 0x09, + 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x75, 0x6c, 0x61, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x6f, 0x77, 0x20, 0x61, 0x73, 0x20, 0x30, 0x78, 0x64, 0x65, 0x62, + 0x63, 0x2e, 0x20, 0x69, 0x74, 0x20, 0x73, 0x6b, 0x69, 0x70, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x27, 0x61, 0x27, 0x20, 0x6e, 0x69, 0x62, 0x62, + 0x6c, 0x65, 0x20, 0x61, 0x73, 0x20, 0x69, 0x74, 0x0a, 0x09, 0x2f, 0x2f, + 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x2e, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x58, 0x69, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x62, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, + 0x22, 0x65, 0x66, 0x2e, 0x2e, 0x2e, 0x22, 0x3b, 0x0a, 0x09, 0x2f, 0x2f, + 0x20, 0x27, 0x65, 0x27, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, + 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x27, 0x66, 0x27, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x0a, 0x09, 0x72, + 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, + 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x34, + 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, + 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x20, 0x3e, 0x3e, + 0x20, 0x31, 0x32, 0x29, 0x20, 0x7c, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, + 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x29, + 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, + 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x29, + 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, + 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, + 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x38, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x20, + 0x3d, 0x20, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, + 0x66, 0x66, 0x66, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, + 0x20, 0x26, 0x20, 0x30, 0x78, 0x63, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x20, 0x3e, 0x3e, 0x20, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x20, 0x3d, 0x20, + 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x63, 0x30, + 0x30, 0x30, 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x32, 0x29, 0x20, 0x7c, + 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, + 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x3c, 0x20, + 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, + 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x20, 0x3e, + 0x3e, 0x20, 0x31, 0x32, 0x29, 0x20, 0x7c, 0x0a, 0x09, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, + 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, + 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, + 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x3b, 0x0a, 0x23, 0x65, + 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, + 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x39, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x0a, 0x09, 0x72, 0x6f, 0x77, + 0x20, 0x3d, 0x20, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x66, 0x66, 0x66, 0x66, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, + 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x65, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x20, 0x3d, + 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x65, + 0x30, 0x30, 0x30, 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x29, 0x20, + 0x7c, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, + 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x29, 0x20, 0x3c, 0x3c, + 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, + 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, 0x20, + 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x20, 0x7c, 0x0a, 0x09, 0x20, 0x20, + 0x20, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x66, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, + 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x3b, 0x0a, 0x23, + 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, + 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x30, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x28, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x0a, 0x09, 0x72, 0x6f, + 0x77, 0x20, 0x3d, 0x20, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x66, 0x66, 0x66, 0x66, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, + 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x34, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x72, 0x6f, 0x77, 0x20, + 0x3d, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x66, 0x30, 0x30, 0x30, 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x30, 0x29, + 0x20, 0x7c, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x78, 0x69, + 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x29, 0x20, 0x3c, + 0x3c, 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, + 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0x29, + 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x20, 0x7c, 0x0a, 0x09, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x66, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x34, 0x29, 0x20, 0x7c, 0x20, + 0x28, 0x28, 0x78, 0x69, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x30, + 0x30, 0x30, 0x29, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x32, 0x29, 0x3b, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, + 0x47, 0x22, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x78, 0x69, 0x30, 0x20, 0x3d, 0x20, 0x28, 0x78, 0x69, 0x30, + 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x36, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x78, + 0x69, 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2d, 0x20, + 0x31, 0x36, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x69, + 0x31, 0x20, 0x3d, 0x20, 0x28, 0x78, 0x69, 0x31, 0x20, 0x3e, 0x3e, 0x20, + 0x31, 0x36, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x78, 0x69, 0x32, 0x20, 0x3c, + 0x3c, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x31, 0x36, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x69, 0x32, 0x20, 0x3d, 0x20, + 0x28, 0x78, 0x69, 0x32, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x36, 0x29, 0x20, + 0x7c, 0x20, 0x28, 0x78, 0x69, 0x33, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x36, + 0x34, 0x20, 0x2d, 0x20, 0x31, 0x36, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x68, 0x74, 0x20, 0x2b, 0x20, 0x72, + 0x6f, 0x77, 0x20, 0x2a, 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, + 0x53, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6e, 0x74, 0x20, 0x3d, 0x20, + 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x63, 0x28, 0x28, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x29, 0x70, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x63, 0x6e, 0x74, 0x20, 0x3e, 0x3d, 0x20, 0x4e, + 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x20, 0x2b, 0x3d, 0x20, + 0x63, 0x6e, 0x74, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, + 0x45, 0x4e, 0x20, 0x2b, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x22, 0x69, + 0x22, 0x20, 0x28, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x34, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, + 0x20, 0x58, 0x69, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x28, 0x5f, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, 0x2d, 0x20, 0x34, 0x29, 0x20, 0x3d, + 0x20, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x20, 0x7c, + 0x7c, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x31, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, + 0x2f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x32, 0x34, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, + 0x29, 0x28, 0x70, 0x20, 0x2b, 0x20, 0x30, 0x29, 0x20, 0x3d, 0x20, 0x78, + 0x69, 0x30, 0x3b, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, + 0x28, 0x70, 0x20, 0x2b, 0x20, 0x38, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, + 0x31, 0x3b, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, + 0x70, 0x20, 0x2b, 0x20, 0x31, 0x36, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x32, 0x30, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, + 0x70, 0x20, 0x2b, 0x20, 0x30, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x30, + 0x3b, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, 0x70, + 0x20, 0x2b, 0x20, 0x38, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x31, 0x3b, + 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, 0x2b, + 0x20, 0x31, 0x36, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x32, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x20, 0x31, 0x36, 0x20, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, 0x70, + 0x20, 0x2b, 0x20, 0x30, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x30, 0x3b, + 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, + 0x2b, 0x20, 0x38, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x31, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x35, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x20, 0x31, 0x32, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x0a, 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, + 0x2b, 0x20, 0x30, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x30, 0x3b, 0x0a, + 0x09, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, 0x2b, 0x20, + 0x38, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x31, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, + 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x20, 0x3d, 0x3d, 0x20, 0x36, 0x20, 0x7c, 0x7c, 0x20, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x37, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x20, 0x38, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, + 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, 0x2b, 0x20, + 0x30, 0x29, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, + 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x20, 0x3d, 0x3d, 0x20, 0x38, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x20, 0x34, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, 0x2a, 0x28, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x29, 0x28, 0x70, 0x20, 0x2b, 0x20, 0x30, 0x29, 0x20, + 0x3d, 0x20, 0x78, 0x69, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x61, 0x2c, 0x20, + 0x76, 0x62, 0x2c, 0x20, 0x76, 0x63, 0x2c, 0x20, 0x76, 0x64, 0x2c, 0x20, + 0x78, 0x2c, 0x20, 0x79, 0x29, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x61, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x61, 0x20, 0x2b, 0x20, 0x76, + 0x62, 0x20, 0x2b, 0x20, 0x78, 0x29, 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x64, 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, + 0x65, 0x28, 0x28, 0x76, 0x64, 0x20, 0x5e, 0x20, 0x76, 0x61, 0x29, 0x2c, + 0x20, 0x28, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x36, 0x34, 0x20, 0x2d, + 0x20, 0x33, 0x32, 0x29, 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x63, 0x20, 0x3d, 0x20, 0x28, 0x76, 0x63, 0x20, 0x2b, 0x20, 0x76, + 0x64, 0x29, 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x62, + 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x28, 0x76, + 0x62, 0x20, 0x5e, 0x20, 0x76, 0x63, 0x29, 0x2c, 0x20, 0x28, 0x75, 0x6c, + 0x6f, 0x6e, 0x67, 0x29, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x32, 0x34, 0x29, + 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x20, 0x3d, + 0x20, 0x28, 0x76, 0x61, 0x20, 0x2b, 0x20, 0x76, 0x62, 0x20, 0x2b, 0x20, + 0x79, 0x29, 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x64, + 0x20, 0x3d, 0x20, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x28, 0x76, + 0x64, 0x20, 0x5e, 0x20, 0x76, 0x61, 0x29, 0x2c, 0x20, 0x28, 0x75, 0x6c, + 0x6f, 0x6e, 0x67, 0x29, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x31, 0x36, 0x29, + 0x3b, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x63, 0x20, 0x3d, + 0x20, 0x28, 0x76, 0x63, 0x20, 0x2b, 0x20, 0x76, 0x64, 0x29, 0x3b, 0x20, + 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x62, 0x20, 0x3d, 0x20, 0x72, + 0x6f, 0x74, 0x61, 0x74, 0x65, 0x28, 0x28, 0x76, 0x62, 0x20, 0x5e, 0x20, + 0x76, 0x63, 0x29, 0x2c, 0x20, 0x28, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x29, + 0x36, 0x34, 0x20, 0x2d, 0x20, 0x36, 0x33, 0x29, 0x3b, 0x0a, 0x0a, 0x2f, + 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x30, 0x20, 0x28, 0x62, 0x6c, + 0x61, 0x6b, 0x65, 0x29, 0x2e, 0x0a, 0x2a, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, + 0x4e, 0x6f, 0x74, 0x65, 0x3a, 0x20, 0x6d, 0x61, 0x6b, 0x69, 0x6e, 0x67, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6c, 0x65, 0x73, + 0x73, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x71, + 0x75, 0x61, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x61, 0x76, 0x65, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x0a, 0x2a, 0x2a, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, + 0x72, 0x72, 0x69, 0x65, 0x72, 0x28, 0x29, 0x20, 0x63, 0x61, 0x6c, 0x6c, + 0x73, 0x2c, 0x20, 0x73, 0x65, 0x65, 0x20, 0x22, 0x32, 0x2e, 0x32, 0x20, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x0a, 0x2a, 0x2a, 0x20, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x20, 0x28, 0x4c, 0x44, 0x53, 0x29, 0x20, 0x4f, 0x70, + 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x32, + 0x2d, 0x31, 0x30, 0x22, 0x20, 0x69, 0x6e, 0x3a, 0x0a, 0x2a, 0x2a, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x72, 0x2e, 0x61, 0x6d, 0x64, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x73, + 0x64, 0x6b, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x2d, 0x7a, + 0x6f, 0x6e, 0x65, 0x2f, 0x61, 0x6d, 0x64, 0x2d, 0x61, 0x63, 0x63, 0x65, + 0x6c, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x70, 0x61, 0x72, 0x61, + 0x6c, 0x6c, 0x65, 0x6c, 0x2d, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x2d, 0x61, 0x70, 0x70, 0x2d, 0x73, 0x64, 0x6b, 0x2f, + 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x67, 0x75, 0x69, 0x64, + 0x65, 0x2f, 0x0a, 0x2a, 0x2f, 0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x20, 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, 0x36, 0x34, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, + 0x29, 0x29, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x30, 0x28, 0x5f, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x2a, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x2c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x64, 0x20, 0x3d, 0x20, + 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, + 0x6f, 0x6e, 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x36, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x3d, 0x20, 0x4e, 0x52, 0x5f, + 0x49, 0x4e, 0x50, 0x55, 0x54, 0x53, 0x20, 0x2f, 0x20, 0x67, 0x65, 0x74, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x3d, + 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x20, + 0x3d, 0x20, 0x28, 0x74, 0x69, 0x64, 0x20, 0x2b, 0x20, 0x31, 0x29, 0x20, + 0x2a, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x5f, 0x70, 0x65, 0x72, + 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x72, 0x6f, + 0x70, 0x70, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x20, 0x3c, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x65, + 0x6e, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x73, + 0x68, 0x69, 0x66, 0x74, 0x20, 0x22, 0x69, 0x22, 0x20, 0x74, 0x6f, 0x20, + 0x6f, 0x63, 0x63, 0x75, 0x70, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, + 0x69, 0x67, 0x68, 0x20, 0x33, 0x32, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x6f, 0x72, 0x64, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, + 0x6f, 0x72, 0x64, 0x31, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x6c, 0x6f, 0x6e, + 0x67, 0x29, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x3c, 0x3c, 0x20, 0x33, + 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x20, 0x76, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x20, 0x76, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x5d, 0x20, + 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5b, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, + 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x32, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x33, + 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5b, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x62, + 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x34, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x20, 0x3d, + 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5b, 0x36, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, + 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x37, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x20, 0x3d, 0x20, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, + 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x20, 0x3d, 0x20, 0x20, 0x62, 0x6c, 0x61, + 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x20, + 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, 0x32, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, + 0x5f, 0x69, 0x76, 0x5b, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x20, 0x3d, 0x20, + 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, 0x34, 0x5d, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, + 0x33, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, + 0x76, 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, + 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, 0x36, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, + 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x69, 0x76, 0x5b, + 0x37, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x6d, 0x69, 0x78, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x5b, 0x31, + 0x32, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, + 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, + 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x2b, 0x20, 0x34, 0x20, 0x2f, 0x2a, 0x20, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x22, 0x69, + 0x22, 0x20, 0x2a, 0x2f, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x20, 0x5e, 0x3d, 0x20, 0x2d, 0x31, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x31, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, + 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x32, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x31, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x33, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, + 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x34, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x77, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, + 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x35, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x36, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, + 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x34, + 0x5d, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x37, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x64, 0x31, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, + 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x38, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, + 0x64, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x39, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, + 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x31, 0x30, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x35, 0x5d, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x2c, 0x20, 0x30, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x69, 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, + 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, + 0x76, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, + 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x38, + 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, 0x30, + 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, + 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x31, 0x31, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, + 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, + 0x76, 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, + 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x32, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, 0x2c, + 0x20, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x31, 0x32, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x32, + 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, + 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x39, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, 0x20, + 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x32, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, + 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, 0x2c, 0x20, 0x76, 0x5b, + 0x37, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x31, 0x35, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, + 0x28, 0x76, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x2c, + 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x35, + 0x5d, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x31, 0x2c, 0x20, 0x30, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, + 0x78, 0x28, 0x76, 0x5b, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x36, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x31, + 0x32, 0x5d, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, + 0x5b, 0x32, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x37, 0x5d, 0x2c, 0x20, 0x76, + 0x5b, 0x38, 0x5d, 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x33, 0x5d, 0x2c, + 0x20, 0x30, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x76, 0x5b, 0x33, 0x5d, + 0x2c, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x2c, 0x20, 0x76, 0x5b, 0x39, 0x5d, + 0x2c, 0x20, 0x20, 0x76, 0x5b, 0x31, 0x34, 0x5d, 0x2c, 0x20, 0x30, 0x2c, + 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x20, 0x76, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x3b, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x35, 0x30, 0x2d, 0x62, + 0x79, 0x74, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x28, 0x74, 0x77, 0x6f, + 0x20, 0x58, 0x69, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, 0x6e, + 0x67, 0x20, 0x68, 0x5b, 0x37, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x62, + 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x30, + 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x30, 0x5d, 0x20, 0x5e, 0x20, 0x76, + 0x5b, 0x38, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x68, 0x5b, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, + 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x31, 0x5d, 0x20, 0x5e, + 0x20, 0x76, 0x5b, 0x31, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x39, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, + 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x5b, 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, + 0x32, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x31, 0x30, 0x5d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x33, 0x5d, + 0x20, 0x3d, 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5b, 0x33, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x33, 0x5d, + 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x31, 0x31, 0x5d, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x34, 0x5d, 0x20, 0x3d, + 0x20, 0x62, 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5b, 0x34, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x34, 0x5d, 0x20, 0x5e, + 0x20, 0x76, 0x5b, 0x31, 0x32, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x35, 0x5d, 0x20, 0x3d, 0x20, 0x62, + 0x6c, 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x35, + 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x35, 0x5d, 0x20, 0x5e, 0x20, 0x76, + 0x5b, 0x31, 0x33, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x68, 0x5b, 0x36, 0x5d, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x6c, + 0x61, 0x6b, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5b, 0x36, 0x5d, + 0x20, 0x5e, 0x20, 0x76, 0x5b, 0x36, 0x5d, 0x20, 0x5e, 0x20, 0x76, 0x5b, + 0x31, 0x34, 0x5d, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, 0x66, + 0x66, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x74, 0x77, 0x6f, 0x20, 0x58, 0x69, 0x20, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, + 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x5f, + 0x4c, 0x45, 0x4e, 0x20, 0x3d, 0x3d, 0x20, 0x35, 0x30, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, + 0x64, 0x20, 0x2b, 0x3d, 0x20, 0x68, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x30, 0x2c, 0x20, 0x68, 0x74, 0x2c, 0x20, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x20, 0x2a, 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x68, 0x5b, 0x30, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, + 0x31, 0x5d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x32, 0x5d, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x5b, 0x33, 0x5d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x72, 0x6f, + 0x70, 0x70, 0x65, 0x64, 0x20, 0x2b, 0x3d, 0x20, 0x68, 0x74, 0x5f, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x28, 0x30, 0x2c, 0x20, 0x68, 0x74, 0x2c, 0x20, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, + 0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x5b, 0x33, 0x5d, + 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x68, 0x5b, + 0x34, 0x5d, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x36, 0x34, 0x20, 0x2d, 0x20, + 0x38, 0x29, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x5b, + 0x34, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x68, 0x5b, 0x35, 0x5d, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x36, 0x34, 0x20, + 0x2d, 0x20, 0x38, 0x29, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, + 0x68, 0x5b, 0x35, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x20, 0x7c, + 0x20, 0x28, 0x68, 0x5b, 0x36, 0x5d, 0x20, 0x3c, 0x3c, 0x20, 0x28, 0x36, + 0x34, 0x20, 0x2d, 0x20, 0x38, 0x29, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x28, 0x68, 0x5b, 0x36, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x38, 0x29, + 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x20, 0x5a, 0x43, 0x41, 0x53, 0x48, 0x5f, 0x48, + 0x41, 0x53, 0x48, 0x5f, 0x4c, 0x45, 0x4e, 0x22, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x64, 0x65, 0x66, + 0x20, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x44, 0x45, 0x42, 0x55, + 0x47, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5b, + 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5b, + 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2b, 0x20, 0x31, 0x5d, + 0x20, 0x3d, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x3b, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x7d, 0x0a, 0x0a, 0x23, 0x69, + 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, + 0x47, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x36, 0x20, 0x26, 0x26, 0x20, 0x4e, + 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x3c, 0x3d, 0x20, 0x28, + 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x38, 0x29, 0x0a, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x5f, + 0x49, 0x4e, 0x50, 0x55, 0x54, 0x53, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x73, 0x6c, 0x6f, 0x74, 0x30, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x31, + 0x29, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x72, 0x6f, + 0x77, 0x20, 0x3c, 0x3c, 0x20, 0x31, 0x36, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x28, 0x73, 0x6c, 0x6f, 0x74, 0x31, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, + 0x66, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x38, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x73, 0x6c, 0x6f, 0x74, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, + 0x29, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, + 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, 0x52, 0x45, + 0x46, 0x29, 0x09, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x3e, 0x3e, 0x20, + 0x31, 0x36, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x31, + 0x28, 0x52, 0x45, 0x46, 0x29, 0x09, 0x28, 0x28, 0x52, 0x45, 0x46, 0x20, + 0x3e, 0x3e, 0x20, 0x38, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x66, 0x66, + 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, 0x45, + 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x30, 0x28, 0x52, + 0x45, 0x46, 0x29, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x66, 0x66, 0x29, 0x0a, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, + 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, + 0x3d, 0x3d, 0x20, 0x31, 0x38, 0x20, 0x26, 0x26, 0x20, 0x4e, 0x52, 0x5f, + 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x3c, 0x3d, 0x20, 0x28, 0x31, 0x20, + 0x3c, 0x3c, 0x20, 0x37, 0x29, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x20, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, + 0x50, 0x55, 0x54, 0x53, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x73, 0x6c, + 0x6f, 0x74, 0x30, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x31, 0x29, 0x20, + 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x72, 0x6f, 0x77, 0x20, + 0x3c, 0x3c, 0x20, 0x31, 0x34, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x73, + 0x6c, 0x6f, 0x74, 0x31, 0x20, 0x26, 0x20, 0x30, 0x78, 0x37, 0x66, 0x29, + 0x20, 0x3c, 0x3c, 0x20, 0x37, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x73, 0x6c, + 0x6f, 0x74, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x37, 0x66, 0x29, 0x29, + 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, 0x45, 0x43, + 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, 0x52, 0x45, 0x46, 0x29, + 0x09, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x34, + 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, 0x45, + 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x31, 0x28, 0x52, + 0x45, 0x46, 0x29, 0x09, 0x28, 0x28, 0x52, 0x45, 0x46, 0x20, 0x3e, 0x3e, + 0x20, 0x37, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x37, 0x66, 0x29, 0x0a, + 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, 0x45, 0x43, 0x4f, + 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x30, 0x28, 0x52, 0x45, 0x46, + 0x29, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x26, 0x20, 0x30, 0x78, 0x37, + 0x66, 0x29, 0x0a, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, + 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, + 0x20, 0x31, 0x39, 0x20, 0x26, 0x26, 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, + 0x4f, 0x54, 0x53, 0x20, 0x3c, 0x3d, 0x20, 0x28, 0x31, 0x20, 0x3c, 0x3c, + 0x20, 0x36, 0x29, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x50, 0x55, + 0x54, 0x53, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, + 0x30, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x31, 0x29, 0x20, 0x5c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x72, 0x6f, 0x77, 0x20, 0x3c, 0x3c, + 0x20, 0x31, 0x33, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, 0x73, 0x6c, 0x6f, + 0x74, 0x31, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, 0x66, 0x29, 0x20, 0x3c, + 0x3c, 0x20, 0x36, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x73, 0x6c, 0x6f, 0x74, + 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, 0x66, 0x29, 0x29, 0x20, 0x2f, + 0x2a, 0x20, 0x31, 0x20, 0x73, 0x70, 0x61, 0x72, 0x65, 0x20, 0x62, 0x69, + 0x74, 0x20, 0x2a, 0x2f, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x20, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, + 0x52, 0x45, 0x46, 0x29, 0x09, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x3e, + 0x3e, 0x20, 0x31, 0x33, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x20, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, + 0x54, 0x31, 0x28, 0x52, 0x45, 0x46, 0x29, 0x09, 0x28, 0x28, 0x52, 0x45, + 0x46, 0x20, 0x3e, 0x3e, 0x20, 0x36, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, + 0x33, 0x66, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x30, + 0x28, 0x52, 0x45, 0x46, 0x29, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x26, + 0x20, 0x30, 0x78, 0x33, 0x66, 0x29, 0x0a, 0x0a, 0x23, 0x65, 0x6c, 0x69, + 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, + 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x30, 0x20, 0x26, 0x26, 0x20, 0x4e, + 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x3c, 0x3d, 0x20, 0x28, + 0x31, 0x20, 0x3c, 0x3c, 0x20, 0x36, 0x29, 0x0a, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x20, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x5f, + 0x49, 0x4e, 0x50, 0x55, 0x54, 0x53, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x73, 0x6c, 0x6f, 0x74, 0x30, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x31, + 0x29, 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x72, 0x6f, + 0x77, 0x20, 0x3c, 0x3c, 0x20, 0x31, 0x32, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x28, 0x73, 0x6c, 0x6f, 0x74, 0x31, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, + 0x66, 0x29, 0x20, 0x3c, 0x3c, 0x20, 0x36, 0x29, 0x20, 0x7c, 0x20, 0x28, + 0x73, 0x6c, 0x6f, 0x74, 0x30, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, 0x66, + 0x29, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, + 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, 0x52, 0x45, + 0x46, 0x29, 0x09, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x3e, 0x3e, 0x20, + 0x31, 0x32, 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, + 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x31, + 0x28, 0x52, 0x45, 0x46, 0x29, 0x09, 0x28, 0x28, 0x52, 0x45, 0x46, 0x20, + 0x3e, 0x3e, 0x20, 0x36, 0x29, 0x20, 0x26, 0x20, 0x30, 0x78, 0x33, 0x66, + 0x29, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x44, 0x45, + 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x30, 0x28, 0x52, + 0x45, 0x46, 0x29, 0x09, 0x28, 0x52, 0x45, 0x46, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x33, 0x66, 0x29, 0x0a, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x23, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x52, + 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x22, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x58, + 0x4f, 0x52, 0x20, 0x61, 0x20, 0x70, 0x61, 0x69, 0x72, 0x20, 0x6f, 0x66, + 0x20, 0x58, 0x69, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, + 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, + 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x0a, 0x2a, 0x2a, 0x0a, + 0x2a, 0x2a, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x20, + 0x69, 0x66, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, + 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x2c, 0x20, + 0x6f, 0x72, 0x20, 0x31, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x72, 0x6f, 0x77, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x78, 0x6f, 0x72, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, + 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x5f, 0x64, 0x73, 0x74, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x77, 0x2c, 0x0a, 0x09, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x61, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x62, + 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, + 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x61, 0x2c, 0x20, 0x5f, 0x5f, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, + 0x2a, 0x62, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, + 0x6f, 0x6e, 0x67, 0x09, 0x78, 0x69, 0x30, 0x2c, 0x20, 0x78, 0x69, 0x31, + 0x2c, 0x20, 0x78, 0x69, 0x32, 0x3b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x4e, + 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3e, + 0x3d, 0x20, 0x31, 0x36, 0x20, 0x26, 0x26, 0x20, 0x4e, 0x52, 0x5f, 0x52, + 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3c, 0x3d, 0x20, 0x32, + 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x6f, 0x74, + 0x65, 0x3a, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, + 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x30, + 0x2c, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x64, 0x64, 0x20, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x73, 0x2c, 0x20, 0x77, 0x65, 0x20, 0x63, 0x6f, 0x75, + 0x6c, 0x64, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x20, + 0x62, 0x79, 0x20, 0x6e, 0x6f, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x69, 0x74, 0x73, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x20, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x4e, 0x6f, 0x74, 0x65, + 0x3a, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x4e, 0x20, 0x78, 0x6f, + 0x72, 0x73, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x4e, 0x2d, 0x31, 0x0a, + 0x09, 0x2f, 0x2f, 0x20, 0x78, 0x6f, 0x72, 0x20, 0x32, 0x34, 0x20, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, 0x78, 0x69, 0x30, 0x20, 0x3d, 0x20, + 0x2a, 0x28, 0x61, 0x2b, 0x2b, 0x29, 0x20, 0x5e, 0x20, 0x2a, 0x28, 0x62, + 0x2b, 0x2b, 0x29, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x31, 0x20, 0x3d, 0x20, + 0x2a, 0x28, 0x61, 0x2b, 0x2b, 0x29, 0x20, 0x5e, 0x20, 0x2a, 0x28, 0x62, + 0x2b, 0x2b, 0x29, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x32, 0x20, 0x3d, 0x20, + 0x2a, 0x61, 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, + 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x78, 0x6f, 0x72, 0x20, 0x32, 0x30, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, 0x78, 0x69, 0x30, 0x20, + 0x3d, 0x20, 0x2a, 0x61, 0x2b, 0x2b, 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x2b, + 0x2b, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x31, 0x20, 0x3d, 0x20, 0x2a, 0x61, + 0x2b, 0x2b, 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x2b, 0x2b, 0x3b, 0x0a, 0x09, + 0x78, 0x69, 0x32, 0x20, 0x3d, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, + 0x61, 0x20, 0x5e, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x62, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x7c, 0x7c, 0x20, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x35, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, + 0x78, 0x6f, 0x72, 0x20, 0x31, 0x36, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x0a, 0x09, 0x78, 0x69, 0x30, 0x20, 0x3d, 0x20, 0x2a, 0x61, 0x2b, 0x2b, + 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x2b, 0x2b, 0x3b, 0x0a, 0x09, 0x78, 0x69, + 0x31, 0x20, 0x3d, 0x20, 0x2a, 0x61, 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x3b, + 0x0a, 0x09, 0x78, 0x69, 0x32, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, + 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x36, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x78, 0x6f, 0x72, 0x20, + 0x31, 0x32, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x09, 0x78, 0x69, + 0x30, 0x20, 0x3d, 0x20, 0x2a, 0x61, 0x2b, 0x2b, 0x20, 0x5e, 0x20, 0x2a, + 0x62, 0x2b, 0x2b, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x31, 0x20, 0x3d, 0x20, + 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x61, 0x20, 0x5e, 0x20, 0x2a, 0x28, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x29, 0x62, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x32, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x37, + 0x20, 0x7c, 0x7c, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x3d, + 0x20, 0x38, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, + 0x09, 0x2f, 0x2f, 0x20, 0x78, 0x6f, 0x72, 0x20, 0x38, 0x20, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x0a, 0x09, 0x78, 0x69, 0x30, 0x20, 0x3d, 0x20, 0x2a, + 0x61, 0x20, 0x5e, 0x20, 0x2a, 0x62, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x31, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x09, 0x78, 0x69, 0x32, 0x20, 0x3d, + 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x20, 0x28, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x20, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x6e, 0x69, 0x6e, 0x67, + 0x20, 0x69, 0x6e, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x35, 0x29, + 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x78, 0x6f, + 0x72, 0x20, 0x74, 0x6f, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x2c, 0x20, 0x73, + 0x6f, 0x20, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x20, 0x74, 0x68, + 0x65, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, + 0x78, 0x69, 0x30, 0x20, 0x26, 0x26, 0x20, 0x21, 0x78, 0x69, 0x31, 0x29, + 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x30, 0x3b, 0x0a, + 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, + 0x47, 0x22, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x74, 0x5f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2c, + 0x20, 0x68, 0x74, 0x5f, 0x64, 0x73, 0x74, 0x2c, 0x20, 0x45, 0x4e, 0x43, + 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x53, 0x28, 0x72, + 0x6f, 0x77, 0x2c, 0x20, 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x61, 0x2c, 0x20, + 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x62, 0x29, 0x2c, 0x0a, 0x09, 0x20, 0x20, + 0x20, 0x20, 0x78, 0x69, 0x30, 0x2c, 0x20, 0x78, 0x69, 0x31, 0x2c, 0x20, + 0x78, 0x69, 0x32, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x45, 0x71, 0x75, 0x69, 0x68, 0x61, + 0x73, 0x68, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x20, 0x52, 0x65, + 0x61, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x68, 0x74, 0x5f, 0x73, + 0x72, 0x63, 0x2c, 0x20, 0x58, 0x4f, 0x52, 0x20, 0x63, 0x6f, 0x6c, 0x6c, + 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x58, 0x69, 0x2c, 0x0a, 0x2a, 0x2a, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, + 0x68, 0x74, 0x5f, 0x64, 0x73, 0x74, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x65, 0x71, 0x75, 0x69, 0x68, 0x61, 0x73, 0x68, + 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, + 0x5f, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x5f, + 0x64, 0x73, 0x74, 0x2c, 0x0a, 0x09, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x64, 0x20, 0x3d, 0x20, + 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x09, 0x09, 0x74, 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, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x2a, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6e, 0x74, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x09, 0x09, + 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, + 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x09, 0x09, 0x6d, 0x61, + 0x73, 0x6b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x2c, 0x20, 0x6a, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, + 0x53, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x20, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x28, + 0x62, 0x79, 0x20, 0x61, 0x20, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x20, + 0x6f, 0x66, 0x20, 0x4f, 0x56, 0x45, 0x52, 0x48, 0x45, 0x41, 0x44, 0x29, + 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x77, 0x65, 0x20, 0x77, 0x61, 0x6e, + 0x74, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x69, 0x74, 0x20, 0x74, 0x77, 0x69, 0x63, + 0x65, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x09, 0x09, 0x63, 0x6f, 0x6c, + 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x4e, 0x52, 0x5f, 0x53, + 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, 0x32, 0x5d, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, + 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x63, + 0x6f, 0x6c, 0x6c, 0x2c, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, + 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x6c, 0x6f, 0x6e, + 0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2a, 0x61, 0x2c, 0x20, 0x2a, + 0x62, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, + 0x09, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, + 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x20, + 0x6f, 0x66, 0x20, 0x58, 0x69, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, + 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, + 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x20, 0x3d, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, + 0x73, 0x6b, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x63, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x72, + 0x65, 0x61, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x23, 0x69, 0x66, + 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, + 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x36, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, + 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x21, 0x28, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x20, 0x3f, 0x20, + 0x30, 0x78, 0x30, 0x66, 0x20, 0x3a, 0x20, 0x30, 0x78, 0x66, 0x30, 0x29, + 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, + 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, + 0x38, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, + 0x20, 0x28, 0x28, 0x21, 0x28, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x25, + 0x20, 0x32, 0x29, 0x29, 0x20, 0x3f, 0x20, 0x30, 0x78, 0x30, 0x33, 0x20, + 0x3a, 0x20, 0x30, 0x78, 0x33, 0x30, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, + 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, + 0x4f, 0x47, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x39, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x28, 0x28, 0x21, 0x28, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x20, + 0x3f, 0x20, 0x30, 0x78, 0x30, 0x31, 0x20, 0x3a, 0x20, 0x30, 0x78, 0x31, + 0x30, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x69, 0x66, 0x20, 0x4e, 0x52, + 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, 0x3d, 0x3d, + 0x20, 0x32, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x73, 0x6b, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x2f, 0x2a, 0x20, 0x77, 0x65, 0x20, + 0x63, 0x61, 0x6e, 0x20, 0x76, 0x61, 0x73, 0x74, 0x6c, 0x79, 0x20, 0x73, + 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x64, 0x65, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x77, 0x20, 0x2a, + 0x2f, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x23, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, + 0x4c, 0x4f, 0x47, 0x22, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x20, 0x3d, 0x20, 0x28, 0x68, 0x74, 0x5f, + 0x73, 0x72, 0x63, 0x20, 0x2b, 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, + 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, 0x53, + 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x63, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x2a, 0x28, 0x5f, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, + 0x2a, 0x29, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6e, 0x74, + 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x63, 0x6e, 0x74, 0x2c, 0x20, + 0x28, 0x75, 0x69, 0x6e, 0x74, 0x29, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, + 0x54, 0x53, 0x29, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x68, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, + 0x6f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x20, 0x69, 0x6e, 0x20, + 0x70, 0x72, 0x65, 0x76, 0x2e, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x70, 0x20, 0x2b, 0x3d, 0x20, 0x78, 0x69, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, + 0x69, 0x20, 0x3c, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x20, 0x69, 0x2b, 0x2b, + 0x2c, 0x20, 0x70, 0x20, 0x2b, 0x3d, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, + 0x4c, 0x45, 0x4e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, + 0x29, 0x70, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x66, + 0x69, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x72, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x28, 0x6a, 0x20, 0x3d, 0x20, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x3b, 0x20, + 0x6a, 0x20, 0x3c, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x20, 0x6a, 0x2b, 0x2b, + 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x26, 0x20, + 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x20, 0x3d, 0x3d, 0x0a, 0x09, 0x09, 0x20, + 0x20, 0x20, 0x20, 0x28, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x77, 0x6f, + 0x72, 0x64, 0x73, 0x5b, 0x6a, 0x5d, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, + 0x6b, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x2f, 0x2f, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x21, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6e, 0x72, + 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x20, 0x3e, 0x3d, 0x20, 0x73, 0x69, 0x7a, + 0x65, 0x6f, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x29, 0x20, 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, + 0x66, 0x20, 0x28, 0x2a, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, + 0x6c, 0x2b, 0x2b, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, + 0x54, 0x53, 0x20, 0x3c, 0x3d, 0x20, 0x28, 0x31, 0x20, 0x3c, 0x3c, 0x20, + 0x38, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x6e, 0x6f, 0x74, 0x65, 0x3a, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x61, 0x73, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x20, 0x73, 0x6c, 0x6f, + 0x74, 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x38, 0x20, 0x62, + 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6e, + 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x2b, 0x2b, 0x5d, 0x20, 0x3d, 0x0a, + 0x09, 0x09, 0x09, 0x28, 0x28, 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x29, + 0x6a, 0x20, 0x3c, 0x3c, 0x20, 0x38, 0x29, 0x20, 0x7c, 0x20, 0x28, 0x28, + 0x75, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x29, 0x69, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x66, 0x66, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x23, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x53, + 0x4c, 0x4f, 0x54, 0x53, 0x22, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, + 0x64, 0x72, 0x6f, 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, + 0x69, 0x72, 0x65, 0x20, 0x30, 0x78, 0x41, 0x42, 0x20, 0x62, 0x79, 0x74, + 0x65, 0x20, 0x28, 0x73, 0x65, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x74, 0x6f, + 0x70, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, + 0x6c, 0x65, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x61, 0x64, 0x6a, 0x20, 0x3d, 0x20, 0x28, 0x21, 0x28, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x20, 0x25, 0x20, 0x32, 0x29, 0x29, 0x20, 0x3f, 0x20, + 0x31, 0x20, 0x3a, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, + 0x2f, 0x20, 0x58, 0x4f, 0x52, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x6f, 0x66, + 0x20, 0x58, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x72, 0x6f, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6e, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x6e, 0x20, 0x3c, 0x20, 0x6e, 0x72, + 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x3b, 0x20, 0x6e, 0x2b, 0x2b, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6c, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6e, 0x5d, 0x20, 0x26, 0x20, + 0x30, 0x78, 0x66, 0x66, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6a, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x6e, 0x5d, 0x20, 0x3e, 0x3e, 0x20, 0x38, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, + 0x3d, 0x20, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, + 0x5f, 0x73, 0x72, 0x63, 0x20, 0x2b, 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, + 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, + 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x2b, 0x20, 0x69, + 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x20, + 0x2b, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x0a, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2b, 0x20, 0x61, 0x64, 0x6a, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x20, + 0x3d, 0x20, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x2a, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x68, 0x74, + 0x5f, 0x73, 0x72, 0x63, 0x20, 0x2b, 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, + 0x20, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, + 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x2b, 0x20, 0x6a, + 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x20, + 0x2b, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x0a, + 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2b, 0x20, 0x61, 0x64, 0x6a, 0x29, + 0x3b, 0x0a, 0x09, 0x64, 0x72, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x73, + 0x74, 0x6f, 0x72, 0x20, 0x2b, 0x3d, 0x20, 0x78, 0x6f, 0x72, 0x5f, 0x61, + 0x6e, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x28, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x2c, 0x20, 0x68, 0x74, 0x5f, 0x64, 0x73, 0x74, 0x2c, 0x20, + 0x74, 0x69, 0x64, 0x2c, 0x20, 0x69, 0x2c, 0x20, 0x6a, 0x2c, 0x20, 0x61, + 0x2c, 0x20, 0x62, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x23, 0x69, 0x66, 0x64, 0x65, 0x66, 0x20, 0x45, 0x4e, 0x41, + 0x42, 0x4c, 0x45, 0x5f, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5b, 0x74, 0x69, 0x64, 0x20, + 0x2a, 0x20, 0x32, 0x5d, 0x20, 0x3d, 0x20, 0x64, 0x72, 0x6f, 0x70, 0x70, + 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5b, 0x74, 0x69, 0x64, 0x20, 0x2a, + 0x20, 0x32, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x64, 0x72, + 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x3b, 0x0a, + 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, + 0x0a, 0x2a, 0x2a, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x73, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x31, 0x2c, 0x20, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x32, 0x2c, 0x20, 0x2e, + 0x2e, 0x2e, 0x2c, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x38, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x23, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, + 0x5f, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x4e, 0x29, 0x20, 0x5c, 0x0a, + 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x20, 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, 0x36, 0x34, 0x2c, + 0x20, 0x31, 0x2c, 0x20, 0x31, 0x29, 0x29, 0x29, 0x20, 0x5c, 0x0a, 0x76, + 0x6f, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x23, 0x23, 0x20, 0x4e, 0x28, 0x5f, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, + 0x2a, 0x68, 0x74, 0x5f, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x5f, 0x5f, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, + 0x68, 0x74, 0x5f, 0x64, 0x73, 0x74, 0x2c, 0x20, 0x5c, 0x0a, 0x09, 0x5f, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x20, 0x2a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x29, 0x20, 0x5c, 0x0a, 0x7b, + 0x20, 0x5c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x71, 0x75, 0x69, 0x68, + 0x61, 0x73, 0x68, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x4e, 0x2c, + 0x20, 0x68, 0x74, 0x5f, 0x73, 0x72, 0x63, 0x2c, 0x20, 0x68, 0x74, 0x5f, + 0x64, 0x73, 0x74, 0x2c, 0x20, 0x64, 0x65, 0x62, 0x75, 0x67, 0x29, 0x3b, + 0x20, 0x5c, 0x0a, 0x7d, 0x0a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, + 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x31, 0x29, 0x0a, 0x4b, 0x45, 0x52, + 0x4e, 0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x32, 0x29, + 0x0a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x4e, + 0x44, 0x28, 0x33, 0x29, 0x0a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, + 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x34, 0x29, 0x0a, 0x4b, 0x45, 0x52, + 0x4e, 0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x35, 0x29, + 0x0a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x4e, + 0x44, 0x28, 0x36, 0x29, 0x0a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, + 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x37, 0x29, 0x0a, 0x4b, 0x45, 0x52, + 0x4e, 0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x28, 0x38, 0x29, + 0x0a, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x5f, 0x72, 0x65, 0x66, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, + 0x77, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x6c, 0x6f, 0x74, + 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, 0x68, 0x74, + 0x20, 0x2b, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x2a, 0x20, 0x4e, 0x52, 0x5f, + 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, + 0x5f, 0x4c, 0x45, 0x4e, 0x20, 0x2b, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x6c, 0x6f, 0x74, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, + 0x4c, 0x45, 0x4e, 0x20, 0x2b, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x20, 0x2d, 0x20, 0x34, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, + 0x5f, 0x72, 0x65, 0x66, 0x73, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x69, 0x6e, 0x73, + 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x72, 0x5f, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x2a, 0x68, 0x74, + 0x61, 0x62, 0x73, 0x2c, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x09, 0x2a, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x68, 0x74, 0x61, 0x62, + 0x73, 0x5b, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x25, 0x20, 0x32, 0x5d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, + 0x69, 0x20, 0x3d, 0x20, 0x6e, 0x72, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x09, 0x09, 0x6a, 0x20, 0x3d, 0x20, 0x6e, 0x72, 0x5f, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x20, 0x2a, 0x20, 0x32, 0x20, 0x2d, + 0x20, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, + 0x09, 0x09, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, + 0x3d, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, + 0x66, 0x6f, 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e, + 0x73, 0x5b, 0x6a, 0x5d, 0x20, 0x3d, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, + 0x64, 0x5f, 0x72, 0x65, 0x66, 0x28, 0x68, 0x74, 0x2c, 0x20, 0x78, 0x69, + 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x0a, 0x09, 0x09, 0x44, + 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, 0x69, 0x6e, + 0x73, 0x5b, 0x69, 0x5d, 0x29, 0x2c, 0x20, 0x44, 0x45, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x31, 0x28, 0x69, 0x6e, 0x73, 0x5b, + 0x69, 0x5d, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x5b, 0x6a, + 0x20, 0x2d, 0x20, 0x31, 0x5d, 0x20, 0x3d, 0x20, 0x65, 0x78, 0x70, 0x61, + 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x28, 0x68, 0x74, 0x2c, 0x20, 0x78, + 0x69, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c, 0x0a, 0x09, 0x09, + 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x4f, 0x57, 0x28, 0x69, + 0x6e, 0x73, 0x5b, 0x69, 0x5d, 0x29, 0x2c, 0x20, 0x44, 0x45, 0x43, 0x4f, + 0x44, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x30, 0x28, 0x69, 0x6e, 0x73, + 0x5b, 0x69, 0x5d, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, + 0x21, 0x69, 0x29, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x20, 0x3b, 0x0a, 0x09, 0x69, 0x2d, 0x2d, 0x3b, 0x0a, 0x09, + 0x6a, 0x20, 0x2d, 0x3d, 0x20, 0x32, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, + 0x65, 0x20, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, + 0x0a, 0x2a, 0x2a, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x20, 0x69, + 0x66, 0x20, 0x61, 0x20, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x20, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, + 0x73, 0x20, 0x69, 0x6e, 0x20, 0x66, 0x61, 0x63, 0x74, 0x20, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x2e, 0x0a, 0x2a, 0x2f, 0x0a, 0x76, 0x6f, 0x69, 0x64, + 0x20, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, + 0x6f, 0x6c, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x2a, 0x68, 0x74, 0x61, 0x62, 0x73, + 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x73, + 0x6f, 0x6c, 0x73, 0x5f, 0x74, 0x20, 0x2a, 0x73, 0x6f, 0x6c, 0x73, 0x2c, + 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x66, 0x30, 0x2c, + 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x66, 0x31, 0x29, 0x0a, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x73, + 0x6f, 0x6c, 0x5f, 0x69, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x09, 0x6e, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x20, + 0x3d, 0x20, 0x61, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x63, + 0x28, 0x26, 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x6e, 0x72, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x6f, 0x6c, + 0x5f, 0x69, 0x20, 0x3e, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x53, 0x4f, + 0x4c, 0x53, 0x29, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5b, 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x5d, + 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x72, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5b, 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x5d, + 0x5b, 0x6e, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2b, 0x2b, + 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x66, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x5b, 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x5d, 0x5b, 0x6e, 0x72, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2b, 0x2b, 0x5d, 0x20, 0x3d, + 0x20, 0x72, 0x65, 0x66, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x20, + 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4b, 0x20, 0x2d, 0x20, 0x31, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x7b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2d, 0x2d, + 0x3b, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x72, 0x65, + 0x66, 0x73, 0x28, 0x26, 0x28, 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5b, 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x5d, + 0x5b, 0x30, 0x5d, 0x29, 0x2c, 0x20, 0x6e, 0x72, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x2c, 0x20, 0x68, 0x74, 0x61, 0x62, 0x73, 0x2c, 0x20, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x6e, 0x72, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x2a, 0x3d, 0x20, 0x32, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5b, + 0x73, 0x6f, 0x6c, 0x5f, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x31, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a, 0x2f, 0x2a, 0x0a, 0x2a, 0x2a, 0x20, 0x53, 0x63, 0x61, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x69, 0x6e, + 0x64, 0x20, 0x45, 0x71, 0x75, 0x69, 0x68, 0x61, 0x73, 0x68, 0x20, 0x73, + 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x2a, 0x2f, + 0x0a, 0x5f, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x0a, 0x76, 0x6f, + 0x69, 0x64, 0x20, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x6f, + 0x6c, 0x73, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x68, 0x74, 0x30, 0x2c, 0x20, 0x5f, + 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, + 0x20, 0x2a, 0x68, 0x74, 0x31, 0x2c, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x73, 0x6f, 0x6c, 0x73, 0x5f, 0x74, 0x20, 0x2a, + 0x73, 0x6f, 0x6c, 0x73, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, 0x74, 0x69, 0x64, 0x20, 0x3d, 0x20, + 0x67, 0x65, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, + 0x64, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x09, + 0x2a, 0x68, 0x74, 0x61, 0x62, 0x73, 0x5b, 0x32, 0x5d, 0x20, 0x3d, 0x20, + 0x7b, 0x20, 0x68, 0x74, 0x30, 0x2c, 0x20, 0x68, 0x74, 0x31, 0x20, 0x7d, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, + 0x68, 0x74, 0x5f, 0x69, 0x20, 0x3d, 0x20, 0x28, 0x50, 0x41, 0x52, 0x41, + 0x4d, 0x5f, 0x4b, 0x20, 0x2d, 0x20, 0x31, 0x29, 0x20, 0x25, 0x20, 0x32, + 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, + 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x6c, 0x61, 0x73, + 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, 0x63, 0x6e, 0x74, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, 0x78, 0x69, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x78, 0x69, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x28, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x5f, 0x4b, + 0x20, 0x2d, 0x20, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x09, 0x09, 0x69, 0x2c, 0x20, 0x6a, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, + 0x63, 0x68, 0x61, 0x72, 0x09, 0x2a, 0x61, 0x2c, 0x20, 0x2a, 0x62, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, 0x09, 0x72, + 0x65, 0x66, 0x5f, 0x69, 0x2c, 0x20, 0x72, 0x65, 0x66, 0x5f, 0x6a, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x69, 0x74, 0x27, 0x73, + 0x20, 0x6f, 0x6b, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, + 0x6f, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2c, 0x20, 0x61, 0x73, 0x20, + 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x66, 0x69, 0x6c, 0x6c, 0x73, 0x20, + 0x75, 0x70, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, + 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x20, 0x28, 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x73, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x6c, 0x6f, + 0x6e, 0x67, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x5b, 0x35, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x3b, 0x0a, 0x23, + 0x69, 0x66, 0x20, 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, + 0x4f, 0x47, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x36, 0x20, 0x26, 0x26, 0x20, + 0x4e, 0x52, 0x5f, 0x52, 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x20, + 0x3c, 0x3d, 0x20, 0x32, 0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x2c, 0x20, 0x77, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6c, 0x6f, 0x6f, + 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x6f, 0x74, 0x68, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x69, 0x74, 0x73, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, + 0x73, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x20, 0x63, 0x6f, 0x6c, + 0x6c, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x69, 0x74, 0x73, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, + 0x74, 0x20, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x20, 0x62, 0x69, 0x74, + 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x09, + 0x09, 0x6d, 0x61, 0x73, 0x6b, 0x20, 0x3d, 0x20, 0x30, 0x78, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x3b, 0x0a, 0x23, 0x65, 0x6c, 0x73, 0x65, 0x0a, + 0x23, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x22, 0x75, 0x6e, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x4e, 0x52, 0x5f, 0x52, + 0x4f, 0x57, 0x53, 0x5f, 0x4c, 0x4f, 0x47, 0x22, 0x0a, 0x23, 0x65, 0x6e, + 0x64, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x74, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x09, 0x73, + 0x6f, 0x6c, 0x73, 0x2d, 0x3e, 0x6e, 0x72, 0x20, 0x3d, 0x20, 0x73, 0x6f, + 0x6c, 0x73, 0x2d, 0x3e, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x5f, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x73, 0x73, 0x20, 0x3d, 0x20, 0x30, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x6d, 0x5f, 0x66, 0x65, + 0x6e, 0x63, 0x65, 0x28, 0x43, 0x4c, 0x4b, 0x5f, 0x47, 0x4c, 0x4f, 0x42, + 0x41, 0x4c, 0x5f, 0x4d, 0x45, 0x4d, 0x5f, 0x46, 0x45, 0x4e, 0x43, 0x45, + 0x29, 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x69, + 0x64, 0x20, 0x30, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, + 0x61, 0x62, 0x6f, 0x76, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x20, + 0x3d, 0x20, 0x68, 0x74, 0x61, 0x62, 0x73, 0x5b, 0x68, 0x74, 0x5f, 0x69, + 0x5d, 0x20, 0x2b, 0x20, 0x74, 0x69, 0x64, 0x20, 0x2a, 0x20, 0x4e, 0x52, + 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x20, 0x2a, 0x20, 0x53, 0x4c, 0x4f, + 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x61, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6e, 0x74, 0x20, 0x3d, 0x20, + 0x6d, 0x69, 0x6e, 0x28, 0x63, 0x6e, 0x74, 0x2c, 0x20, 0x28, 0x75, 0x69, + 0x6e, 0x74, 0x29, 0x4e, 0x52, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x53, 0x29, + 0x3b, 0x20, 0x2f, 0x2f, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, + 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x76, 0x65, + 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x61, 0x73, + 0x74, 0x20, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x61, 0x20, 0x2b, 0x3d, 0x20, 0x78, 0x69, 0x5f, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, 0x20, 0x69, 0x20, + 0x3c, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x20, 0x69, 0x2b, 0x2b, 0x2c, 0x20, + 0x61, 0x20, 0x2b, 0x3d, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, + 0x4e, 0x29, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x6a, 0x20, 0x3d, + 0x20, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x2c, 0x20, 0x62, 0x20, 0x3d, 0x20, + 0x61, 0x20, 0x2b, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4c, 0x45, 0x4e, + 0x3b, 0x20, 0x6a, 0x20, 0x3c, 0x20, 0x63, 0x6e, 0x74, 0x3b, 0x20, 0x6a, + 0x2b, 0x2b, 0x2c, 0x20, 0x62, 0x20, 0x2b, 0x3d, 0x20, 0x53, 0x4c, 0x4f, + 0x54, 0x5f, 0x4c, 0x45, 0x4e, 0x29, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, + 0x61, 0x29, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, 0x6b, 0x29, 0x20, 0x3d, + 0x3d, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x2a, 0x28, + 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, 0x6e, + 0x74, 0x20, 0x2a, 0x29, 0x62, 0x29, 0x20, 0x26, 0x20, 0x6d, 0x61, 0x73, + 0x6b, 0x29, 0x29, 0x0a, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, + 0x0a, 0x09, 0x09, 0x72, 0x65, 0x66, 0x5f, 0x69, 0x20, 0x3d, 0x20, 0x2a, + 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, 0x69, + 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, 0x61, 0x20, 0x2d, 0x20, 0x34, 0x29, + 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x66, 0x5f, 0x6a, 0x20, 0x3d, 0x20, + 0x2a, 0x28, 0x5f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x75, + 0x69, 0x6e, 0x74, 0x20, 0x2a, 0x29, 0x28, 0x62, 0x20, 0x2d, 0x20, 0x34, + 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x6c, + 0x6c, 0x20, 0x3c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x20, 0x28, + 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x20, + 0x2f, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x20, 0x28, 0x2a, 0x63, + 0x6f, 0x6c, 0x6c, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x29, 0x29, 0x0a, + 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x63, 0x6f, 0x6c, 0x6c, 0x2b, 0x2b, 0x5d, + 0x20, 0x3d, 0x20, 0x28, 0x28, 0x75, 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x72, + 0x65, 0x66, 0x5f, 0x69, 0x20, 0x3c, 0x3c, 0x20, 0x33, 0x32, 0x29, 0x20, + 0x7c, 0x20, 0x72, 0x65, 0x66, 0x5f, 0x6a, 0x3b, 0x0a, 0x09, 0x09, 0x65, + 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x61, 0x74, + 0x6f, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x63, 0x28, 0x26, 0x73, 0x6f, + 0x6c, 0x73, 0x2d, 0x3e, 0x6c, 0x69, 0x6b, 0x65, 0x6c, 0x79, 0x5f, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x73, 0x73, 0x29, 0x3b, 0x0a, 0x09, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x66, 0x20, 0x28, 0x21, 0x63, 0x6f, 0x6c, 0x6c, 0x29, 0x0a, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x20, 0x30, 0x3b, + 0x20, 0x69, 0x20, 0x3c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x3b, 0x20, 0x69, + 0x2b, 0x2b, 0x29, 0x0a, 0x09, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x5f, 0x73, 0x6f, 0x6c, 0x28, 0x68, 0x74, 0x61, 0x62, 0x73, + 0x2c, 0x20, 0x73, 0x6f, 0x6c, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3e, 0x3e, + 0x20, 0x33, 0x32, 0x2c, 0x0a, 0x09, 0x09, 0x63, 0x6f, 0x6c, 0x6c, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x26, 0x20, 0x30, + 0x78, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x29, 0x3b, 0x0a, + 0x7d, 0x0a +}; +const size_t CL_MINER_KERNEL_SIZE = 25106; diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp new file mode 100644 index 00000000000..8c1b0ad1906 --- /dev/null +++ b/src/libgpusolver/libclwrapper.cpp @@ -0,0 +1,482 @@ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +//#include +#include +#include +#include +#include +#include +#include +//#include +#include "libclwrapper.h" +#include "kernels/silentarmy.h" // Created from CMake + +// workaround lame platforms +#if !CL_VERSION_1_2 +#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE +#define CL_MEM_HOST_READ_ONLY 0 +#endif + +#undef min +#undef max + +//#define DEBUG + +using namespace std; + +unsigned const cl_gpuminer::c_defaultLocalWorkSize = 32; +unsigned const cl_gpuminer::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE +unsigned const cl_gpuminer::c_defaultMSPerBatch = 0; +bool cl_gpuminer::s_allowCPU = false; +unsigned cl_gpuminer::s_extraRequiredGPUMem; +unsigned cl_gpuminer::s_msPerBatch = cl_gpuminer::c_defaultMSPerBatch; +unsigned cl_gpuminer::s_workgroupSize = cl_gpuminer::c_defaultLocalWorkSize; +unsigned cl_gpuminer::s_initialGlobalWorkSize = cl_gpuminer::c_defaultGlobalWorkSizeMultiplier * cl_gpuminer::c_defaultLocalWorkSize; + +#if defined(_WIN32) +extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString); +static std::atomic_flag s_logSpin = ATOMIC_FLAG_INIT; +#define CL_LOG(_contents) \ + do \ + { \ + std::stringstream ss; \ + ss << _contents; \ + while (s_logSpin.test_and_set(std::memory_order_acquire)) {} \ + OutputDebugStringA(ss.str().c_str()); \ + cerr << ss.str() << endl << flush; \ + s_logSpin.clear(std::memory_order_release); \ + } while (false) +#else +#define CL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl +#endif + +// Types of OpenCL devices we are interested in +#define CL_QUERIED_DEVICE_TYPES (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR) + +// Inject definitions into the kernal source +static void addDefinition(string& _source, char const* _id, unsigned _value) +{ + char buf[256]; + sprintf(buf, "#define %s %uu\n", _id, _value); + _source.insert(_source.begin(), buf, buf + strlen(buf)); +} + + +cl_gpuminer::cl_gpuminer() +: m_openclOnePointOne() +{ + + dst_solutions = (uint32_t *) malloc(10*NUM_INDICES*sizeof(uint32_t)); + if(dst_solutions == NULL) + std::cout << "Error allocating dst_solutions array!" << std::endl; + +} + +cl_gpuminer::~cl_gpuminer() +{ + if(dst_solutions != NULL) + free(dst_solutions); + finish(); +} + +std::vector cl_gpuminer::getPlatforms() +{ + vector platforms; + try + { + cl::Platform::get(&platforms); + } + catch(cl::Error const& err) + { +#if defined(CL_PLATFORM_NOT_FOUND_KHR) + if (err.err() == CL_PLATFORM_NOT_FOUND_KHR) + CL_LOG("No OpenCL platforms found"); + else +#endif + throw err; + } + return platforms; +} + +string cl_gpuminer::platform_info(unsigned _platformId, unsigned _deviceId) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return {}; + // get GPU device of the selected platform + unsigned platform_num = min(_platformId, platforms.size() - 1); + vector devices = getDevices(platforms, _platformId); + if (devices.empty()) + { + CL_LOG("No OpenCL devices found."); + return {}; + } + + // use selected default device + unsigned device_num = min(_deviceId, devices.size() - 1); + cl::Device& device = devices[device_num]; + string device_version = device.getInfo(); + + return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; +} + +std::vector cl_gpuminer::getDevices(std::vector const& _platforms, unsigned _platformId) +{ + vector devices; + unsigned platform_num = min(_platformId, _platforms.size() - 1); + try + { + _platforms[platform_num].getDevices( + s_allowCPU ? CL_DEVICE_TYPE_ALL : CL_QUERIED_DEVICE_TYPES, + &devices + ); + } + catch (cl::Error const& err) + { + // if simply no devices found return empty vector + if (err.err() != CL_DEVICE_NOT_FOUND) + throw err; + } + return devices; +} + +unsigned cl_gpuminer::getNumPlatforms() +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return 0; + return platforms.size(); +} + +unsigned cl_gpuminer::getNumDevices(unsigned _platformId) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return 0; + + vector devices = getDevices(platforms, _platformId); + if (devices.empty()) + { + CL_LOG("No OpenCL devices found."); + return 0; + } + return devices.size(); +} + +// This needs customizing apon completion of the kernel - Checks memory requirements - May not be applicable +bool cl_gpuminer::configureGPU( + unsigned _platformId, + unsigned _localWorkSize, + unsigned _globalWorkSize +) +{ + // Set the local/global work sizes + s_workgroupSize = _localWorkSize; + s_initialGlobalWorkSize = _globalWorkSize; + + return searchForAllDevices(_platformId, [](cl::Device const& _device) -> bool + { + cl_ulong result; + _device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); + + CL_LOG( + "Found suitable OpenCL device [" << _device.getInfo() + << "] with " << result << " bytes of GPU memory" + ); + return true; + } + ); +} + +bool cl_gpuminer::searchForAllDevices(function _callback) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return false; + for (unsigned i = 0; i < platforms.size(); ++i) + if (searchForAllDevices(i, _callback)) + return true; + + return false; +} + +bool cl_gpuminer::searchForAllDevices(unsigned _platformId, function _callback) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return false; + if (_platformId >= platforms.size()) + return false; + + vector devices = getDevices(platforms, _platformId); + for (cl::Device const& device: devices) + if (_callback(device)) + return true; + + return false; +} + +void cl_gpuminer::doForAllDevices(function _callback) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return; + for (unsigned i = 0; i < platforms.size(); ++i) + doForAllDevices(i, _callback); +} + +void cl_gpuminer::doForAllDevices(unsigned _platformId, function _callback) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return; + if (_platformId >= platforms.size()) + return; + + vector devices = getDevices(platforms, _platformId); + for (cl::Device const& device: devices) + _callback(device); +} + +void cl_gpuminer::listDevices() +{ + string outString ="\nListing OpenCL devices.\nFORMAT: [deviceID] deviceName\n"; + unsigned int i = 0; + doForAllDevices([&outString, &i](cl::Device const _device) + { + outString += "[" + to_string(i) + "] " + _device.getInfo() + "\n"; + outString += "\tCL_DEVICE_TYPE: "; + switch (_device.getInfo()) + { + case CL_DEVICE_TYPE_CPU: + outString += "CPU\n"; + break; + case CL_DEVICE_TYPE_GPU: + outString += "GPU\n"; + break; + case CL_DEVICE_TYPE_ACCELERATOR: + outString += "ACCELERATOR\n"; + break; + default: + outString += "DEFAULT\n"; + break; + } + outString += "\tCL_DEVICE_GLOBAL_MEM_SIZE: " + to_string(_device.getInfo()) + "\n"; + outString += "\tCL_DEVICE_MAX_MEM_ALLOC_SIZE: " + to_string(_device.getInfo()) + "\n"; + outString += "\tCL_DEVICE_MAX_WORK_GROUP_SIZE: " + to_string(_device.getInfo()) + "\n"; + ++i; + } + ); + CL_LOG(outString); +} + +void cl_gpuminer::finish() +{ + + if (m_queue()) + m_queue.finish(); +} + +// Customise given kernel - This builds the kernel and creates memory buffers +bool cl_gpuminer::init( + unsigned _platformId, + unsigned _deviceId, + const std::vector _kernels +) +{ + // get all platforms + try + { + vector platforms = getPlatforms(); + if (platforms.empty()) + return false; + + // use selected platform + _platformId = min(_platformId, platforms.size() - 1); + CL_LOG("Using platform: " << platforms[_platformId].getInfo().c_str()); + + // get GPU device of the default platform + vector devices = getDevices(platforms, _platformId); + if (devices.empty()) + { + CL_LOG("No OpenCL devices found."); + return false; + } + + // use selected device + cl::Device& device = devices[min(_deviceId, devices.size() - 1)]; + string device_version = device.getInfo(); + CL_LOG("Using device: " << device.getInfo().c_str() << "(" << device_version.c_str() << ")"); + + if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) + { + CL_LOG("OpenCL 1.0 is not supported."); + return false; + } + if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) + m_openclOnePointOne = true; + + // create context + m_context = cl::Context(vector(&device, &device + 1)); + m_queue = cl::CommandQueue(m_context, device); + + // make sure that global work size is evenly divisible by the local workgroup size + m_globalWorkSize = s_initialGlobalWorkSize; + if (m_globalWorkSize % s_workgroupSize != 0) + m_globalWorkSize = ((m_globalWorkSize / s_workgroupSize) + 1) * s_workgroupSize; + // remember the device's address bits + m_deviceBits = device.getInfo(); + // make sure first step of global work size adjustment is large enough + m_stepWorkSizeAdjust = pow(2, m_deviceBits / 2 + 1); + + // patch source code + // note: CL_MINER_KERNEL is simply cl_gpuminer_kernel.cl compiled + // into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime + + // Uncomment for loading kernel from compiled cl file. +#ifdef DEBUG + ifstream kernel_file("./libgpuminer/kernels/silentarmy.cl"); + string code((istreambuf_iterator(kernel_file)), istreambuf_iterator()); + kernel_file.close(); +#else + string code(CL_MINER_KERNEL, CL_MINER_KERNEL + CL_MINER_KERNEL_SIZE); +#endif + // create miner OpenCL program + cl::Program::Sources sources; + sources.push_back({ code.c_str(), code.size() }); + + cl::Program program(m_context, sources); + try + { + program.build({ device }); + CL_LOG("Printing program log"); + CL_LOG(program.getBuildInfo(device).c_str()); + } + catch (cl::Error const&) + { + CL_LOG(program.getBuildInfo(device).c_str()); + return false; + } + + try + { + for (auto & _kernel : _kernels) + m_gpuKernels.push_back(cl::Kernel(program, _kernel.c_str())); + } + catch (cl::Error const& err) + { + CL_LOG("gpuKERNEL Creation failed: " << err.what() << "(" << err.err() << "). Bailing."); + return false; + } + + // TODO create buffer kernel inputs (private variables) + buf_dbg = cl::Buffer(m_context, CL_MEM_READ_WRITE, dbg_size, NULL, NULL); + //TODO Dangger + m_queue.enqueueFillBuffer(buf_dbg, &zero, 1, 0, dbg_size, 0); + buf_ht[0] = cl::Buffer(m_context, CL_MEM_READ_WRITE, HT_SIZE, NULL, NULL); + buf_ht[1] = cl::Buffer(m_context, CL_MEM_READ_WRITE, HT_SIZE, NULL, NULL); + buf_sols = cl::Buffer(m_context, CL_MEM_READ_WRITE, sizeof (sols_t), NULL, NULL); + + m_queue.finish(); + + } + catch (cl::Error const& err) + { + CL_LOG("CL ERROR:" << get_error_string(err.err())); + return false; + } + return true; +} + + +void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) +{ + try + { + + blake2b_state_t blake; + cl::Buffer buf_blake_st; + uint32_t sol_found = 0; + size_t local_ws = 64; + size_t global_ws; + uint64_t *nonce_ptr; + assert(header_len == ZCASH_BLOCK_HEADER_LEN || + header_len == ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN); + nonce_ptr = (uint64_t *)(header + ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN); + //memset(nonce_ptr, 0, ZCASH_NONCE_LEN); + //*nonce_ptr = nonce; + *ptr = *nonce_ptr; + + //printf("\nSolving nonce %s\n", s_hexdump(nonce_ptr, ZCASH_NONCE_LEN)); + + zcash_blake2b_init(&blake, ZCASH_HASH_LEN, PARAM_N, PARAM_K); + zcash_blake2b_update(&blake, header, 128, 0); + buf_blake_st = cl::Buffer(m_context, CL_MEM_READ_ONLY, sizeof (blake.h), NULL, NULL); + m_queue.enqueueWriteBuffer(buf_blake_st, true, 0, sizeof(blake.h), blake.h); + + m_queue.finish(); + + for (unsigned round = 0; round < PARAM_K; round++) { + + size_t global_ws = NR_ROWS; + + m_gpuKernels[0].setArg(0, buf_ht[round % 2]); + m_queue.enqueueNDRangeKernel(m_gpuKernels[0], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + + if (!round) { + m_gpuKernels[1+round].setArg(0, buf_blake_st); + m_gpuKernels[1+round].setArg(1, buf_ht[round % 2]); + global_ws = select_work_size_blake(); + } else { + m_gpuKernels[1+round].setArg(0, buf_ht[(round - 1) % 2]); + m_gpuKernels[1+round].setArg(1, buf_ht[round % 2]); + global_ws = NR_ROWS; + } + + m_gpuKernels[1+round].setArg(2, buf_dbg); + + m_queue.enqueueNDRangeKernel(m_gpuKernels[1+round], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + + } + + m_gpuKernels[10].setArg(0, buf_ht[0]); + m_gpuKernels[10].setArg(1, buf_ht[1]); + m_gpuKernels[10].setArg(2, buf_sols); + global_ws = NR_ROWS; + m_queue.enqueueNDRangeKernel(m_gpuKernels[10], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + + sols_t * sols; + sols = (sols_t *)malloc(sizeof(*sols)); + + m_queue.enqueueReadBuffer(buf_sols, true, 0, sizeof (*sols), sols); + + m_queue.finish(); + + if (sols->nr > MAX_SOLS) { + /*fprintf(stderr, "%d (probably invalid) solutions were dropped!\n", + sols->nr - MAX_SOLS);*/ + sols->nr = MAX_SOLS; + } + + for (unsigned sol_i = 0; sol_i < sols->nr; sol_i++) + sol_found += verify_sol(sols, sol_i); + + //print_sols(sols, nonce, nr_valid_sols); + + //printf("\nSolutions: %u\n", sol_found); + + *n_sol = sol_found; + memcpy(indices, sols, sizeof(sols_t)); + + free(sols); + + } + catch (cl::Error const& err) + { + CL_LOG("CL ERROR:" << get_error_string(err.err())); + //CL_LOG(err.what() << "(" << err.err() << ")"); + } +} diff --git a/src/libgpusolver/libclwrapper.h b/src/libgpusolver/libclwrapper.h new file mode 100644 index 00000000000..3818194bc65 --- /dev/null +++ b/src/libgpusolver/libclwrapper.h @@ -0,0 +1,331 @@ +#pragma once + +#define __CL_ENABLE_EXCEPTIONS +#define CL_USE_DEPRECATED_OPENCL_2_0_APIS + +// Just for Hello World Kernel +//#define DATA_SIZE 100 + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#include "cl.hpp" +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstack-protector" +#include "cl.hpp" +#pragma GCC diagnostic pop +#else +#include "cl.hpp" +#endif +#include +#include + +#include "sodium.h" + +typedef uint8_t uchar; +typedef uint32_t uint; +typedef uint64_t ulong; + +#include "param.h" +#include "blake.h" +#include + +#define EQUIHASH_N 200 +#define EQUIHASH_K 9 + +#define NUM_COLLISION_BITS (EQUIHASH_N / (EQUIHASH_K + 1)) +#define NUM_INDICES (1 << EQUIHASH_K) + +#define NUM_VALUES (1 << (NUM_COLLISION_BITS+1)) +#define NUM_BUCKETS (1 << NUM_COLLISION_BITS) +#define DIGEST_SIZE 25 + +typedef struct element element_t; +typedef uint64_t digest_t[(DIGEST_SIZE + sizeof(uint64_t) - 1) / sizeof(uint64_t)]; + +struct element { + uint32_t digest_index; + uint32_t parent_bucket_index; +}; + + +typedef struct bucket { + unsigned size; + element_t data[18]; +} bucket_t; + +typedef struct debug_s +{ + uint32_t dropped_coll; + uint32_t dropped_stor; +} debug_t; + +typedef uint32_t eh_index; + +class cl_gpuminer +{ + +public: + + cl_gpuminer(); + ~cl_gpuminer(); + + static bool searchForAllDevices(unsigned _platformId, std::function _callback); + static bool searchForAllDevices(std::function _callback); + static void doForAllDevices(unsigned _platformId, std::function _callback); + static void doForAllDevices(std::function _callback); + static unsigned getNumPlatforms(); + static unsigned getNumDevices(unsigned _platformId = 0); + static std::string platform_info(unsigned _platformId = 0, unsigned _deviceId = 0); + static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); + static std::vector getPlatforms(); + static void listDevices(); + + // Currently just prints memory of the GPU + static bool configureGPU( + unsigned _platformId, + unsigned _localWorkSize, + unsigned _globalWorkSize + ); + + bool init( + unsigned _platformId, + unsigned _deviceId, + std::vector _kernels + ); + + void run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr); + + void finish(); + + /* -- default values -- */ + /// Default value of the local work size. Also known as workgroup size. + static unsigned const c_defaultLocalWorkSize; + /// Default value of the global work size as a multiplier of the local work size + static unsigned const c_defaultGlobalWorkSizeMultiplier; + /// Default value of the milliseconds per global work size (per batch) + static unsigned const c_defaultMSPerBatch; + +private: + static const unsigned int z_n = 200; + static const unsigned int z_k = 9; + static const size_t z_collision_bit_length = z_n / (z_k + 1); + static const eh_index z_N = 1 << (z_collision_bit_length + 1); + + int compare_indices32(uint32_t* a, uint32_t* b, size_t n_current_indices) { + for(size_t i = 0; i < n_current_indices; ++i, ++a, ++b) { + if(*a < *b) { + return -1; + } else if(*a > *b) { + return 1; + } else { + return 0; + } + } + return 0; + } + void normalize_indices(uint32_t* indices) { + for(size_t step_index = 0; step_index < EQUIHASH_K; ++step_index) { + for(size_t i = 0; i < NUM_INDICES; i += (1 << (step_index+1))) { + if(compare_indices32(indices+i, indices+i+(1 << step_index), (1 << step_index)) > 0) { + uint32_t tmp_indices[(1 << step_index)]; + memcpy(tmp_indices, indices+i, (1 << step_index)*sizeof(uint32_t)); + memcpy(indices+i, indices+i+(1 << step_index), (1 << step_index)*sizeof(uint32_t)); + memcpy(indices+i+(1 << step_index), tmp_indices, (1 << step_index)*sizeof(uint32_t)); + } + } + } + } + char *s_hexdump(const void *_a, uint32_t a_len) + { + const uint8_t *a = (const uint8_t *) _a; + static char buf[1024]; + uint32_t i; + for (i = 0; i < a_len && i + 2 < sizeof (buf); i++) + sprintf(buf + i * 2, "%02x", a[i]); + buf[i * 2] = 0; + return buf; + } + size_t select_work_size_blake(void) + { + size_t work_size = + 64 * /* thread per wavefront */ + BLAKE_WPS * /* wavefront per simd */ + 4 * /* simd per compute unit */ + 36; + // Make the work group size a multiple of the nr of wavefronts, while + // dividing the number of inputs. This results in the worksize being a + // power of 2. + while (NR_INPUTS % work_size) + work_size += 64; + //debug("Blake: work size %zd\n", work_size); + return work_size; + } + void sort_pair(uint32_t *a, uint32_t len) + { + uint32_t *b = a + len; + uint32_t tmp, need_sorting = 0; + for (uint32_t i = 0; i < len; i++) + if (need_sorting || a[i] > b[i]) + { + need_sorting = 1; + tmp = a[i]; + a[i] = b[i]; + b[i] = tmp; + } + else if (a[i] < b[i]) + return ; + } + + uint32_t verify_sol(sols_t *sols, unsigned sol_i) { + uint32_t *inputs = sols->values[sol_i]; + uint32_t seen_len = (1 << (PREFIX + 1)) / 8; + uint8_t seen[seen_len]; + uint32_t i; + uint8_t tmp; + // look for duplicate inputs + memset(seen, 0, seen_len); + for (i = 0; i < (1 << PARAM_K); i++) + { + tmp = seen[inputs[i] / 8]; + seen[inputs[i] / 8] |= 1 << (inputs[i] & 7); + if (tmp == seen[inputs[i] / 8]) + { + // at least one input value is a duplicate + sols->valid[sol_i] = 0; + return 0; + } + } + // the valid flag is already set by the GPU, but set it again because + // I plan to change the GPU code to not set it + sols->valid[sol_i] = 1; + // sort the pairs in place + for (uint32_t level = 0; level < PARAM_K; level++) + for (i = 0; i < (1 << PARAM_K); i += (2 << level)) + sort_pair(&inputs[i], 1 << level); + return 1; + } + cl::Context m_context; + cl::CommandQueue m_queue; + std::vector m_gpuKernels; + /*cl::Buffer m_digests[2]; + cl::Buffer m_buckets; + cl::Buffer m_new_digest_index; + cl::Buffer m_blake2b_digest; + cl::Buffer m_dst_solutions; + cl::Buffer m_n_solutions;*/ + cl::Buffer buf_ht[2]; + cl::Buffer buf_sols; + cl::Buffer buf_dbg; + + uint64_t nonce; + uint64_t total; + size_t dbg_size = 1 * sizeof (debug_t); + + const cl_int zero = 0; + uint32_t solutions; + uint32_t * dst_solutions; + + unsigned m_globalWorkSize; + bool m_openclOnePointOne; + unsigned m_deviceBits; + + /// The step used in the work size adjustment + unsigned int m_stepWorkSizeAdjust; + /// The Work Size way of adjustment, > 0 when previously increased, < 0 when previously decreased + int m_wayWorkSizeAdjust = 0; + + /// The local work size for the search + static unsigned s_workgroupSize; + /// The initial global work size for the searches + static unsigned s_initialGlobalWorkSize; + /// The target milliseconds per batch for the search. If 0, then no adjustment will happen + static unsigned s_msPerBatch; + /// Allow CPU to appear as an OpenCL device or not. Default is false + static bool s_allowCPU; + /// GPU memory required for other things, like window rendering e.t.c. + /// User can set it via the --cl-extragpu-mem argument. + static unsigned s_extraRequiredGPUMem; + + const char *get_error_string(cl_int error) + { + switch(error){ + // run-time and JIT compiler errors + case 0: return "CL_SUCCESS"; + case -1: return "CL_DEVICE_NOT_FOUND"; + case -2: return "CL_DEVICE_NOT_AVAILABLE"; + case -3: return "CL_COMPILER_NOT_AVAILABLE"; + case -4: return "CL_MEM_OBJECT_ALLOCATION_FAILURE"; + case -5: return "CL_OUT_OF_RESOURCES"; + case -6: return "CL_OUT_OF_HOST_MEMORY"; + case -7: return "CL_PROFILING_INFO_NOT_AVAILABLE"; + case -8: return "CL_MEM_COPY_OVERLAP"; + case -9: return "CL_IMAGE_FORMAT_MISMATCH"; + case -10: return "CL_IMAGE_FORMAT_NOT_SUPPORTED"; + case -11: return "CL_BUILD_PROGRAM_FAILURE"; + case -12: return "CL_MAP_FAILURE"; + case -13: return "CL_MISALIGNED_SUB_BUFFER_OFFSET"; + case -14: return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST"; + case -15: return "CL_COMPILE_PROGRAM_FAILURE"; + case -16: return "CL_LINKER_NOT_AVAILABLE"; + case -17: return "CL_LINK_PROGRAM_FAILURE"; + case -18: return "CL_DEVICE_PARTITION_FAILED"; + case -19: return "CL_KERNEL_ARG_INFO_NOT_AVAILABLE"; + + // compile-time errors + case -30: return "CL_INVALID_VALUE"; + case -31: return "CL_INVALID_DEVICE_TYPE"; + case -32: return "CL_INVALID_PLATFORM"; + case -33: return "CL_INVALID_DEVICE"; + case -34: return "CL_INVALID_CONTEXT"; + case -35: return "CL_INVALID_QUEUE_PROPERTIES"; + case -36: return "CL_INVALID_COMMAND_QUEUE"; + case -37: return "CL_INVALID_HOST_PTR"; + case -38: return "CL_INVALID_MEM_OBJECT"; + case -39: return "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"; + case -40: return "CL_INVALID_IMAGE_SIZE"; + case -41: return "CL_INVALID_SAMPLER"; + case -42: return "CL_INVALID_BINARY"; + case -43: return "CL_INVALID_BUILD_OPTIONS"; + case -44: return "CL_INVALID_PROGRAM"; + case -45: return "CL_INVALID_PROGRAM_EXECUTABLE"; + case -46: return "CL_INVALID_KERNEL_NAME"; + case -47: return "CL_INVALID_KERNEL_DEFINITION"; + case -48: return "CL_INVALID_KERNEL"; + case -49: return "CL_INVALID_ARG_INDEX"; + case -50: return "CL_INVALID_ARG_VALUE"; + case -51: return "CL_INVALID_ARG_SIZE"; + case -52: return "CL_INVALID_KERNEL_ARGS"; + case -53: return "CL_INVALID_WORK_DIMENSION"; + case -54: return "CL_INVALID_WORK_GROUP_SIZE"; + case -55: return "CL_INVALID_WORK_ITEM_SIZE"; + case -56: return "CL_INVALID_GLOBAL_OFFSET"; + case -57: return "CL_INVALID_EVENT_WAIT_LIST"; + case -58: return "CL_INVALID_EVENT"; + case -59: return "CL_INVALID_OPERATION"; + case -60: return "CL_INVALID_GL_OBJECT"; + case -61: return "CL_INVALID_BUFFER_SIZE"; + case -62: return "CL_INVALID_MIP_LEVEL"; + case -63: return "CL_INVALID_GLOBAL_WORK_SIZE"; + case -64: return "CL_INVALID_PROPERTY"; + case -65: return "CL_INVALID_IMAGE_DESCRIPTOR"; + case -66: return "CL_INVALID_COMPILER_OPTIONS"; + case -67: return "CL_INVALID_LINKER_OPTIONS"; + case -68: return "CL_INVALID_DEVICE_PARTITION_COUNT"; + + // extension errors + case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR"; + case -1001: return "CL_PLATFORM_NOT_FOUND_KHR"; + case -1002: return "CL_INVALID_D3D10_DEVICE_KHR"; + case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR"; + case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR"; + case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR"; + case -9999: return "NVIDIA: ILLEGAL READ OR WRITE TO A BUFFER"; + default: + fprintf(stderr, "'%d'\n", error); + return "Unknown OpenCL error"; + } + } +}; diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp new file mode 100644 index 00000000000..edbbbee419b --- /dev/null +++ b/src/libgpusolver/libgpusolver.cpp @@ -0,0 +1,212 @@ +/* MIT License + * + * Copyright (c) 2016 Omar Alvarez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "libgpusolver.h" +#include "util.h" +#include "primitives/block.h" +#include "arith_uint256.h" + +//#define DEBUG + +char *s_hexdump(const void *_a, uint32_t a_len) +{ + const uint8_t *a = (const uint8_t *) _a; + static char buf[1024]; + uint32_t i; + for (i = 0; i < a_len && i + 2 < sizeof (buf); i++) + sprintf(buf + i * 2, "%02x", a[i]); + buf[i * 2] = 0; + return buf; +} + +GPUSolver::GPUSolver() { + + size_t global_work_size = 1 << 20; + size_t local_work_size = 32; + + miner = new cl_gpuminer(); + + indices = (sols_t *) malloc(sizeof(sols_t)); + if(indices == NULL) + std::cout << "Error allocating indices array!" << std::endl; + + GPU = miner->configureGPU(0, local_work_size, global_work_size); + if(!GPU) + std::cout << "ERROR: No suitable GPU found! No work will be performed!" << std::endl; + + /*Initialize the kernel, compile it and create buffers + Currently runs for the gpu-list-gen.c kernel DATA_SIZE=100 times + */ + std::vector kernels {"kernel_init_ht", "kernel_round0", "kernel_round1", "kernel_round2","kernel_round3", "kernel_round4", "kernel_round5", "kernel_round6", "kernel_round7", "kernel_round8", "kernel_sols"}; + + if(GPU) + initOK = miner->init(0, 0, kernels); + +} + +GPUSolver::GPUSolver(unsigned platform, unsigned device) { + + /* Notes + I've added some extra parameters in this interface to assist with dev, such as + a kernel string to specify which kernel to run and local/global work sizes. + */ + //TODO This looks like IND_PER_BUCKET, enough for GPU? + size_t global_work_size = 1 << 20; + size_t local_work_size = 32; + + miner = new cl_gpuminer(); + + indices = (sols_t *) malloc(sizeof(sols_t)); + if(indices == NULL) + std::cout << "Error allocating indices array!" << std::endl; + + /* Checks each device for memory requirements and sets local/global sizes + TODO: Implement device logic for equihash kernel + @params: unsigned platformId + @params: unsigned localWorkSizes + @params: unsigned globalWorkSizes + */ + GPU = miner->configureGPU(platform, local_work_size, global_work_size); + if(!GPU) + std::cout << "ERROR: No suitable GPU found! No work will be performed!" << std::endl; + + /*Initialize the kernel, compile it and create buffers + Currently runs for the gpu-list-gen.c kernel DATA_SIZE=100 times + TODO: pass base state and nonce's to kernel. + @params: unsigned _platformId + @params: unsigned _deviceId + @params: string& _kernel - The name of the kernel for dev purposes + */ + std::vector kernels {"kernel_init_ht", "kernel_round0", "kernel_round1", "kernel_round2","kernel_round3", "kernel_round4", "kernel_round5", "kernel_round6", "kernel_round7", "kernel_round8", "kernel_sols"}; + if(GPU) + initOK = miner->init(platform, device, kernels); + +} + +GPUSolver::~GPUSolver() { + + if(GPU) + miner->finish(); + + delete miner; + + if(indices != NULL) + free(indices); + +} + +bool GPUSolver::run(unsigned int n, unsigned int k, uint8_t *header, size_t header_len, uint64_t nonce, + const std::function)> validBlock, + const std::function cancelled, + crypto_generichash_blake2b_state base_state) { + + if (n == 200 && k == 9) { + return GPUSolve200_9(header, header_len, nonce, validBlock, cancelled, base_state); + } else { + throw std::invalid_argument("Unsupported Equihash parameters"); + } + +} + +bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce, + const std::function)> validBlock, + const std::function cancelled, + crypto_generichash_blake2b_state base_state) { + + /* Run the kernel + TODO: Optimise and figure out how we want this to go + @params eh_HashState& base_state - Sends to kernel in a buffer. Will update for specific kernels + */ + + if(GPU && initOK) { + // auto t = std::chrono::high_resolution_clock::now(); + uint64_t ptr; + miner->run(header, header_len, nonce, indices, &n_sol, &ptr); + + //uint256 nNonce = ArithToUint256(ptr); + /*crypto_generichash_blake2b_update(&base_state, + nNonce.begin(), + nNonce.size());*/ + +/* + auto d = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t); + auto milis = std::chrono::duration_cast(d).count(); + + if(!counter) { + sum = 1000.f*n_sol/milis; + } else { + sum += 1000.f*n_sol/milis; + } + + avg = sum/++counter; + + if(!(counter % 10)) + std::cout << "Kernel run took " << milis << " ms. (" << avg << " H/s)" << std::endl; +*/ + + size_t checkedSols = n_sol; + size_t s = 0; + while (checkedSols) { + ++s; + if(indices->valid[s-1]) + --checkedSols; + else + continue; + //std::cout << "Checking solution " << checkedSols << std::endl; + std::vector index_vector(PROOFSIZE); + for (size_t i = 0; i < PROOFSIZE; i++) { + //std::cout << s << "] ["<< " " << i << std::endl; + index_vector[i] = indices->values[s-1][i]; + } + std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS_S); +#ifdef DEBUG + bool isValid; + /*LogPrint("pow", "Checking with = %s\n", + nNonce.ToString());*/ + EhIsValidSolution(200, 9, base_state, sol_char, isValid); + std::cout << "is valid: " << isValid << '\n'; + if (!isValid) { + //If we find invalid solution bail, it cannot be a valid POW + std::cout << "Invalid solution found!" << std::endl; + //return false; + } else { + std::cout << "Valid solution found!" << std::endl; + } +#endif + if (validBlock(sol_char)) { + // If we find a POW solution, do not try other solutions + // because they become invalid as we created a new block in blockchain. + std::cout << "Valid block found!" << std::endl; + return true; + } + } + + //free(indices); + + } + + return false; + +} diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h new file mode 100644 index 00000000000..ca0c463e0c6 --- /dev/null +++ b/src/libgpusolver/libgpusolver.h @@ -0,0 +1,96 @@ +/* MIT License + * + * Copyright (c) 2016 Omar Alvarez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __GPU_SOLVER_H +#define __GPU_SOLVER_H + +#include +#include +#include + +#include "crypto/equihash.h" +#include "libclwrapper.h" + + +//#include "param.h" +//#include "blake.h" +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +// The maximum size of the .cl file we read in and compile +#define MAX_SOURCE_SIZE (0x200000) + +#define EK 9 +#define EN 200 +#define DIGITBITS_S (EN/(EK+1)) + +class GPUSolverCancelledException : public std::exception +{ + virtual const char* what() const throw() { + return "GPU Equihash solver was cancelled"; + } +}; + +enum GPUSolverCancelCheck +{ + ListGenerationGPU, + ListSortingGPU +}; + +class GPUSolver { + +public: + GPUSolver(); + GPUSolver(unsigned platform, unsigned device); + ~GPUSolver(); + bool run(unsigned int n, unsigned int k, uint8_t *header, size_t header_len, uint64_t nonce, + const std::function)> validBlock, + const std::function cancelled, + crypto_generichash_blake2b_state base_state); + +private: + cl_gpuminer * miner; + bool GPU; + bool initOK; + static const uint32_t PROOFSIZE = 1 << EK; + //TODO 20? + sols_t * indices; + uint32_t n_sol; + //Avg + uint32_t counter = 0; + float sum = 0.f; + float avg = 0.f; + + bool GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce, + const std::function)> validBlock, + const std::function cancelled, + crypto_generichash_blake2b_state base_state); + +}; + +#endif // __GPU_SOLVER_H diff --git a/src/libgpusolver/param.h b/src/libgpusolver/param.h new file mode 100644 index 00000000000..6cc943ec504 --- /dev/null +++ b/src/libgpusolver/param.h @@ -0,0 +1,66 @@ +#define PARAM_N 200 +#define PARAM_K 9 +#define PREFIX (PARAM_N / (PARAM_K + 1)) +#define NR_INPUTS (1 << PREFIX) +// Approximate log base 2 of number of elements in hash tables +#define APX_NR_ELMS_LOG (PREFIX + 1) +// Number of rows and slots is affected by this. 20 offers the best performance +// but occasionally misses ~1% of solutions. +#define NR_ROWS_LOG 20 + +// Make hash tables OVERHEAD times larger than necessary to store the average +// number of elements per row. The ideal value is as small as possible to +// reduce memory usage, but not too small or else elements are dropped from the +// hash tables. +// +// The actual number of elements per row is closer to the theoretical average +// (less variance) when NR_ROWS_LOG is small. So accordingly OVERHEAD can be +// smaller. +// +// Even (as opposed to odd) values of OVERHEAD sometimes significantly decrease +// performance as they cause VRAM channel conflicts. +#if NR_ROWS_LOG == 16 +#define OVERHEAD 3 +#elif NR_ROWS_LOG == 18 +#define OVERHEAD 5 +#elif NR_ROWS_LOG == 19 +#define OVERHEAD 9 +#elif NR_ROWS_LOG == 20 +#define OVERHEAD 13 +#endif + +#define NR_ROWS (1 << NR_ROWS_LOG) +#define NR_SLOTS ((1 << (APX_NR_ELMS_LOG - NR_ROWS_LOG)) * OVERHEAD) +// Length of 1 element (slot) in bytes +#define SLOT_LEN 32 +// Total size of hash table +#define HT_SIZE (NR_ROWS * NR_SLOTS * SLOT_LEN) +// Length of Zcash block header and nonce +#define ZCASH_BLOCK_HEADER_LEN 140 +#define ZCASH_NONCE_LEN 32 +// Number of bytes Zcash needs out of Blake +#define ZCASH_HASH_LEN 50 +// Number of wavefronts per SIMD for the Blake kernel. +// Blake is ALU-bound (beside the atomic counter being incremented) so we need +// at least 2 wavefronts per SIMD to hide the 2-clock latency of integer +// instructions. 10 is the max supported by the hw. +#define BLAKE_WPS 10 +#define MAX_SOLS 2000 + +// Optional features +#undef ENABLE_DEBUG + +/* +** Return the offset of Xi in bytes from the beginning of the slot. +*/ +#define xi_offset_for_round(round) (8 + ((round) / 2) * 4) + +// An (uncompressed) solution stores (1 << PARAM_K) 32-bit values +#define SOL_SIZE ((1 << PARAM_K) * 4) +typedef struct sols_s +{ + uint nr; + uint likely_invalidss; + uchar valid[MAX_SOLS]; + uint values[MAX_SOLS][(1 << PARAM_K)]; +} sols_t; diff --git a/src/metrics.cpp b/src/metrics.cpp index a5480838e02..5730ab9fd02 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -108,7 +108,7 @@ int printMetrics(size_t cols, int64_t nStart, bool mining) } std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration); std::cout << strDuration << std::endl; - lines += (strDuration.size() / cols); + //lines += (strDuration.size() / cols); std::cout << "- " << strprintf(_("You have validated %d transactions!"), transactionsValidated.get()) << std::endl; diff --git a/src/miner.cpp b/src/miner.cpp index bde9babd59c..66dfcd70510 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -26,6 +26,8 @@ #include #endif +#include "libgpusolver/libgpusolver.h" + #include "sodium.h" #include @@ -443,9 +445,13 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese return true; } -void static BitcoinMiner(CWallet *pwallet) +void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) { - LogPrintf("ZcashMiner started\n"); + if(conf.useGPU) + LogPrintf("ZcashMiner started on device: %u\n", conf.currentDevice); + else + LogPrintf("ZcashMiner started\n"); + SetThreadPriority(THREAD_PRIORITY_LOWEST); RenameThread("zcash-miner"); const CChainParams& chainparams = Params(); @@ -457,10 +463,21 @@ void static BitcoinMiner(CWallet *pwallet) unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); + //uint64_t nn = 0; + GPUSolver * g_solver; + // If zcash.conf GPU=1 + if(conf.useGPU) { + g_solver = new GPUSolver(conf.currentPlatform, conf.currentDevice); + LogPrint("pow", "Using Equihash solver GPU with n = %u, k = %u\n", n, k); + } + uint8_t * header = (uint8_t *) calloc(ZCASH_BLOCK_HEADER_LEN, sizeof(uint8_t)); + uint8_t * zero = (uint8_t *) calloc(12, sizeof(uint8_t)); + std::string solver = GetArg("-equihashsolver", "default"); - assert(solver == "tromp" || solver == "default"); + assert(solver == "tromp" || solver == "GPU" || solver == "default"); LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); + std::mutex m_cs; bool cancelSolver = false; boost::signals2::connection c = uiInterface.NotifyBlockTip.connect( @@ -511,6 +528,11 @@ void static BitcoinMiner(CWallet *pwallet) int64_t nStart = GetTime(); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + crypto_generichash_blake2b_state state; + EhInitialiseState(n, k, state); + + memset(pblock->nNonce.begin()+20, 0, 12); + while (true) { // Hash state crypto_generichash_blake2b_state state; @@ -520,20 +542,28 @@ void static BitcoinMiner(CWallet *pwallet) CEquihashInput I{*pblock}; CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << I; - + memcpy(header, &ss[0], ss.size()); // H(I||... crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size()); // H(I||V||... crypto_generichash_blake2b_state curr_state; curr_state = state; - crypto_generichash_blake2b_update(&curr_state, - pblock->nNonce.begin(), - pblock->nNonce.size()); + + //memset(pblock->nNonce.begin()+20, 0, 12); + + //if(!conf.useGPU) { + crypto_generichash_blake2b_update(&curr_state, + pblock->nNonce.begin(), + pblock->nNonce.size()); + //} + + for (size_t i = 0; i < ZCASH_NONCE_LEN; ++i) + header[108 + i] = pblock->nNonce.begin()[i]; // (x_1, x_2, ...) = A(I, V, n, k) - LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n", - solver, pblock->nNonce.ToString()); + LogPrint("pow", "Running Equihash solver with nNonce = %s\n", + pblock->nNonce.ToString()); std::function)> validBlock = [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams] @@ -567,13 +597,17 @@ void static BitcoinMiner(CWallet *pwallet) return true; }; + std::function cancelledGPU = [&m_cs, &cancelSolver](GPUSolverCancelCheck pos) { + std::lock_guard lock{m_cs}; + return cancelSolver; + }; std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { std::lock_guard lock{m_cs}; return cancelSolver; }; // TODO: factor this out into a function with the same API for each solver. - if (solver == "tromp") { + if (solver == "tromp" && !conf.useGPU) { // Create solver and initialize it. equi eq(1); eq.setstate(&curr_state); @@ -605,40 +639,50 @@ void static BitcoinMiner(CWallet *pwallet) break; } } - } else { - try { - // If we find a valid block, we rebuild - bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); - ehSolverRuns.increment(); - if (found) { - break; - } - } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); - std::lock_guard lock{m_cs}; - cancelSolver = false; - } } - - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - // Regtest mode doesn't require peers - if (vNodes.empty() && chainparams.MiningRequiresPeers()) - break; - if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) - break; - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - break; - if (pindexPrev != chainActive.Tip()) - break; - - // Update nNonce and nTime - pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) - { - // Changing pblock->nTime can change work required on testnet: - hashTarget.SetCompact(pblock->nBits); + else { + try { + if(!conf.useGPU) { + // If we find a valid block, we rebuild + bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); + ehSolverRuns.increment(); + if (found) + break; + } else { + bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN, 0, validBlock, cancelledGPU, curr_state); + ehSolverRuns.increment(); + if (found) + break; + } + } catch (EhSolverCancelledException&) { + LogPrint("pow", "Equihash solver cancelled\n"); + std::lock_guard lock{m_cs}; + cancelSolver = false; + } + + + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // Regtest mode doesn't require peers + if (vNodes.empty() && chainparams.MiningRequiresPeers()) + break; + if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) + break; + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + break; + if (pindexPrev != chainActive.Tip()) + break; + + // Update nNonce and nTime + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + if(memcmp(pblock->nNonce.begin()+20, zero, 12)) + memset(pblock->nNonce.begin()+20, 0, 12); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) + { + // Changing pblock->nTime can change work required on testnet: + hashTarget.SetCompact(pblock->nBits); + } } } } @@ -646,13 +690,27 @@ void static BitcoinMiner(CWallet *pwallet) catch (const boost::thread_interrupted&) { LogPrintf("ZcashMiner terminated\n"); + if(conf.useGPU) + delete g_solver; + free(header); + free(zero); throw; } catch (const std::runtime_error &e) { LogPrintf("ZcashMiner runtime error: %s\n", e.what()); + if(conf.useGPU) + delete g_solver; + free(header); + free(zero); return; } + + if(conf.useGPU) + delete g_solver; + free(header); + free(zero); + c.disconnect(); } @@ -678,9 +736,125 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) if (nThreads == 0 || !fGenerate) return; - minerThreads = new boost::thread_group(); - for (int i = 0; i < nThreads; i++) - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } + +void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig conf) +{ + static boost::thread_group* minerThreads = NULL; + + if (nThreads < 0) { + // In regtest threads defaults to 1 + if (Params().DefaultMinerThreads()) + nThreads = Params().DefaultMinerThreads(); + else + nThreads = boost::thread::hardware_concurrency(); + } + + if (minerThreads != NULL) + { + minerThreads->interrupt_all(); + delete minerThreads; + minerThreads = NULL; + } + + if (nThreads == 0 || !fGenerate) + return; + + minerThreads = new boost::thread_group(); + + // If using GPU + if(conf.useGPU) { + + conf.currentPlatform = 0; + conf.currentDevice = conf.selGPU; + + vector platforms = cl_gpuminer::getPlatforms(); + + // use all available GPUs + if(conf.allGPU) { + + int devicesFound = 0; + unsigned numPlatforms = platforms.size(); + + for(unsigned platform = 0; platform < numPlatforms; ++platform) { + + vector devices = cl_gpuminer::getDevices(platforms, platform); + unsigned noDevices = devices.size(); + devicesFound += noDevices; + for(unsigned device = 0; device < noDevices; ++device) { + + conf.currentPlatform = platform; + conf.currentDevice = device; + + cl_ulong result; + devices[device].getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); + + int maxThreads = nThreads; + if (!conf.forceGenProcLimit) { + if (result > 7500000000) { + maxThreads = min(4, nThreads); + } else if (result > 5500000000) { + maxThreads = min(3, nThreads); + } else if (result > 3500000000) { + maxThreads = min(2, nThreads); + } else { + maxThreads = min(1, nThreads); + } + } + + LogPrintf("ZcashMiner GPU[%d][%d] MemLimit: %s nThreads: %d\n", platform, device, to_string(result), maxThreads); + + for (int i = 0; i < maxThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + + } + } + + if (devicesFound <= 0) { + LogPrintf("ZcashMiner ERROR, No OpenCL devices found!\n"); + } + + } else { + + // mine on specified GPU device + vector devices = cl_gpuminer::getDevices(platforms, conf.currentPlatform); + + if (devices.size() > conf.currentDevice) { + + cl_ulong result; + devices[conf.currentDevice].getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result); + + int maxThreads = nThreads; + if (!conf.forceGenProcLimit) { + if (result > 7500000000) { + maxThreads = min(4, nThreads); + } else if (result > 5500000000) { + maxThreads = min(3, nThreads); + } else if (result > 3500000000) { + maxThreads = min(2, nThreads); + } else { + maxThreads = min(1, nThreads); + } + } + + LogPrintf("ZcashMiner GPU[%d][%d] MemLimit: %s nThreads: %d\n", conf.currentPlatform, conf.currentDevice, to_string(result), maxThreads); + + for (int i = 0; i < maxThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + + } else { + LogPrintf("ZcashMiner ERROR, No OpenCL devices found!\n"); + } + + } + + } + else + { + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + } + +} #endif // ENABLE_WALLET diff --git a/src/miner.h b/src/miner.h index 96a6b70ecd7..edf5890ec3c 100644 --- a/src/miner.h +++ b/src/miner.h @@ -8,6 +8,8 @@ #include "primitives/block.h" +#include "libgpusolver/gpuconfig.h" + #include class CBlockIndex; @@ -25,6 +27,7 @@ struct CBlockTemplate /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads); +void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig conf); /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey); diff --git a/zcutil/build.sh b/zcutil/build.sh index 0000c620129..a04212323e4 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -35,7 +35,14 @@ fi # BUG: parameterize the platform/host directory: PREFIX="$(pwd)/depends/x86_64-unknown-linux-gnu/" -HOST=x86_64-unknown-linux-gnu BUILD=x86_64-unknown-linux-gnu make "$@" -C ./depends/ V=1 NO_QT=1 +xxd -i src/libgpusolver/kernels/silentarmy.cl \ +| sed 's/unsigned/const unsigned/;s/unsigned int/size_t/;s/src_libgpusolver_kernels_silentarmy_cl/CL_MINER_KERNEL/;s/_len/_SIZE/'> \ +src/libgpusolver/kernels/silentarmy.h + +#HOST=x86_64-unknown-linux-gnu BUILD=x86_64-unknown-linux-gnu make "$@" -C ./depends/ V=1 NO_QT=1 +make "$@" -C ./depends/ V=1 NO_QT=1 ./autogen.sh -./configure --prefix="${PREFIX}" --host=x86_64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" CXXFLAGS='-fwrapv -fno-strict-aliasing -Werror -g' +# ./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" CXXFLAGS='-fwrapv -fno-strict-aliasing -g' +./configure --prefix="${PREFIX}" --host=x86_64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" CXXFLAGS='-fwrapv -fno-strict-aliasing -g' +# ./configure --prefix="${PREFIX}" --host=x86_64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" CXXFLAGS='-fwrapv -fno-strict-aliasing -Werror -g' make "$@" V=1