From b3c83e28644c7b08fe69c8f80a4f6e0254eae7b9 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 13:03:40 +1100 Subject: [PATCH 01/26] adds base libraries for gpu mining --- src/libgpusolver/blake.cpp | 104 + src/libgpusolver/blake.h | 10 + src/libgpusolver/cl.hpp | 4037 ++++++++++++++++++++++++ src/libgpusolver/gpuconfig.h | 40 + src/libgpusolver/kernels/silentarmy.cl | 734 +++++ src/libgpusolver/libgpusolver.cpp | 243 ++ src/libgpusolver/libgpusolver.h | 95 + src/libgpusolver/libkernel.cpp | 502 +++ src/libgpusolver/libkernel.h | 331 ++ src/libgpusolver/param.h | 66 + zcutil/build.sh | 4 + 11 files changed, 6166 insertions(+) create mode 100644 src/libgpusolver/blake.cpp create mode 100644 src/libgpusolver/blake.h create mode 100644 src/libgpusolver/cl.hpp create mode 100644 src/libgpusolver/gpuconfig.h create mode 100644 src/libgpusolver/kernels/silentarmy.cl create mode 100644 src/libgpusolver/libgpusolver.cpp create mode 100644 src/libgpusolver/libgpusolver.h create mode 100644 src/libgpusolver/libkernel.cpp create mode 100644 src/libgpusolver/libkernel.h create mode 100644 src/libgpusolver/param.h 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..6878fa08f62 --- /dev/null +++ b/src/libgpusolver/gpuconfig.h @@ -0,0 +1,40 @@ +/* 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; + int64_t selGPU; + 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..bc8ad0c92a8 --- /dev/null +++ b/src/libgpusolver/kernels/silentarmy.cl @@ -0,0 +1,734 @@ +#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; + +/* +** 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) +** +** - 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 +** +** 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' is padding, 'B' is part of the colliding PREFIX +** +** 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 and store them in the hash table. +** +** 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) + { + // Note: round N xors bytes from round N-1 + // xor 24 bytes + xi0 = *(a++) ^ *(b++); + xi1 = *(a++) ^ *(b++); + xi2 = *a ^ *b; + } + 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; + } + else if (round == 6) + { + // xor 12 bytes + xi0 = *a++ ^ *b++; + xi1 = *(__global uint *)a ^ *(__global uint *)b; + xi2 = 0; + } + else if (round == 7 || round == 8) + { + // xor 8 bytes + xi0 = *a ^ *b; + xi1 = 0; + xi2 = 0; + } + // 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 + } + // drop the entire 0xAB byte (see description at top of this file) + uint adj = (!(round % 2)) ? 1 : 0; + // 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 + + adj); + b = (__global ulong *) + (ht_src + tid * NR_SLOTS * SLOT_LEN + j * SLOT_LEN + xi_offset + + adj); + dropped_stor += xor_and_store(round, ht_dst, tid, i, j, a, b); + } +#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_invalidss = 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_invalidss); + } + if (!coll) + return ; + for (i = 0; i < coll; i++) + potential_sol(htabs, sols, collisions[i] >> 32, + collisions[i] & 0xffffffff); +} diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp new file mode 100644 index 00000000000..f4cdb822231 --- /dev/null +++ b/src/libgpusolver/libgpusolver.cpp @@ -0,0 +1,243 @@ +/* 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 "gpusolver.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() { + + /* 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. + + The following are private members of the class, but I have them here to specify the + global work size for now. This will probably be hard-coded later + */ + unsigned int z_n = 200; + unsigned int z_k = 9; + size_t z_collision_bit_length = z_n / (z_k + 1); + eh_index z_N = 1 << (z_collision_bit_length + 1); + //uint32_t global_work_size = z_N; + + //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_zogminer(); + + 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(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 + 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(0, 0, kernels); + +} + +GPUSolver::GPUSolver(int64_t selGPU) { + + /* 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. + + The following are private members of the class, but I have them here to specify the + global work size for now. This will probably be hard-coded later + */ + unsigned int z_n = 200; + unsigned int z_k = 9; + size_t z_collision_bit_length = z_n / (z_k + 1); + eh_index z_N = 1 << (z_collision_bit_length + 1); + //uint32_t global_work_size = z_N; + + //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_zogminer(); + + 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(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 + 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(0, selGPU, 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); +#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..0b8f0b4093f --- /dev/null +++ b/src/libgpusolver/libgpusolver.h @@ -0,0 +1,95 @@ +/* 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 "cl_zogminer.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 (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(int64_t selGPU); + ~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_zogminer * 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/libkernel.cpp b/src/libgpusolver/libkernel.cpp new file mode 100644 index 00000000000..f3e0135c9d7 --- /dev/null +++ b/src/libgpusolver/libkernel.cpp @@ -0,0 +1,502 @@ +/* MIT License + * + * Copyright (c) 2016 Age Manning + * + * 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. + */ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include +#include "libkernel.h" +#include "kernels/silentarmy.h" + +// 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_kernel::c_defaultLocalWorkSize = 32; +unsigned const cl_kernel::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE +unsigned const cl_kernel::c_defaultMSPerBatch = 0; +bool cl_kernel::s_allowCPU = false; +unsigned cl_kernel::s_extraRequiredGPUMem; +unsigned cl_kernel::s_msPerBatch = cl_kernel::c_defaultMSPerBatch; +unsigned cl_kernel::s_workgroupSize = cl_kernel::c_defaultLocalWorkSize; +unsigned cl_kernel::s_initialGlobalWorkSize = cl_kernel::c_defaultGlobalWorkSizeMultiplier * cl_zogminer::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_kernel::cl_kernel() +: 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_kernel::~cl_kernel() +{ + if(dst_solutions != NULL) + free(dst_solutions); + finish(); +} + +std::vector cl_kernel::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_kernel::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_kernel::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_kernel::getNumPlatforms() +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return 0; + return platforms.size(); +} + +unsigned cl_kernel::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_kernel::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_kernel::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_kernel::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_kernel::doForAllDevices(function _callback) +{ + vector platforms = getPlatforms(); + if (platforms.empty()) + return; + for (unsigned i = 0; i < platforms.size(); ++i) + doForAllDevices(i, _callback); +} + +void cl_kernel::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_kernel::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_kernel::finish() +{ + + if (m_queue()) + m_queue.finish(); +} + +// Customise given kernel - This builds the kernel and creates memory buffers +bool cl_kernel::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_kernel_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("./libzogminer/kernels/cl_kernel_kernel.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_zogKernels.push_back(cl::Kernel(program, _kernel.c_str())); + } + catch (cl::Error const& err) + { + CL_LOG("ZOGKERNEL 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_kernel::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); + if (header_len == ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN) + memset(nonce_ptr, 0, ZCASH_NONCE_LEN); + // add the nonce + //*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_zogKernels[0].setArg(0, buf_ht[round % 2]); + m_queue.enqueueNDRangeKernel(m_zogKernels[0], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + + if (!round) { + m_zogKernels[1+round].setArg(0, buf_blake_st); + m_zogKernels[1+round].setArg(1, buf_ht[round % 2]); + global_ws = select_work_size_blake(); + } else { + m_zogKernels[1+round].setArg(0, buf_ht[(round - 1) % 2]); + m_zogKernels[1+round].setArg(1, buf_ht[round % 2]); + global_ws = NR_ROWS; + } + + m_zogKernels[1+round].setArg(2, buf_dbg); + + m_queue.enqueueNDRangeKernel(m_zogKernels[1+round], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + + } + + m_zogKernels[10].setArg(0, buf_ht[0]); + m_zogKernels[10].setArg(1, buf_ht[1]); + m_zogKernels[10].setArg(2, buf_sols); + global_ws = NR_ROWS; + m_queue.enqueueNDRangeKernel(m_zogKernels[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); + + *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/libkernel.h b/src/libgpusolver/libkernel.h new file mode 100644 index 00000000000..d3516f25e42 --- /dev/null +++ b/src/libgpusolver/libkernel.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_kernel +{ + +public: + + cl_kernel(); + ~cl_kernel(); + + 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 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); + + static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); + static std::vector getPlatforms(); + 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_zogKernels; + /*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/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/zcutil/build.sh b/zcutil/build.sh index 0000c620129..8cf1b4cbafa 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -35,6 +35,10 @@ fi # BUG: parameterize the platform/host directory: PREFIX="$(pwd)/depends/x86_64-unknown-linux-gnu/" +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 ./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' From 96f36baa13de1483cc2d2d47d4c7a1ee94d583ec Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 13:43:07 +1100 Subject: [PATCH 02/26] name changes and makefile addition --- src/Makefile.am | 34 ++++++++++++++++++++++++++++--- src/libgpusolver/libgpusolver.cpp | 8 ++++---- src/libgpusolver/libgpusolver.h | 2 +- src/libgpusolver/libkernel.h | 2 +- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 8ded268cfde..0e00994504b 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 +LIBGPUMINER=libgpuminer.a $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) @@ -45,7 +46,8 @@ 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 +82,15 @@ LIBZCASH_H = \ zcash/util.h \ zcash/Zcash.h +LIBGPUSOLVER_H = \ + libgpusolver/libgpusolver.h \ + libgpusolver/gpuconfig.h \ + libgpusolver/kernels/silentarmy.h \ + libgpusolver/libkernel.h \ + libgpusolver/cl.hpp \ + libgpusolver/blake.h \ + libgpusolver/param.h + .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ @@ -226,7 +237,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 @@ -378,7 +390,9 @@ zcashd_LDADD += \ $(MINIUPNPC_LIBS) \ $(LIBZCASH) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBGPUSOLVER) \ + $(LIBGPUSOLVER_LIBS) # # bitcoin-cli binary # @@ -448,6 +462,20 @@ libzcash_a_LDFLAGS = $(HARDENED_LDFLAGS) libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT +libgpusolver_a_SOURCES = \ + libgpusolver/libgpusolver.cpp \ + libgpusolver/libkernel.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/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index f4cdb822231..368927a389f 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -23,12 +23,12 @@ #include -#include "gpusolver.h" +#include "libgpusolver.h" #include "util.h" #include "primitives/block.h" #include "arith_uint256.h" -#define DEBUG +//#define DEBUG char *s_hexdump(const void *_a, uint32_t a_len) { @@ -60,7 +60,7 @@ GPUSolver::GPUSolver() { size_t global_work_size = 1 << 20; size_t local_work_size = 32; - miner = new cl_zogminer(); + miner = new cl_kernel(); indices = (sols_t *) malloc(sizeof(sols_t)); if(indices == NULL) @@ -109,7 +109,7 @@ GPUSolver::GPUSolver(int64_t selGPU) { size_t global_work_size = 1 << 20; size_t local_work_size = 32; - miner = new cl_zogminer(); + miner = new cl_kernel(); indices = (sols_t *) malloc(sizeof(sols_t)); if(indices == NULL) diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index 0b8f0b4093f..5cc5ce2ec01 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -29,7 +29,7 @@ #include #include "crypto/equihash.h" -#include "cl_zogminer.h" +#include "cl_kernel.h" //#include "param.h" //#include "blake.h" diff --git a/src/libgpusolver/libkernel.h b/src/libgpusolver/libkernel.h index d3516f25e42..e17d5c0558b 100644 --- a/src/libgpusolver/libkernel.h +++ b/src/libgpusolver/libkernel.h @@ -4,7 +4,7 @@ #define CL_USE_DEPRECATED_OPENCL_2_0_APIS // Just for Hello World Kernel -#define DATA_SIZE 100 +//#define DATA_SIZE 100 #if defined(__clang__) #pragma clang diagnostic push From cf889c6c5874fbe5f7a8641ad4b13df1e666ee59 Mon Sep 17 00:00:00 2001 From: nginnever Date: Fri, 28 Oct 2016 20:08:43 -0700 Subject: [PATCH 03/26] gpu miner init --- src/libgpusolver/kernels/silentarmy.h | 2096 +++++++++++++++++++++++++ src/libgpusolver/libgpusolver.cpp | 2 +- src/miner.cpp | 67 +- 3 files changed, 2152 insertions(+), 13 deletions(-) create mode 100644 src/libgpusolver/kernels/silentarmy.h 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/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index 368927a389f..6fed89a89ed 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -28,7 +28,7 @@ #include "primitives/block.h" #include "arith_uint256.h" -//#define DEBUG +#define DEBUG char *s_hexdump(const void *_a, uint32_t a_len) { diff --git a/src/miner.cpp b/src/miner.cpp index bde9babd59c..395e3656022 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -26,6 +26,8 @@ #include #endif +#include "libgpusolver/libgpusolver.h" + #include "sodium.h" #include @@ -443,7 +445,7 @@ 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"); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -457,9 +459,17 @@ void static BitcoinMiner(CWallet *pwallet) unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); - std::string solver = GetArg("-equihashsolver", "default"); - assert(solver == "tromp" || solver == "default"); - LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); + // If zcash.conf GPU=1 + if(conf.useGPU) { + GPUSolver * solver; + solver = new GPUSolver(conf.selGPU); + LogPrint("pow", "Using Equihash solver GPU with n = %u, k = %u\n", n, k); + } else { + std::string solver = GetArg("-equihashsolver", "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; @@ -511,6 +521,10 @@ void static BitcoinMiner(CWallet *pwallet) int64_t nStart = GetTime(); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + uint8_t * header = (uint8_t *) calloc(ZCASH_BLOCK_HEADER_LEN, sizeof(uint8_t)); + crypto_generichash_blake2b_state state; + EhInitialiseState(n, k, state); + while (true) { // Hash state crypto_generichash_blake2b_state state; @@ -527,9 +541,12 @@ void static BitcoinMiner(CWallet *pwallet) // H(I||V||... crypto_generichash_blake2b_state curr_state; curr_state = state; - crypto_generichash_blake2b_update(&curr_state, - pblock->nNonce.begin(), - pblock->nNonce.size()); + + if(!conf.useGPU) { + crypto_generichash_blake2b_update(&curr_state, + pblock->nNonce.begin(), + pblock->nNonce.size()); + } // (x_1, x_2, ...) = A(I, V, n, k) LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n", @@ -567,6 +584,10 @@ 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; @@ -607,11 +628,19 @@ void static BitcoinMiner(CWallet *pwallet) } } else { try { - // If we find a valid block, we rebuild - bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); - ehSolverRuns.increment(); - if (found) { - break; + 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 = solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state) { + if (found) { + break; + } + } } } catch (EhSolverCancelledException&) { LogPrint("pow", "Equihash solver cancelled\n"); @@ -646,13 +675,27 @@ void static BitcoinMiner(CWallet *pwallet) catch (const boost::thread_interrupted&) { LogPrintf("ZcashMiner terminated\n"); + if(conf.useGPU) + delete solver; + if(tmp_header) + free(tmp_header); throw; } catch (const std::runtime_error &e) { LogPrintf("ZcashMiner runtime error: %s\n", e.what()); + if(conf.useGPU) + delete solver; + if(tmp_header) + free(tmp_header); return; } + + if(conf.useGPU) + delete solver; + if(tmp_header) + free(tmp_header); + c.disconnect(); } From e7f28baccd3ffb0bffc685cd16ba6a6207960a39 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 14:12:25 +1100 Subject: [PATCH 04/26] changed name of silentarmy compiled header --- src/libgpusolver/libgpusolver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index 5cc5ce2ec01..0a2643cdc6f 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -29,7 +29,7 @@ #include #include "crypto/equihash.h" -#include "cl_kernel.h" +#include "silentarmy.h" //#include "param.h" //#include "blake.h" From c09636df1e0e0c78d1521e73d3720c172dc7593c Mon Sep 17 00:00:00 2001 From: nginnever Date: Fri, 28 Oct 2016 20:25:49 -0700 Subject: [PATCH 05/26] opencl work --- src/libgpusolver/cl_gpuminer.cpp | 484 +++++++++++++++++++++++++++++++ src/libgpusolver/cl_gpuminer.h | 331 +++++++++++++++++++++ 2 files changed, 815 insertions(+) create mode 100644 src/libgpusolver/cl_gpuminer.cpp create mode 100644 src/libgpusolver/cl_gpuminer.h diff --git a/src/libgpusolver/cl_gpuminer.cpp b/src/libgpusolver/cl_gpuminer.cpp new file mode 100644 index 00000000000..268cf6aaa19 --- /dev/null +++ b/src/libgpusolver/cl_gpuminer.cpp @@ -0,0 +1,484 @@ + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +//#include +#include +#include +#include +#include +#include +#include +//#include +#include "cl_gpuminer.h" +#include "kernels/cl_gpuminer_kernel.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 + printf("IN INIT CLASS\n"); + 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) +{ + printf("IN CLRUN CLASS\n"); + 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); + if (header_len == ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN) + memset(nonce_ptr, 0, ZCASH_NONCE_LEN); + // add the nonce + //*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); + + *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/cl_gpuminer.h b/src/libgpusolver/cl_gpuminer.h new file mode 100644 index 00000000000..f9b608e4dd6 --- /dev/null +++ b/src/libgpusolver/cl_gpuminer.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 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); + + static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); + static std::vector getPlatforms(); + 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"; + } + } +}; From 5e3512500a4a4f0d4bf6cb5a208b2d752e1ac7a9 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 14:26:37 +1100 Subject: [PATCH 06/26] correct name change --- src/libgpusolver/libgpusolver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index 0a2643cdc6f..c256a15297b 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -29,7 +29,7 @@ #include #include "crypto/equihash.h" -#include "silentarmy.h" +#include "libkernel.h" //#include "param.h" //#include "blake.h" From 482f3d88e96bd6c8cbc0aac45cc5b595bf7d2529 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 14:53:37 +1100 Subject: [PATCH 07/26] modified names and added gpu support in init.cpp --- src/init.cpp | 18 ++++--- src/libgpusolver/gpuconfig.h | 7 ++- .../{libkernel.cpp => libclwrapper.cpp} | 54 +++++++++---------- .../{libkernel.h => libclwrapper.h} | 0 src/libgpusolver/libgpusolver.cpp | 43 ++------------- src/libgpusolver/libgpusolver.h | 3 +- 6 files changed, 51 insertions(+), 74 deletions(-) rename src/libgpusolver/{libkernel.cpp => libclwrapper.cpp} (88%) rename src/libgpusolver/{libkernel.h => libclwrapper.h} (100%) diff --git a/src/init.cpp b/src/init.cpp index 384a82bba82..584bf2f1fb1 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:")); @@ -1014,15 +1015,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 +1493,13 @@ 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("-GPU", false); + conf.selGPU = GetArg("-deviceid", 0); + conf.allGPU = GetArg("-allgpu", 0); + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1),conf); + } #endif // ********************************************************* Step 11: finished diff --git a/src/libgpusolver/gpuconfig.h b/src/libgpusolver/gpuconfig.h index 6878fa08f62..48042de76c8 100644 --- a/src/libgpusolver/gpuconfig.h +++ b/src/libgpusolver/gpuconfig.h @@ -29,9 +29,12 @@ class GPUConfig { public: //GPUConfig(); //~GPUConfig(); - + bool useGPU; - int64_t selGPU; + unsigned selGPU; + bool allGPU; + unsigned currentPlatform; + unsigned currentDevice; unsigned globalWorkSize; unsigned workgroupSize; diff --git a/src/libgpusolver/libkernel.cpp b/src/libgpusolver/libclwrapper.cpp similarity index 88% rename from src/libgpusolver/libkernel.cpp rename to src/libgpusolver/libclwrapper.cpp index f3e0135c9d7..9ce7f12835a 100644 --- a/src/libgpusolver/libkernel.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -31,7 +31,7 @@ #include #include #include -#include "libkernel.h" +#include "libclwrapper.h" #include "kernels/silentarmy.h" // workaround lame platforms @@ -47,14 +47,14 @@ using namespace std; -unsigned const cl_kernel::c_defaultLocalWorkSize = 32; -unsigned const cl_kernel::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE -unsigned const cl_kernel::c_defaultMSPerBatch = 0; -bool cl_kernel::s_allowCPU = false; -unsigned cl_kernel::s_extraRequiredGPUMem; -unsigned cl_kernel::s_msPerBatch = cl_kernel::c_defaultMSPerBatch; -unsigned cl_kernel::s_workgroupSize = cl_kernel::c_defaultLocalWorkSize; -unsigned cl_kernel::s_initialGlobalWorkSize = cl_kernel::c_defaultGlobalWorkSizeMultiplier * cl_zogminer::c_defaultLocalWorkSize; +unsigned const cl_wrapper::c_defaultLocalWorkSize = 32; +unsigned const cl_wrapper::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE +unsigned const cl_wrapper::c_defaultMSPerBatch = 0; +bool cl_wrapper::s_allowCPU = false; +unsigned cl_wrapper::s_extraRequiredGPUMem; +unsigned cl_wrapper::s_msPerBatch = cl_wrapper::c_defaultMSPerBatch; +unsigned cl_wrapper::s_workgroupSize = cl_wrapper::c_defaultLocalWorkSize; +unsigned cl_wrapper::s_initialGlobalWorkSize = cl_wrapper::c_defaultGlobalWorkSizeMultiplier * cl_zogminer::c_defaultLocalWorkSize; #if defined(_WIN32) extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString); @@ -85,7 +85,7 @@ static void addDefinition(string& _source, char const* _id, unsigned _value) } -cl_kernel::cl_kernel() +cl_wrapper::cl_wrapper() : m_openclOnePointOne() { @@ -95,14 +95,14 @@ cl_kernel::cl_kernel() } -cl_kernel::~cl_kernel() +cl_wrapper::~cl_wrapper() { if(dst_solutions != NULL) free(dst_solutions); finish(); } -std::vector cl_kernel::getPlatforms() +std::vector cl_wrapper::getPlatforms() { vector platforms; try @@ -121,7 +121,7 @@ std::vector cl_kernel::getPlatforms() return platforms; } -string cl_kernel::platform_info(unsigned _platformId, unsigned _deviceId) +string cl_wrapper::platform_info(unsigned _platformId, unsigned _deviceId) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -143,7 +143,7 @@ string cl_kernel::platform_info(unsigned _platformId, unsigned _deviceId) return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; } -std::vector cl_kernel::getDevices(std::vector const& _platforms, unsigned _platformId) +std::vector cl_wrapper::getDevices(std::vector const& _platforms, unsigned _platformId) { vector devices; unsigned platform_num = min(_platformId, _platforms.size() - 1); @@ -163,7 +163,7 @@ std::vector cl_kernel::getDevices(std::vector const& _ return devices; } -unsigned cl_kernel::getNumPlatforms() +unsigned cl_wrapper::getNumPlatforms() { vector platforms = getPlatforms(); if (platforms.empty()) @@ -171,7 +171,7 @@ unsigned cl_kernel::getNumPlatforms() return platforms.size(); } -unsigned cl_kernel::getNumDevices(unsigned _platformId) +unsigned cl_wrapper::getNumDevices(unsigned _platformId) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -187,7 +187,7 @@ unsigned cl_kernel::getNumDevices(unsigned _platformId) } // This needs customizing apon completion of the kernel - Checks memory requirements - May not be applicable -bool cl_kernel::configureGPU( +bool cl_wrapper::configureGPU( unsigned _platformId, unsigned _localWorkSize, unsigned _globalWorkSize @@ -211,7 +211,7 @@ bool cl_kernel::configureGPU( ); } -bool cl_kernel::searchForAllDevices(function _callback) +bool cl_wrapper::searchForAllDevices(function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -223,7 +223,7 @@ bool cl_kernel::searchForAllDevices(function _callback) return false; } -bool cl_kernel::searchForAllDevices(unsigned _platformId, function _callback) +bool cl_wrapper::searchForAllDevices(unsigned _platformId, function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -239,7 +239,7 @@ bool cl_kernel::searchForAllDevices(unsigned _platformId, function _callback) +void cl_wrapper::doForAllDevices(function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -248,7 +248,7 @@ void cl_kernel::doForAllDevices(function _callback) doForAllDevices(i, _callback); } -void cl_kernel::doForAllDevices(unsigned _platformId, function _callback) +void cl_wrapper::doForAllDevices(unsigned _platformId, function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -261,7 +261,7 @@ void cl_kernel::doForAllDevices(unsigned _platformId, function _kernels @@ -353,12 +353,12 @@ bool cl_kernel::init( m_stepWorkSizeAdjust = pow(2, m_deviceBits / 2 + 1); // patch source code - // note: CL_MINER_KERNEL is simply cl_kernel_kernel.cl compiled + // note: CL_MINER_KERNEL is simply cl_wrapper_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("./libzogminer/kernels/cl_kernel_kernel.cl"); + ifstream kernel_file("./libzogminer/kernels/cl_wrapper_kernel.cl"); string code((istreambuf_iterator(kernel_file)), istreambuf_iterator()); kernel_file.close(); #else @@ -412,7 +412,7 @@ bool cl_kernel::init( } -void cl_kernel::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) +void cl_wrapper::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) { try { diff --git a/src/libgpusolver/libkernel.h b/src/libgpusolver/libclwrapper.h similarity index 100% rename from src/libgpusolver/libkernel.h rename to src/libgpusolver/libclwrapper.h diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index 6fed89a89ed..cd621513e11 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -43,45 +43,21 @@ char *s_hexdump(const void *_a, uint32_t a_len) GPUSolver::GPUSolver() { - /* 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. - - The following are private members of the class, but I have them here to specify the - global work size for now. This will probably be hard-coded later - */ - unsigned int z_n = 200; - unsigned int z_k = 9; - size_t z_collision_bit_length = z_n / (z_k + 1); - eh_index z_N = 1 << (z_collision_bit_length + 1); - //uint32_t global_work_size = z_N; - - //TODO This looks like IND_PER_BUCKET, enough for GPU? size_t global_work_size = 1 << 20; - size_t local_work_size = 32; + size_t local_work_size = 32; - miner = new cl_kernel(); + miner = new cl_wrapper(); 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(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 - 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"}; @@ -90,26 +66,17 @@ GPUSolver::GPUSolver() { } -GPUSolver::GPUSolver(int64_t selGPU) { +GPUSolver::GPUSolver(unsigned selGPU) { /* 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. - - The following are private members of the class, but I have them here to specify the - global work size for now. This will probably be hard-coded later */ - unsigned int z_n = 200; - unsigned int z_k = 9; - size_t z_collision_bit_length = z_n / (z_k + 1); - eh_index z_N = 1 << (z_collision_bit_length + 1); - //uint32_t global_work_size = z_N; - //TODO This looks like IND_PER_BUCKET, enough for GPU? size_t global_work_size = 1 << 20; - size_t local_work_size = 32; + size_t local_work_size = 32; - miner = new cl_kernel(); + miner = new cl_wrapper(); indices = (sols_t *) malloc(sizeof(sols_t)); if(indices == NULL) diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index c256a15297b..aecbde7b1c0 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -29,7 +29,8 @@ #include #include "crypto/equihash.h" -#include "libkernel.h" +#include "libclwrapper.h" + //#include "param.h" //#include "blake.h" From 014efb399f791c7d3aa030b15d4e3b32c0880df3 Mon Sep 17 00:00:00 2001 From: nginnever Date: Fri, 28 Oct 2016 21:58:19 -0700 Subject: [PATCH 08/26] builf fail --- src/libgpusolver/cl_gpuminer.cpp | 484 ------------------------------ src/libgpusolver/cl_gpuminer.h | 331 -------------------- src/libgpusolver/libclwrapper.cpp | 122 ++++---- src/libgpusolver/libclwrapper.h | 8 +- src/libgpusolver/libgpusolver.cpp | 2 +- src/libgpusolver/libgpusolver.h | 4 +- src/miner.cpp | 74 +++-- src/miner.h | 3 + 8 files changed, 110 insertions(+), 918 deletions(-) delete mode 100644 src/libgpusolver/cl_gpuminer.cpp delete mode 100644 src/libgpusolver/cl_gpuminer.h diff --git a/src/libgpusolver/cl_gpuminer.cpp b/src/libgpusolver/cl_gpuminer.cpp deleted file mode 100644 index 268cf6aaa19..00000000000 --- a/src/libgpusolver/cl_gpuminer.cpp +++ /dev/null @@ -1,484 +0,0 @@ - -#define _CRT_SECURE_NO_WARNINGS - -#include -#include -//#include -#include -#include -#include -#include -#include -#include -//#include -#include "cl_gpuminer.h" -#include "kernels/cl_gpuminer_kernel.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 - printf("IN INIT CLASS\n"); - 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) -{ - printf("IN CLRUN CLASS\n"); - 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); - if (header_len == ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN) - memset(nonce_ptr, 0, ZCASH_NONCE_LEN); - // add the nonce - //*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); - - *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/cl_gpuminer.h b/src/libgpusolver/cl_gpuminer.h deleted file mode 100644 index f9b608e4dd6..00000000000 --- a/src/libgpusolver/cl_gpuminer.h +++ /dev/null @@ -1,331 +0,0 @@ -#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 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); - - static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); - static std::vector getPlatforms(); - 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/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index 9ce7f12835a..268cf6aaa19 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -1,38 +1,18 @@ -/* MIT License - * - * Copyright (c) 2016 Age Manning - * - * 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. - */ #define _CRT_SECURE_NO_WARNINGS #include #include +//#include #include #include #include #include #include #include -#include "libclwrapper.h" -#include "kernels/silentarmy.h" +//#include +#include "cl_gpuminer.h" +#include "kernels/cl_gpuminer_kernel.h" // Created from CMake // workaround lame platforms #if !CL_VERSION_1_2 @@ -47,14 +27,14 @@ using namespace std; -unsigned const cl_wrapper::c_defaultLocalWorkSize = 32; -unsigned const cl_wrapper::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE -unsigned const cl_wrapper::c_defaultMSPerBatch = 0; -bool cl_wrapper::s_allowCPU = false; -unsigned cl_wrapper::s_extraRequiredGPUMem; -unsigned cl_wrapper::s_msPerBatch = cl_wrapper::c_defaultMSPerBatch; -unsigned cl_wrapper::s_workgroupSize = cl_wrapper::c_defaultLocalWorkSize; -unsigned cl_wrapper::s_initialGlobalWorkSize = cl_wrapper::c_defaultGlobalWorkSizeMultiplier * cl_zogminer::c_defaultLocalWorkSize; +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); @@ -85,24 +65,24 @@ static void addDefinition(string& _source, char const* _id, unsigned _value) } -cl_wrapper::cl_wrapper() +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; + std::cout << "Error allocating dst_solutions array!" << std::endl; } -cl_wrapper::~cl_wrapper() +cl_gpuminer::~cl_gpuminer() { if(dst_solutions != NULL) free(dst_solutions); finish(); } -std::vector cl_wrapper::getPlatforms() +std::vector cl_gpuminer::getPlatforms() { vector platforms; try @@ -121,7 +101,7 @@ std::vector cl_wrapper::getPlatforms() return platforms; } -string cl_wrapper::platform_info(unsigned _platformId, unsigned _deviceId) +string cl_gpuminer::platform_info(unsigned _platformId, unsigned _deviceId) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -143,7 +123,7 @@ string cl_wrapper::platform_info(unsigned _platformId, unsigned _deviceId) return "{ \"platform\": \"" + platforms[platform_num].getInfo() + "\", \"device\": \"" + device.getInfo() + "\", \"version\": \"" + device_version + "\" }"; } -std::vector cl_wrapper::getDevices(std::vector const& _platforms, unsigned _platformId) +std::vector cl_gpuminer::getDevices(std::vector const& _platforms, unsigned _platformId) { vector devices; unsigned platform_num = min(_platformId, _platforms.size() - 1); @@ -163,7 +143,7 @@ std::vector cl_wrapper::getDevices(std::vector const& return devices; } -unsigned cl_wrapper::getNumPlatforms() +unsigned cl_gpuminer::getNumPlatforms() { vector platforms = getPlatforms(); if (platforms.empty()) @@ -171,7 +151,7 @@ unsigned cl_wrapper::getNumPlatforms() return platforms.size(); } -unsigned cl_wrapper::getNumDevices(unsigned _platformId) +unsigned cl_gpuminer::getNumDevices(unsigned _platformId) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -187,7 +167,7 @@ unsigned cl_wrapper::getNumDevices(unsigned _platformId) } // This needs customizing apon completion of the kernel - Checks memory requirements - May not be applicable -bool cl_wrapper::configureGPU( +bool cl_gpuminer::configureGPU( unsigned _platformId, unsigned _localWorkSize, unsigned _globalWorkSize @@ -211,7 +191,7 @@ bool cl_wrapper::configureGPU( ); } -bool cl_wrapper::searchForAllDevices(function _callback) +bool cl_gpuminer::searchForAllDevices(function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -223,7 +203,7 @@ bool cl_wrapper::searchForAllDevices(function _callback return false; } -bool cl_wrapper::searchForAllDevices(unsigned _platformId, function _callback) +bool cl_gpuminer::searchForAllDevices(unsigned _platformId, function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -239,7 +219,7 @@ bool cl_wrapper::searchForAllDevices(unsigned _platformId, function _callback) +void cl_gpuminer::doForAllDevices(function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -248,7 +228,7 @@ void cl_wrapper::doForAllDevices(function _callback) doForAllDevices(i, _callback); } -void cl_wrapper::doForAllDevices(unsigned _platformId, function _callback) +void cl_gpuminer::doForAllDevices(unsigned _platformId, function _callback) { vector platforms = getPlatforms(); if (platforms.empty()) @@ -261,7 +241,7 @@ void cl_wrapper::doForAllDevices(unsigned _platformId, function _kernels ) { // get all platforms + printf("IN INIT CLASS\n"); try { vector platforms = getPlatforms(); @@ -353,12 +334,12 @@ bool cl_wrapper::init( m_stepWorkSizeAdjust = pow(2, m_deviceBits / 2 + 1); // patch source code - // note: CL_MINER_KERNEL is simply cl_wrapper_kernel.cl compiled + // 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("./libzogminer/kernels/cl_wrapper_kernel.cl"); + ifstream kernel_file("./libgpuminer/kernels/silentarmy.cl"); string code((istreambuf_iterator(kernel_file)), istreambuf_iterator()); kernel_file.close(); #else @@ -384,11 +365,11 @@ bool cl_wrapper::init( try { for (auto & _kernel : _kernels) - m_zogKernels.push_back(cl::Kernel(program, _kernel.c_str())); + m_gpuKernels.push_back(cl::Kernel(program, _kernel.c_str())); } catch (cl::Error const& err) { - CL_LOG("ZOGKERNEL Creation failed: " << err.what() << "(" << err.err() << "). Bailing."); + CL_LOG("gpuKERNEL Creation failed: " << err.what() << "(" << err.err() << "). Bailing."); return false; } @@ -412,8 +393,9 @@ bool cl_wrapper::init( } -void cl_wrapper::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) +void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) { + printf("IN CLRUN CLASS\n"); try { @@ -444,31 +426,31 @@ void cl_wrapper::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t for (unsigned round = 0; round < PARAM_K; round++) { size_t global_ws = NR_ROWS; - - m_zogKernels[0].setArg(0, buf_ht[round % 2]); - m_queue.enqueueNDRangeKernel(m_zogKernels[0], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); - + + 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_zogKernels[1+round].setArg(0, buf_blake_st); - m_zogKernels[1+round].setArg(1, buf_ht[round % 2]); + 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_zogKernels[1+round].setArg(0, buf_ht[(round - 1) % 2]); - m_zogKernels[1+round].setArg(1, buf_ht[round % 2]); + 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_zogKernels[1+round].setArg(2, buf_dbg); - - m_queue.enqueueNDRangeKernel(m_zogKernels[1+round], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); - + m_queue.enqueueNDRangeKernel(m_gpuKernels[1+round], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + } - - m_zogKernels[10].setArg(0, buf_ht[0]); - m_zogKernels[10].setArg(1, buf_ht[1]); - m_zogKernels[10].setArg(2, buf_sols); + + 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_zogKernels[10], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws)); + 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)); diff --git a/src/libgpusolver/libclwrapper.h b/src/libgpusolver/libclwrapper.h index e17d5c0558b..53f4414e9d7 100644 --- a/src/libgpusolver/libclwrapper.h +++ b/src/libgpusolver/libclwrapper.h @@ -64,13 +64,13 @@ typedef struct debug_s typedef uint32_t eh_index; -class cl_kernel +class cl_gpukernel { public: - cl_kernel(); - ~cl_kernel(); + cl_gpukernel(); + ~cl_gpukernel(); static bool searchForAllDevices(unsigned _platformId, std::function _callback); static bool searchForAllDevices(std::function _callback); @@ -209,7 +209,7 @@ class cl_kernel } cl::Context m_context; cl::CommandQueue m_queue; - std::vector m_zogKernels; + std::vector m_gpuKernels; /*cl::Buffer m_digests[2]; cl::Buffer m_buckets; cl::Buffer m_new_digest_index; diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index cd621513e11..d0df72ea1e3 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -178,7 +178,7 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce //std::cout << s << "] ["<< " " << i << std::endl; index_vector[i] = indices->values[s-1][i]; } - std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); + std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS_S); #ifdef DEBUG bool isValid; LogPrint("pow", "Checking with = %s\n", diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index aecbde7b1c0..e3e0368c4d0 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -47,7 +47,7 @@ #define EK 9 #define EN 200 -#define DIGITBITS (EN/(EK+1)) +#define DIGITBITS_S (EN/(EK+1)) class GPUSolverCancelledException : public std::exception { @@ -74,7 +74,7 @@ class GPUSolver { crypto_generichash_blake2b_state base_state); private: - cl_zogminer * miner; + cl_gpukernel * miner; bool GPU; bool initOK; static const uint32_t PROOFSIZE = 1 << EK; diff --git a/src/miner.cpp b/src/miner.cpp index 395e3656022..8ec412fa9e7 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -459,17 +459,18 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) 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) { - GPUSolver * solver; - solver = new GPUSolver(conf.selGPU); + g_solver = new GPUSolver(conf.selGPU); LogPrint("pow", "Using Equihash solver GPU with n = %u, k = %u\n", n, k); - } else { - std::string solver = GetArg("-equihashsolver", "default"); - assert(solver == "tromp" || solver == "GPU" || solver == "default"); - LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); } + std::string solver = GetArg("-equihashsolver", "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; @@ -549,8 +550,8 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) } // (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] @@ -626,28 +627,26 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) break; } } - } 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) { + if (found) break; - } } else { - bool found = solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state) { - if (found) { - break; - } - } + bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); + 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(); @@ -676,25 +675,19 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) { LogPrintf("ZcashMiner terminated\n"); if(conf.useGPU) - delete solver; - if(tmp_header) - free(tmp_header); + delete g_solver; throw; } catch (const std::runtime_error &e) { LogPrintf("ZcashMiner runtime error: %s\n", e.what()); if(conf.useGPU) - delete solver; - if(tmp_header) - free(tmp_header); + delete g_solver; return; } if(conf.useGPU) - delete solver; - if(tmp_header) - free(tmp_header); + delete g_solver; c.disconnect(); } @@ -726,4 +719,33 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) 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(); + + 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); From 56b41abe52fdb156025d9ff0f858851fcd63c07f Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 16:22:45 +1100 Subject: [PATCH 09/26] small changes to miner.cpp for building --- src/Makefile.am | 4 ++-- src/libgpusolver/libgpusolver.h | 2 +- src/miner.cpp | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 0e00994504b..aa676f56952 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -86,7 +86,7 @@ LIBGPUSOLVER_H = \ libgpusolver/libgpusolver.h \ libgpusolver/gpuconfig.h \ libgpusolver/kernels/silentarmy.h \ - libgpusolver/libkernel.h \ + libgpusolver/libclwrapper.h \ libgpusolver/cl.hpp \ libgpusolver/blake.h \ libgpusolver/param.h @@ -464,7 +464,7 @@ libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT libgpusolver_a_SOURCES = \ libgpusolver/libgpusolver.cpp \ - libgpusolver/libkernel.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 diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index e3e0368c4d0..cfec4fc8d97 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -66,7 +66,7 @@ class GPUSolver { public: GPUSolver(); - GPUSolver(int64_t selGPU); + GPUSolver(unsigned selGPU); ~GPUSolver(); bool run(unsigned int n, unsigned int k, uint8_t *header, size_t header_len, uint64_t nonce, const std::function)> validBlock, diff --git a/src/miner.cpp b/src/miner.cpp index 8ec412fa9e7..3a4ebf45532 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -627,7 +627,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) break; } } - } + } try { if(!conf.useGPU) { @@ -638,7 +638,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) break; } else { bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); - if (found) + if (found) break; } } catch (EhSolverCancelledException&) { @@ -714,9 +714,6 @@ 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)); } From 93bd2ff022bee6e9c4ca34c46f6f2b9b59e70cd3 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 16:53:43 +1100 Subject: [PATCH 10/26] correcting build issues --- src/Makefile.am | 2 +- src/Makefile.zcash.include | 2 +- src/libgpusolver/libclwrapper.cpp | 18 +++++++++--------- src/libgpusolver/libclwrapper.h | 6 +++--- src/libgpusolver/libgpusolver.cpp | 4 ++-- src/libgpusolver/libgpusolver.h | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index aa676f56952..6ca0e68bb69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la LIBZCASH=libzcash.a -LIBGPUMINER=libgpuminer.a +LIBGPUSOLVER=libgpusolver.a $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include index 341ba273621..c4743cff76c 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 diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index 268cf6aaa19..019c6361395 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -11,8 +11,8 @@ #include #include //#include -#include "cl_gpuminer.h" -#include "kernels/cl_gpuminer_kernel.h" // Created from CMake +#include "libclwrapper.h" +#include "kernels/silentarmy.h" // Created from CMake // workaround lame platforms #if !CL_VERSION_1_2 @@ -71,7 +71,7 @@ cl_gpuminer::cl_gpuminer() dst_solutions = (uint32_t *) malloc(10*NUM_INDICES*sizeof(uint32_t)); if(dst_solutions == NULL) - std::cout << "Error allocating dst_solutions array!" << std::endl; + std::cout << "Error allocating dst_solutions array!" << std::endl; } @@ -426,10 +426,10 @@ void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t 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]); @@ -439,18 +439,18 @@ void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t 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)); + 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)); diff --git a/src/libgpusolver/libclwrapper.h b/src/libgpusolver/libclwrapper.h index 53f4414e9d7..744917e165c 100644 --- a/src/libgpusolver/libclwrapper.h +++ b/src/libgpusolver/libclwrapper.h @@ -64,13 +64,13 @@ typedef struct debug_s typedef uint32_t eh_index; -class cl_gpukernel +class cl_gpuminer { public: - cl_gpukernel(); - ~cl_gpukernel(); + cl_gpuminer(); + ~cl_gpuminer(); static bool searchForAllDevices(unsigned _platformId, std::function _callback); static bool searchForAllDevices(std::function _callback); diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index d0df72ea1e3..64fc35586e8 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -46,7 +46,7 @@ GPUSolver::GPUSolver() { size_t global_work_size = 1 << 20; size_t local_work_size = 32; - miner = new cl_wrapper(); + miner = new cl_gpuminer(); indices = (sols_t *) malloc(sizeof(sols_t)); if(indices == NULL) @@ -76,7 +76,7 @@ GPUSolver::GPUSolver(unsigned selGPU) { size_t global_work_size = 1 << 20; size_t local_work_size = 32; - miner = new cl_wrapper(); + miner = new cl_gpuminer(); indices = (sols_t *) malloc(sizeof(sols_t)); if(indices == NULL) diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index cfec4fc8d97..3a452be2f62 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -74,7 +74,7 @@ class GPUSolver { crypto_generichash_blake2b_state base_state); private: - cl_gpukernel * miner; + cl_gpuminer * miner; bool GPU; bool initOK; static const uint32_t PROOFSIZE = 1 << EK; From 18fbcb7ce3d9234cb2c15059985c91db2d866f63 Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 17:36:23 +1100 Subject: [PATCH 11/26] modying build --- src/Makefile.am | 2 +- src/Makefile.zcash.include | 20 ++++++++++++++++++++ zcutil/build.sh | 6 ++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 6ca0e68bb69..e5cd9da1282 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -467,7 +467,7 @@ libgpusolver_a_SOURCES = \ 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_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -fPIE -fvisibility=hidden libgpusolver_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include index c4743cff76c..546dd7944be 100644 --- a/src/Makefile.zcash.include +++ b/src/Makefile.zcash.include @@ -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/zcutil/build.sh b/zcutil/build.sh index 8cf1b4cbafa..f4a593c509c 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -39,7 +39,9 @@ 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 +#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 -Werror -g' make "$@" V=1 From d4a0631e0fa268f7ed35b72dba0efe304c648f8f Mon Sep 17 00:00:00 2001 From: Age Date: Sat, 29 Oct 2016 18:11:40 +1100 Subject: [PATCH 12/26] build issues --- configure.ac | 9 ++++++--- src/Makefile.am | 2 +- src/Makefile.test.include | 2 +- zcutil/build.sh | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) 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 e5cd9da1282..6ca0e68bb69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -467,7 +467,7 @@ libgpusolver_a_SOURCES = \ 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 -fPIE -fvisibility=hidden +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 diff --git a/src/Makefile.test.include b/src/Makefile.test.include index e296428054b..691b2a3414e 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) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/zcutil/build.sh b/zcutil/build.sh index f4a593c509c..3022860737d 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -42,6 +42,6 @@ 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}" --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 From 821e8c16bda8ebb3d96a4c5ac3bcde3417df9f74 Mon Sep 17 00:00:00 2001 From: nginnever Date: Sat, 29 Oct 2016 01:17:58 -0700 Subject: [PATCH 13/26] solo working, very slowly --- src/Makefile.am | 4 +++- src/Makefile.gtest.include | 2 +- src/Makefile.test.include | 2 +- src/miner.cpp | 32 ++++++++++++++++---------------- zcutil/build.sh | 3 ++- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 6ca0e68bb69..1ae96b16362 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,7 @@ EXTRA_LIBRARIES = \ libbitcoin_cli.a \ libzcash.a \ libgpusolver.a + if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += libbitcoin_wallet.a @@ -376,7 +377,8 @@ zcashd_LDADD = \ $(LIBZCASH) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ - $(LIBSECP256K1) + $(LIBSECP256K1) \ + $(LIBGPUSOLVER) if ENABLE_WALLET zcashd_LDADD += libbitcoin_wallet.a 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 691b2a3414e..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) $(LIBGPUSOLVER) $(LIBGPUSOLVER_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/miner.cpp b/src/miner.cpp index 3a4ebf45532..494c885a1a3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -629,23 +629,23 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) } } - 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 - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); - if (found) - break; - } - } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); - std::lock_guard lock{m_cs}; - cancelSolver = false; + 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 - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); + 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 diff --git a/zcutil/build.sh b/zcutil/build.sh index 3022860737d..fcb94750660 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -42,6 +42,7 @@ 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 -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 From 050a47a255a13f261a531935c50584891d0d1862 Mon Sep 17 00:00:00 2001 From: nginnever Date: Sat, 29 Oct 2016 03:42:54 -0700 Subject: [PATCH 14/26] solver doesn't hang, invalid sols --- src/libgpusolver/libgpusolver.cpp | 4 ++-- src/miner.cpp | 2 +- zcutil/build.sh | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index 64fc35586e8..d9779ad1bfc 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -74,7 +74,7 @@ GPUSolver::GPUSolver(unsigned selGPU) { */ //TODO This looks like IND_PER_BUCKET, enough for GPU? size_t global_work_size = 1 << 20; - size_t local_work_size = 32; + size_t local_work_size = 32; miner = new cl_gpuminer(); @@ -196,7 +196,7 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce 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; + std::cout << "Valid block found!" << std::endl; return true; } } diff --git a/src/miner.cpp b/src/miner.cpp index 494c885a1a3..d4dda7e6413 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -459,7 +459,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); - uint64_t nn= 0; + uint64_t nn = 0; GPUSolver * g_solver; // If zcash.conf GPU=1 if(conf.useGPU) { diff --git a/zcutil/build.sh b/zcutil/build.sh index fcb94750660..a04212323e4 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -42,7 +42,7 @@ 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}" --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}" --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 From 3339745b9e7e96186e766a4b4193cafb75d6edc2 Mon Sep 17 00:00:00 2001 From: Age Date: Sun, 30 Oct 2016 00:50:12 +1100 Subject: [PATCH 15/26] solo mining on single card and basic infrastructure for multi gpu support --- src/init.cpp | 9 ++++-- src/libgpusolver/gpuconfig.h | 1 + src/libgpusolver/libclwrapper.cpp | 1 - src/libgpusolver/libgpusolver.cpp | 12 ++++---- src/libgpusolver/libgpusolver.h | 2 +- src/miner.cpp | 47 +++++++++++++++++++++++++++---- 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 584bf2f1fb1..108ac603be9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -386,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("-2x", _("If -G is enabled this will create two threads per GPU device (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)); @@ -1495,9 +1499,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Generate coins in the background if (pwalletMain){ GPUConfig conf; - conf.useGPU = GetBoolArg("-GPU", false); + conf.useGPU = GetBoolArg("-G", false); conf.selGPU = GetArg("-deviceid", 0); - conf.allGPU = GetArg("-allgpu", 0); + conf.allGPU = GetBoolArg("-allgpu", 0); + conf.twoThreadsPerCard = GetBoolArg("-2x", 0); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1),conf); } #endif diff --git a/src/libgpusolver/gpuconfig.h b/src/libgpusolver/gpuconfig.h index 48042de76c8..0b94aeec0a0 100644 --- a/src/libgpusolver/gpuconfig.h +++ b/src/libgpusolver/gpuconfig.h @@ -33,6 +33,7 @@ class GPUConfig { bool useGPU; unsigned selGPU; bool allGPU; + bool twoThreadsPerCard; unsigned currentPlatform; unsigned currentDevice; unsigned globalWorkSize; diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index 019c6361395..d3eb197aef5 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -395,7 +395,6 @@ bool cl_gpuminer::init( void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t * indices, uint32_t * n_sol, uint64_t * ptr) { - printf("IN CLRUN CLASS\n"); try { diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index d9779ad1bfc..dc332f24651 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -28,7 +28,7 @@ #include "primitives/block.h" #include "arith_uint256.h" -#define DEBUG +//#define DEBUG char *s_hexdump(const void *_a, uint32_t a_len) { @@ -66,7 +66,7 @@ GPUSolver::GPUSolver() { } -GPUSolver::GPUSolver(unsigned selGPU) { +GPUSolver::GPUSolver(unsigned platform, unsigned device) { /* Notes I've added some extra parameters in this interface to assist with dev, such as @@ -88,7 +88,7 @@ GPUSolver::GPUSolver(unsigned selGPU) { @params: unsigned localWorkSizes @params: unsigned globalWorkSizes */ - GPU = miner->configureGPU(0, local_work_size, global_work_size); + 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; @@ -101,7 +101,7 @@ GPUSolver::GPUSolver(unsigned selGPU) { */ 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, selGPU, kernels); + initOK = miner->init(platform, device, kernels); } @@ -141,7 +141,7 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce */ if(GPU && initOK) { - auto t = std::chrono::high_resolution_clock::now(); + // auto t = std::chrono::high_resolution_clock::now(); uint64_t ptr; miner->run(header, header_len, nonce, indices, &n_sol, &ptr); @@ -150,6 +150,7 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce 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(); @@ -163,6 +164,7 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce if(!(counter % 10)) std::cout << "Kernel run took " << milis << " ms. (" << avg << " H/s)" << std::endl; +*/ size_t checkedSols = n_sol; size_t s = 0; diff --git a/src/libgpusolver/libgpusolver.h b/src/libgpusolver/libgpusolver.h index 3a452be2f62..ca0c463e0c6 100644 --- a/src/libgpusolver/libgpusolver.h +++ b/src/libgpusolver/libgpusolver.h @@ -66,7 +66,7 @@ class GPUSolver { public: GPUSolver(); - GPUSolver(unsigned selGPU); + 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, diff --git a/src/miner.cpp b/src/miner.cpp index d4dda7e6413..86e3f1b496a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -447,7 +447,11 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese 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(); @@ -463,7 +467,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) GPUSolver * g_solver; // If zcash.conf GPU=1 if(conf.useGPU) { - g_solver = new GPUSolver(conf.selGPU); + g_solver = new GPUSolver(conf.currentPlatform, conf.selGPU); LogPrint("pow", "Using Equihash solver GPU with n = %u, k = %u\n", n, k); } @@ -535,7 +539,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) 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()); @@ -638,6 +642,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) break; } else { bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); + ehSolverRuns.increment(); if (found) break; } @@ -719,8 +724,38 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig conf) { - static boost::thread_group* minerThreads = NULL; + static boost::thread_group* minerThreads = NULL; + if(conf.useGPU) + { + minerThreads = new boost::thread_group(); + conf.currentPlatform =0; + conf.currentDevice = conf.selGPU; + if(conf.allGPU) + { + static unsigned numPlatforms = cl_gpuminer::getNumPlatforms(); + for(unsigned platform = 0; platform < numPlatforms; ++platform){ + static unsigned noDevices = cl_gpuminer::getNumDevices(platform); + fprintf(stderr, "noDevices:%u", noDevices); + for(unsigned j = 0; j < noDevices; ++j){ + conf.currentPlatform = platform; + conf.currentDevice = j; + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + + if(conf.twoThreadsPerCard) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + + } + } + } + else + + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + if(conf.twoThreadsPerCard) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + } + else + { if (nThreads < 0) { // In regtest threads defaults to 1 if (Params().DefaultMinerThreads()) @@ -740,9 +775,9 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig return; minerThreads = new boost::thread_group(); - for (int i = 0; i < nThreads; i++) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); -} + } +} #endif // ENABLE_WALLET From 3764d38fe64f79b2950da5ad01d6c77f040f8f5a Mon Sep 17 00:00:00 2001 From: Age Date: Sun, 30 Oct 2016 14:12:30 +1100 Subject: [PATCH 16/26] fixing threading issues --- src/init.cpp | 2 -- src/libgpusolver/gpuconfig.h | 1 - src/miner.cpp | 63 +++++++++++++++++------------------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 108ac603be9..f9cd87bf9e1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -389,7 +389,6 @@ std::string HelpMessage(HelpMessageMode mode) 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("-2x", _("If -G is enabled this will create two threads per GPU device (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)); @@ -1502,7 +1501,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) conf.useGPU = GetBoolArg("-G", false); conf.selGPU = GetArg("-deviceid", 0); conf.allGPU = GetBoolArg("-allgpu", 0); - conf.twoThreadsPerCard = GetBoolArg("-2x", 0); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1),conf); } #endif diff --git a/src/libgpusolver/gpuconfig.h b/src/libgpusolver/gpuconfig.h index 0b94aeec0a0..48042de76c8 100644 --- a/src/libgpusolver/gpuconfig.h +++ b/src/libgpusolver/gpuconfig.h @@ -33,7 +33,6 @@ class GPUConfig { bool useGPU; unsigned selGPU; bool allGPU; - bool twoThreadsPerCard; unsigned currentPlatform; unsigned currentDevice; unsigned globalWorkSize; diff --git a/src/miner.cpp b/src/miner.cpp index 86e3f1b496a..2e82e02ccc3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -467,7 +467,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) GPUSolver * g_solver; // If zcash.conf GPU=1 if(conf.useGPU) { - g_solver = new GPUSolver(conf.currentPlatform, conf.selGPU); + g_solver = new GPUSolver(conf.currentPlatform, conf.currentDevice); LogPrint("pow", "Using Equihash solver GPU with n = %u, k = %u\n", n, k); } @@ -725,38 +725,8 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig conf) { static boost::thread_group* minerThreads = NULL; - if(conf.useGPU) - { - minerThreads = new boost::thread_group(); - conf.currentPlatform =0; - conf.currentDevice = conf.selGPU; - if(conf.allGPU) - { - static unsigned numPlatforms = cl_gpuminer::getNumPlatforms(); - for(unsigned platform = 0; platform < numPlatforms; ++platform){ - static unsigned noDevices = cl_gpuminer::getNumDevices(platform); - fprintf(stderr, "noDevices:%u", noDevices); - for(unsigned j = 0; j < noDevices; ++j){ - conf.currentPlatform = platform; - conf.currentDevice = j; - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - - if(conf.twoThreadsPerCard) - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - - } - } - } - else - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - if(conf.twoThreadsPerCard) - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - - } - else - { - if (nThreads < 0) { + if (nThreads < 0) { // In regtest threads defaults to 1 if (Params().DefaultMinerThreads()) nThreads = Params().DefaultMinerThreads(); @@ -774,10 +744,35 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig if (nThreads == 0 || !fGenerate) return; - minerThreads = new boost::thread_group(); + minerThreads = new boost::thread_group(); + // If using GPU + if(conf.useGPU) + { + conf.currentPlatform =0; + conf.currentDevice = conf.selGPU; + for (int i = 0; i < nThreads; i++){ + if(conf.allGPU) + { + unsigned numPlatforms = cl_gpuminer::getNumPlatforms(); + for(unsigned platform = 0; platform < numPlatforms; ++platform){ + unsigned noDevices = cl_gpuminer::getNumDevices(platform); + fprintf(stderr, "noDevices:%u", noDevices); + for(unsigned device = 0; device < noDevices; ++device){ + conf.currentPlatform = platform; + conf.currentDevice = device; + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + } + } + } + else + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + } + } + else + { for (int i = 0; i < nThreads; i++) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - } + } } #endif // ENABLE_WALLET From 76767f97131378f4a480febb3412956292b953a1 Mon Sep 17 00:00:00 2001 From: Age Date: Sun, 30 Oct 2016 23:24:56 +1100 Subject: [PATCH 17/26] correcting nonce issues --- src/libgpusolver/libclwrapper.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index d3eb197aef5..5abfbf0fa58 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -399,18 +399,16 @@ void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t { blake2b_state_t blake; - cl::Buffer buf_blake_st; + 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); - if (header_len == ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN) - memset(nonce_ptr, 0, ZCASH_NONCE_LEN); - // add the nonce - //*nonce_ptr += nonce; + 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)); @@ -469,6 +467,8 @@ void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t //print_sols(sols, nonce, nr_valid_sols); + //printf("\nSolutions: %u\n", sol_found); + *n_sol = sol_found; memcpy(indices, sols, sizeof(sols_t)); From 08ada7f60ad4181bf6425f7b9feb0e3f2a5689ef Mon Sep 17 00:00:00 2001 From: Age Date: Mon, 31 Oct 2016 00:39:29 +1100 Subject: [PATCH 18/26] fix for -equihashsolver=tromp option --- src/libgpusolver/libclwrapper.cpp | 1 - src/miner.cpp | 83 ++++++++++++++++--------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index 5abfbf0fa58..88016703a17 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -288,7 +288,6 @@ bool cl_gpuminer::init( ) { // get all platforms - printf("IN INIT CLASS\n"); try { vector platforms = getPlatforms(); diff --git a/src/miner.cpp b/src/miner.cpp index 2e82e02ccc3..f91f3bac034 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -599,7 +599,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) }; // 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); @@ -632,46 +632,47 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) } } } - - 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 - ZCASH_NONCE_LEN, nn++, 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); - 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 - ZCASH_NONCE_LEN, nn++, 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); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) + { + // Changing pblock->nTime can change work required on testnet: + hashTarget.SetCompact(pblock->nBits); + } } } } From b8f34596c39c5fb503455f4b3984d1f84ce116a8 Mon Sep 17 00:00:00 2001 From: Omar Alvarez Date: Sun, 30 Oct 2016 18:17:58 +0100 Subject: [PATCH 19/26] 192 bit nonce fix and block validation fix --- src/libgpusolver/libclwrapper.cpp | 4 ++-- src/libgpusolver/libgpusolver.cpp | 10 +++++----- src/miner.cpp | 20 +++++++++++++++----- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/libgpusolver/libclwrapper.cpp b/src/libgpusolver/libclwrapper.cpp index 88016703a17..8c1b0ad1906 100644 --- a/src/libgpusolver/libclwrapper.cpp +++ b/src/libgpusolver/libclwrapper.cpp @@ -406,8 +406,8 @@ void cl_gpuminer::run(uint8_t *header, size_t header_len, uint64_t nonce, sols_t 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; + //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)); diff --git a/src/libgpusolver/libgpusolver.cpp b/src/libgpusolver/libgpusolver.cpp index dc332f24651..edbbbee419b 100644 --- a/src/libgpusolver/libgpusolver.cpp +++ b/src/libgpusolver/libgpusolver.cpp @@ -145,10 +145,10 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce uint64_t ptr; miner->run(header, header_len, nonce, indices, &n_sol, &ptr); - uint256 nNonce = ArithToUint256(ptr); - crypto_generichash_blake2b_update(&base_state, + //uint256 nNonce = ArithToUint256(ptr); + /*crypto_generichash_blake2b_update(&base_state, nNonce.begin(), - nNonce.size()); + nNonce.size());*/ /* auto d = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t); @@ -183,8 +183,8 @@ bool GPUSolver::GPUSolve200_9(uint8_t *header, size_t header_len, uint64_t nonce std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS_S); #ifdef DEBUG bool isValid; - LogPrint("pow", "Checking with = %s\n", - nNonce.ToString()); + /*LogPrint("pow", "Checking with = %s\n", + nNonce.ToString());*/ EhIsValidSolution(200, 9, base_state, sol_char, isValid); std::cout << "is valid: " << isValid << '\n'; if (!isValid) { diff --git a/src/miner.cpp b/src/miner.cpp index f91f3bac034..7d3a38ec142 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -463,13 +463,15 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); - uint64_t nn = 0; + //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 == "GPU" || solver == "default"); @@ -526,10 +528,11 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) int64_t nStart = GetTime(); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - uint8_t * header = (uint8_t *) calloc(ZCASH_BLOCK_HEADER_LEN, sizeof(uint8_t)); 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; @@ -547,11 +550,16 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) crypto_generichash_blake2b_state curr_state; curr_state = state; - if(!conf.useGPU) { + //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 with nNonce = %s\n", @@ -641,7 +649,7 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) if (found) break; } else { - bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN - ZCASH_NONCE_LEN, nn++, validBlock, cancelledGPU, curr_state); + bool found = g_solver->run(n, k, header, ZCASH_BLOCK_HEADER_LEN, 0, validBlock, cancelledGPU, curr_state); ehSolverRuns.increment(); if (found) break; @@ -667,6 +675,8 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) // 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) { From af9fa73a90658cfaa259ac44f513f0a4537a44f1 Mon Sep 17 00:00:00 2001 From: hellcatz Date: Sun, 30 Oct 2016 10:41:54 -0700 Subject: [PATCH 20/26] Fixed use of genproclimit per GPU in miner.cpp --- src/miner.cpp | 53 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 7d3a38ec142..d2d56f4f0be 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -756,34 +756,49 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig return; minerThreads = new boost::thread_group(); + // If using GPU - if(conf.useGPU) - { - conf.currentPlatform =0; - conf.currentDevice = conf.selGPU; - for (int i = 0; i < nThreads; i++){ - if(conf.allGPU) - { + if(conf.useGPU) { + + conf.currentPlatform =0; + conf.currentDevice = conf.selGPU; + + // use all available GPUs + if(conf.allGPU) { + unsigned numPlatforms = cl_gpuminer::getNumPlatforms(); - for(unsigned platform = 0; platform < numPlatforms; ++platform){ + + for(unsigned platform = 0; platform < numPlatforms; ++platform) { + unsigned noDevices = cl_gpuminer::getNumDevices(platform); + fprintf(stderr, "noDevices:%u", noDevices); - for(unsigned device = 0; device < noDevices; ++device){ + + for(unsigned device = 0; device < noDevices; ++device) { conf.currentPlatform = platform; conf.currentDevice = device; - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - } + + // genproclimit, threads per gpu + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + + } } + + } else { + + // genproclimit, threads on single gpu + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + } - else - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - } - } - else - { - for (int i = 0; i < nThreads; i++) + + } + else + { + for (int i = 0; i < nThreads; i++) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); - } + } } #endif // ENABLE_WALLET From 1e8921ab2eb3c425e14b52016fff8653fea921a3 Mon Sep 17 00:00:00 2001 From: Omar Alvarez Date: Sun, 30 Oct 2016 18:44:38 +0100 Subject: [PATCH 21/26] Allow -GPU and -G options --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index f9cd87bf9e1..e87d11042f9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1498,7 +1498,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Generate coins in the background if (pwalletMain){ GPUConfig conf; - conf.useGPU = GetBoolArg("-G", false); + conf.useGPU = GetBoolArg("-G", false) || GetBoolArg("-GPU", false); conf.selGPU = GetArg("-deviceid", 0); conf.allGPU = GetBoolArg("-allgpu", 0); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1),conf); From f454d9c43c09c44772f355acaae4cc41b9a95ea4 Mon Sep 17 00:00:00 2001 From: Omar Alvarez Date: Sun, 30 Oct 2016 19:07:59 +0100 Subject: [PATCH 22/26] Fixed mem leak --- src/miner.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/miner.cpp b/src/miner.cpp index d2d56f4f0be..4ac0f57f5c4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -692,6 +692,8 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) LogPrintf("ZcashMiner terminated\n"); if(conf.useGPU) delete g_solver; + free(header); + free(zero); throw; } catch (const std::runtime_error &e) @@ -699,11 +701,15 @@ void static BitcoinMiner(CWallet *pwallet, GPUConfig conf) 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(); } From 07ddb1493d75f933f3516ba321cfaa4ab5358ee7 Mon Sep 17 00:00:00 2001 From: hellcatz Date: Sun, 30 Oct 2016 15:05:27 -0700 Subject: [PATCH 23/26] Added GPU thread count memory limits. The max number of threads up to GenProcLimit will be used based on GPU memory limits. --- src/init.cpp | 2 + src/libgpusolver/gpuconfig.h | 1 + src/libgpusolver/libclwrapper.h | 4 +- src/miner.cpp | 72 ++++++++++++++++++++++++++++----- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index e87d11042f9..483976c2b44 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -389,6 +389,7 @@ std::string HelpMessage(HelpMessageMode mode) 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)); @@ -1501,6 +1502,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) 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 diff --git a/src/libgpusolver/gpuconfig.h b/src/libgpusolver/gpuconfig.h index 48042de76c8..ea4922881e0 100644 --- a/src/libgpusolver/gpuconfig.h +++ b/src/libgpusolver/gpuconfig.h @@ -33,6 +33,7 @@ class GPUConfig { bool useGPU; unsigned selGPU; bool allGPU; + bool forceGenProcLimit; unsigned currentPlatform; unsigned currentDevice; unsigned globalWorkSize; diff --git a/src/libgpusolver/libclwrapper.h b/src/libgpusolver/libclwrapper.h index 744917e165c..3818194bc65 100644 --- a/src/libgpusolver/libclwrapper.h +++ b/src/libgpusolver/libclwrapper.h @@ -79,6 +79,8 @@ class cl_gpuminer 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 @@ -112,8 +114,6 @@ class cl_gpuminer 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); - static std::vector getDevices(std::vector const& _platforms, unsigned _platformId); - static std::vector getPlatforms(); 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) { diff --git a/src/miner.cpp b/src/miner.cpp index 4ac0f57f5c4..66dfcd70510 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -766,36 +766,86 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads, GPUConfig // If using GPU if(conf.useGPU) { - conf.currentPlatform =0; + conf.currentPlatform = 0; conf.currentDevice = conf.selGPU; + vector platforms = cl_gpuminer::getPlatforms(); + // use all available GPUs if(conf.allGPU) { - unsigned numPlatforms = cl_gpuminer::getNumPlatforms(); + int devicesFound = 0; + unsigned numPlatforms = platforms.size(); for(unsigned platform = 0; platform < numPlatforms; ++platform) { - unsigned noDevices = cl_gpuminer::getNumDevices(platform); - - fprintf(stderr, "noDevices:%u", noDevices); - + 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; - // genproclimit, threads per gpu - for (int i = 0; i < nThreads; i++) + 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 { - // genproclimit, threads on single gpu - for (int i = 0; i < nThreads; i++) - minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, conf)); + // 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"); + } } From 4d91debbb66c3eef83ec2f6ec4d8fd4912023dfa Mon Sep 17 00:00:00 2001 From: nginnever Date: Sun, 30 Oct 2016 18:34:37 -0700 Subject: [PATCH 24/26] temporary print fix --- src/metrics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 2f3a4bd34c5a544639b049b3e9952ccbe09a96b9 Mon Sep 17 00:00:00 2001 From: Age Date: Mon, 31 Oct 2016 17:14:52 +1100 Subject: [PATCH 25/26] updated silentarmy kernel. Support for GCN1 cards. --- src/libgpusolver/kernels/silentarmy.cl | 58 +++++++++++++++++++------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/libgpusolver/kernels/silentarmy.cl b/src/libgpusolver/kernels/silentarmy.cl index bc8ad0c92a8..02ffd133358 100644 --- a/src/libgpusolver/kernels/silentarmy.cl +++ b/src/libgpusolver/kernels/silentarmy.cl @@ -60,7 +60,7 @@ typedef struct sols_s { uint nr; - uint likely_invalidss; + uint likely_invalids; uchar valid[MAX_SOLS]; uint values[MAX_SOLS][(1 << PARAM_K)]; } sols_t; @@ -79,16 +79,17 @@ typedef struct sols_s ** 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 ** -** 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' is padding, 'B' is part of the colliding PREFIX -** ** 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 @@ -467,7 +468,12 @@ void kernel_round0(__global ulong *blake_state, __global char *ht, #endif /* -** XOR a pair of Xi values and store them in the hash table. +** 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. */ @@ -480,11 +486,17 @@ uint xor_and_store(uint round, __global char *ht_dst, uint row, // storing the byte containing bits from the previous PREFIX block for if (round == 1 || round == 2) { - // Note: round N xors bytes from round N-1 // 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) { @@ -499,6 +511,12 @@ uint xor_and_store(uint round, __global char *ht_dst, uint row, 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) { @@ -506,6 +524,12 @@ uint xor_and_store(uint round, __global char *ht_dst, uint row, 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) { @@ -513,6 +537,11 @@ uint xor_and_store(uint round, __global char *ht_dst, uint row, 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 @@ -587,8 +616,6 @@ void equihash_round(uint round, __global char *ht_src, __global char *ht_dst, #error "unsupported NR_SLOTS" #endif } - // drop the entire 0xAB byte (see description at top of this file) - uint adj = (!(round % 2)) ? 1 : 0; // XOR colliding pairs of Xi dropped_stor = 0; for (n = 0; n < nr_coll; n++) @@ -596,13 +623,14 @@ void equihash_round(uint round, __global char *ht_src, __global char *ht_dst, i = collisions[n] & 0xff; j = collisions[n] >> 8; a = (__global ulong *) - (ht_src + tid * NR_SLOTS * SLOT_LEN + i * SLOT_LEN + xi_offset - + adj); + (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 - + adj); + (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; @@ -707,7 +735,7 @@ void kernel_sols(__global char *ht0, __global char *ht1, __global sols_t *sols) #error "unsupported NR_ROWS_LOG" #endif if (tid == 0) - sols->nr = sols->likely_invalidss = 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; @@ -724,7 +752,7 @@ void kernel_sols(__global char *ht0, __global char *ht1, __global sols_t *sols) if (coll < sizeof (collisions) / sizeof (*collisions)) collisions[coll++] = ((ulong)ref_i << 32) | ref_j; else - atomic_inc(&sols->likely_invalidss); + atomic_inc(&sols->likely_invalids); } if (!coll) return ; From f8ee44fea126359778c24a156becb87337313943 Mon Sep 17 00:00:00 2001 From: Nathan Ginnever Date: Sun, 6 Nov 2016 22:09:39 -0800 Subject: [PATCH 26/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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?