From 3ff49cbf4ad20a938beb3b0912400e3479008ab4 Mon Sep 17 00:00:00 2001 From: xiaying Date: Wed, 18 Oct 2023 10:31:02 +0800 Subject: [PATCH 1/5] [MNN:Sync] Sync Internal 2.7.2 --- 3rd_party/OpenCLHeaders/CL/cl2.hpp | 7 +- CMakeLists.txt | 6 + docs/compile/cmake.md | 2 + express/Executor.cpp | 1 + express/MathOp.cpp | 13 +- include/MNN/MNNDefine.h | 2 +- include/MNN/expr/MathOp.hpp | 1 - llm/CMakeLists.txt | 21 + llm/include/llm.hpp | 133 ++ llm/include/tokenizer.hpp | 87 + llm/llm_demo.cpp | 22 + llm/src/llm.cpp | 375 ++++ llm/src/tokenizer.cpp | 393 +++++ package_scripts/win/build_lib_release.ps1 | 7 +- project/ios/MNN.xcodeproj/project.pbxproj | 8 + pymnn/pip_package/build_deps.py | 6 +- pymnn/src/MNNTools.cc | 57 +- schema/current/TensorflowOp_generated.h | 26 +- schema/default/TensorflowOp.fbs | 1 + source/backend/arm82/asm/arm32/MNNGeluFP16.S | 7 + source/backend/arm82/asm/arm64/MNNGeluFP16.S | 7 + source/backend/cpu/BinaryUtils.hpp | 100 +- source/backend/cpu/CPUBinary.cpp | 7 +- source/backend/cpu/CPULayerNorm.cpp | 3 +- source/backend/cpu/CPUPool.cpp | 54 +- source/backend/cpu/CPUPool.hpp | 64 + source/backend/cpu/CPURaster.cpp | 81 +- source/backend/cpu/CPUReduction.cpp | 9 +- source/backend/cpu/CPURelu.cpp | 53 + source/backend/cpu/CPURelu.hpp | 10 +- source/backend/cpu/CPUTopKV2.cpp | 110 +- source/backend/cpu/CPUUnary.cpp | 354 +++- source/backend/cpu/CPUUnary.hpp | 10 +- source/backend/cpu/arm/arm32/MNNGelu.S | 12 +- .../arm/arm32/MNNReluWithSlopeChannelInt8.S | 170 ++ .../backend/cpu/arm/arm32/bf16/MNNGelu_BF16.S | 7 + source/backend/cpu/arm/arm64/MNNGelu.S | 8 + .../arm/arm64/MNNReluWithSlopeChannelInt8.S | 147 ++ .../backend/cpu/arm/arm64/bf16/MNNGelu_BF16.S | 8 + .../low_memory/MNNPackedMatMulRemain_int8.S | 236 ++- source/backend/cpu/bf16/BF16Functions.cpp | 1 + .../backend/cpu/compute/CommonOptFunction.cpp | 272 ++- .../backend/cpu/compute/CommonOptFunction.h | 14 + .../cpu/compute/Convolution1x1Strassen.cpp | 49 +- .../cpu/compute/Convolution1x1Strassen.hpp | 2 +- .../cpu/compute/ConvolutionFloatFactory.cpp | 11 +- .../compute/DenseConvolutionTiledExecutor.cpp | 31 +- .../compute/DenseConvolutionTiledExecutor.hpp | 5 +- .../backend/cpu/compute/Int8FunctionsOpt.cpp | 28 +- source/backend/cpu/compute/Int8FunctionsOpt.h | 5 +- .../cpu/compute/StrassenMatmulComputor.cpp | 28 +- .../cpu/compute/StrassenMatmulComputor.hpp | 6 +- .../cpu/x86_x64/FunctionDispatcher.cpp | 1 + .../backend/cpu/x86_x64/avx/GemmFunction.hpp | 2 +- .../backend/cpu/x86_x64/avx/MathFunctions.cpp | 4 + .../cpu/x86_x64/avx/PackedFunction.cpp | 3 +- source/backend/cpu/x86_x64/avx/Vec8.hpp | 26 + .../cpu/x86_x64/avx512/PackedFunction.cpp | 1 + .../cpu/x86_x64/avxfma/MathFunctions.cpp | 4 + .../cpu/x86_x64/sse/FunctionSummary.hpp | 1 + .../backend/cpu/x86_x64/sse/MathFunctions.cpp | 49 +- source/backend/cuda/CMakeLists.txt | 5 + source/backend/cuda/core/CUDABackend.cpp | 9 +- source/backend/cuda/core/CUDABackend.hpp | 2 + .../backend/cuda/core/runtime/CUDARuntime.cpp | 66 + .../backend/cuda/core/runtime/CUDARuntime.hpp | 11 + .../cuda/execution/ConvCutlassExecution.cu | 41 + .../cuda/execution/ConvCutlassExecution.hpp | 2 +- .../cuda/execution/ConvWinogradExecution.cu | 56 +- .../cuda/execution/ConvWinogradExecution.hpp | 11 +- .../execution/DeconvSingleInputExecution.hpp | 2 +- .../backend/cuda/execution/MatMulExecution.cu | 90 +- .../cuda/execution/MatMulExecution.hpp | 13 +- .../execution/MultiInputConvExecution.hpp | 2 +- .../execution/MultiInputDeconvExecution.hpp | 2 +- .../bf16/ConvCutlassBf16Execution.hpp | 2 +- .../CutlassConvCommonExecution.cu | 13 +- .../CutlassConvCommonExecution.hpp | 11 +- .../CutlassDeconvCommonExecution.cu | 0 .../CutlassDeconvCommonExecution.hpp | 0 .../CutlassGemmBf16TensorCore.cu | 0 .../CutlassGemmCUDACoreFloat16.cu | 0 .../CutlassGemmCUDACoreFloat16Deconv.cu | 0 .../CutlassGemmCUDACoreFloat32.cu | 0 .../CutlassGemmCUDACoreFloat32Decov.cu | 0 .../CutlassGemmTensorCore.cu | 0 .../CutlassGemmTensorCore884.cu | 0 .../CutlassGemmTensorCoreDeconv.cu | 0 .../cutlass_common/tune/CudaCache_generated.h | 297 ++++ .../tune/CutlassGemmBatchedParamTune.hpp | 828 +++++++++ ...utlassGemmBatchedTensorFloat16TuneInfer.cu | 917 ++++++++++ .../tune/CutlassGemmParamTune.hpp | 1305 ++++++++++++++ .../cutlass_common/tune/CutlassGemmTune.hpp | 40 + .../tune/CutlassGemmTuneCommonExecution.hpp | 122 ++ .../tune/GemmBatchedTensorCoreFloat16Tune.cu | 1513 +++++++++++++++++ .../tune/GemmTensorCoreFloat16Tune.cu | 755 ++++++++ .../tune/GemmTensorCoreFloat16TuneInfer.cu | 1147 +++++++++++++ .../tune/make_cutlass_tune_param.py | 208 +++ .../cutlass_common/tune/schema/CudaCache.fbs | 18 + .../cutlass_common/tune/schema/README.md | 2 + .../cutlass_common/tune/schema/generate.sh | 20 + source/backend/metal/AllShader.cpp | 18 +- source/backend/metal/MetalUnary.mm | 4 +- source/backend/metal/shader/MetalUnary.metal | 19 +- source/backend/opencl/core/OpenCLBackend.cpp | 23 +- source/backend/opencl/core/OpenCLBackend.hpp | 2 + .../opencl/core/runtime/OpenCLRuntime.cpp | 2 +- .../execution/buffer/BinaryBufExecution.cpp | 4 + .../execution/buffer/ConvBufExecution.cpp | 5 + .../buffer/DepthwiseConvBufExecution.cpp | 2 +- .../execution/buffer/MatmulBufExecution.cpp | 2 + .../execution/buffer/PoolBufExecution.cpp | 34 +- .../execution/buffer/UnaryBufExecution.cpp | 5 + source/backend/opencl/execution/cl/binary.cl | 24 + .../backend/opencl/execution/cl/binary_buf.cl | 15 + .../execution/cl/binary_subgroup_buf.cl | 115 +- .../opencl/execution/cl/depthwise_deconv2d.cl | 11 +- .../backend/opencl/execution/cl/matmul_buf.cl | 201 ++- .../opencl/execution/cl/opencl_program.cc | 22 +- source/backend/opencl/execution/cl/pooling.cl | 16 +- .../opencl/execution/cl/pooling_buf.cl | 16 + .../execution/cl/pooling_subgroup_buf.cl | 94 +- source/backend/opencl/execution/cl/unary.cl | 5 + .../backend/opencl/execution/cl/unary_buf.cl | 5 + .../opencl/execution/cl/unary_subgroup_buf.cl | 30 +- .../opencl/execution/image/ConvExecution.cpp | 13 +- .../execution/image/EltwiseExecution.cpp | 4 + .../image/MultiInputDWDeconvExecution.cpp | 2 +- .../opencl/execution/image/PoolExecution.cpp | 47 +- .../opencl/execution/image/UnaryExecution.cpp | 19 +- .../opencl/execution/image/UnaryExecution.hpp | 1 + source/backend/opengl/GLBackend.hpp | 2 +- source/core/Pipeline.cpp | 12 +- source/geometry/GeometryDilation2D.cpp | 2 +- source/geometry/GeometryPoolGrad.cpp | 11 +- source/math/Vec.hpp | 410 ++++- source/shape/ShapeBinaryOp.cpp | 2 + source/shape/ShapePool.cpp | 21 +- source/shape/ShapeReduction.cpp | 9 +- source/shape/ShapeScatterNd.cpp | 8 +- test.sh | 42 + test/op/BinaryOPTest.cpp | 70 +- test/op/PReLUTest.cpp | 34 + test/op/ROIPoolingTest.cpp | 27 +- test/op/UnaryTest.cpp | 824 ++++++++- tools/converter/include/config.hpp | 2 +- tools/converter/source/common/cli.cpp | 4 +- tools/converter/source/optimizer/Program.cpp | 23 - .../source/optimizer/merge/FuseTemplateOp.cpp | 51 +- .../source/optimizer/onnxextra/OnnxEinsum.cpp | 67 +- .../optimizer/onnxextra/OnnxPooling.cpp | 3 +- .../optimizer/onnxextra/OnnxScatterND.cpp | 34 +- .../optimizer/postconvert/ReIndexTensor.cpp | 2 + tools/cpp/revertMNNModel.cpp | 8 +- tools/quantization/TensorStatistic.cpp | 30 +- tools/quantization/TensorStatistic.hpp | 5 +- tools/quantization/calibration.cpp | 242 ++- tools/quantization/calibration.hpp | 10 +- tools/quantization/quantized.cpp | 47 +- tools/script/testPTQ.py | 6 +- 160 files changed, 12759 insertions(+), 687 deletions(-) create mode 100644 llm/CMakeLists.txt create mode 100644 llm/include/llm.hpp create mode 100644 llm/include/tokenizer.hpp create mode 100644 llm/llm_demo.cpp create mode 100644 llm/src/llm.cpp create mode 100644 llm/src/tokenizer.cpp create mode 100644 source/backend/cpu/arm/arm32/MNNReluWithSlopeChannelInt8.S create mode 100644 source/backend/cpu/arm/arm64/MNNReluWithSlopeChannelInt8.S rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassConvCommonExecution.cu (95%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassConvCommonExecution.hpp (93%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassDeconvCommonExecution.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassDeconvCommonExecution.hpp (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmBf16TensorCore.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmCUDACoreFloat16.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmCUDACoreFloat16Deconv.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmCUDACoreFloat32.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmCUDACoreFloat32Decov.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmTensorCore.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmTensorCore884.cu (100%) rename source/backend/cuda/execution/{cutlass => cutlass_common}/CutlassGemmTensorCoreDeconv.cu (100%) create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CudaCache_generated.h create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedParamTune.hpp create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedTensorFloat16TuneInfer.cu create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CutlassGemmParamTune.hpp create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTune.hpp create mode 100644 source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp create mode 100644 source/backend/cuda/execution/cutlass_common/tune/GemmBatchedTensorCoreFloat16Tune.cu create mode 100644 source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16Tune.cu create mode 100644 source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16TuneInfer.cu create mode 100644 source/backend/cuda/execution/cutlass_common/tune/make_cutlass_tune_param.py create mode 100644 source/backend/cuda/execution/cutlass_common/tune/schema/CudaCache.fbs create mode 100644 source/backend/cuda/execution/cutlass_common/tune/schema/README.md create mode 100644 source/backend/cuda/execution/cutlass_common/tune/schema/generate.sh diff --git a/3rd_party/OpenCLHeaders/CL/cl2.hpp b/3rd_party/OpenCLHeaders/CL/cl2.hpp index f4ff8f895..491285264 100644 --- a/3rd_party/OpenCLHeaders/CL/cl2.hpp +++ b/3rd_party/OpenCLHeaders/CL/cl2.hpp @@ -7111,8 +7111,8 @@ class CommandQueue : public detail::Wrapper size_t num_local_workgroups, const cl_workgroup_qcom *local_workgroups_array, cl_uint num_events_in_wait_list, - const cl_event *event_wait_list, - cl_event *event) + const vector* events = NULL, + Event* event = NULL) const { cl_event tmp; cl_int err = detail::errHandler( @@ -7120,7 +7120,8 @@ class CommandQueue : public detail::Wrapper object_, recording, num_args, arg_array, num_global_offsets, global_offset_array, num_global_workgroups, global_workgroup_array, num_local_workgroups, local_workgroups_array, num_events_in_wait_list, - event_wait_list, &tmp), + (events != NULL && num_events_in_wait_list > 0) ? (cl_event*) &events->front() : NULL, + (event != NULL) ? &tmp : NULL), __ENQUEUE_READ_BUFFER_ERR); if (event != NULL && err == CL_SUCCESS) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e0a5f01d..e5e979117 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ option(MNN_BUILD_CODEGEN "Build with codegen" OFF) option(MNN_ENABLE_COVERAGE "Build with coverage enable" OFF) option(MNN_BUILD_PROTOBUFFER "Build with protobuffer in MNN" ON) option(MNN_BUILD_OPENCV "Build OpenCV api in MNN." OFF) +option(MNN_BUILD_LLM "Build llm library based MNN." OFF) option(MNN_INTERNAL "Build with MNN internal features, such as model authentication, metrics logging" OFF) option(MNN_JNI "Build MNN Jni for java to use" OFF) @@ -612,6 +613,11 @@ IF(MNN_BUILD_CODEGEN) include(${CMAKE_CURRENT_LIST_DIR}/codegen/CMakeLists.txt) ENDIF() +IF(MNN_BUILD_LLM) + # add_definitions(-DMNN_BUILD_LLM) + include(${CMAKE_CURRENT_LIST_DIR}/llm/CMakeLists.txt) +ENDIF() + # NPU IF(MNN_NPU) add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/source/backend/hiai/) diff --git a/docs/compile/cmake.md b/docs/compile/cmake.md index b2d24a6b3..5b4f1923f 100644 --- a/docs/compile/cmake.md +++ b/docs/compile/cmake.md @@ -45,6 +45,7 @@ MNN使用CMake构建项目,CMake中的宏定义列表如下: | MNN_CUDA_PROFILE | 是否打开CUDA profile工具,默认为`OFF` | | MNN_CUDA_QUANT | 是否打开CUDA 量化文件编译,默认为`OFF` | | MNN_CUDA_BF16 | 是否打开CUDA Bf16文件编译,默认为`OFF` | +| MNN_CUDA_TUNE_PARAM | 是否打开CUDA TUNE相关文件编译,目前仅支持安培及以上架构,默认为`OFF` | | MNN_TENSORRT | 是否构建`TensorRT`后端,默认为`OFF` | | MNN_COREML | 是否构建`CoreML`后端,默认为`OFF` | | MNN_NNAPI | 是否构建`NNAPI`后端,默认为`OFF` | @@ -82,3 +83,4 @@ MNN使用CMake构建项目,CMake中的宏定义列表如下: | MNN_OPENCV_BENCH | 构建MNN的OpenCV功能是否开启性能benchmark,默认为`OFF` | | MNN_VULKAN_IMAGE | 构建MNN的Vulkan后端时采用Image内存模式,以便支持FP16和部分移动端上GPU的加速,默认为`ON` | | MNN_LOW_MEMORY | 是否支持低内存模式,支持低内存模式使用权值量化模型并设置`low_memory`则会使用计算时反量化,默认为`OFF` | +| MNN_BUILD_LLM | 是否构建基于MNN的llm库和demo,默认为`OFF` | diff --git a/express/Executor.cpp b/express/Executor.cpp index 06b1e9953..195ee1105 100644 --- a/express/Executor.cpp +++ b/express/Executor.cpp @@ -535,6 +535,7 @@ void Executor::_makeCache(const std::vector& expr, bool forceCPU) { TensorUtils::getDescribe(tensor.get())->quantAttr.reset(new QuantAttr); auto quant = TensorUtils::getDescribe(tensor.get())->quantAttr.get(); quant->scale = TensorUtils::getDescribe(srcTensor)->quantAttr.get()->scale; + quant->zero = TensorUtils::getDescribe(srcTensor)->quantAttr.get()->zero; } TensorUtils::getDescribe(tensor.get())->index = (int)scheduleInfo.allTensors.size(); diff --git a/express/MathOp.cpp b/express/MathOp.cpp index a39af2d2c..0d5c8476c 100644 --- a/express/MathOp.cpp +++ b/express/MathOp.cpp @@ -329,6 +329,7 @@ VARP _Asin(VARP x) { return _Unary(x, UnaryOpOperation_ASIN); } + /*Computes acos of x element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float @@ -344,7 +345,7 @@ VARP _Acos(VARP x) /*Computes acosh of x element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float -Note: The output of atan will lie within the invertible range of tan, i.e (0.0, pi). +Note: The output of atan will lie within (0, +inf). The input lies in [1, +inf) Returns: A variable. Has the same type as x. */ @@ -368,7 +369,7 @@ VARP _Asinh(VARP x) /*Computes atanh of x element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float -Note: The output of atan will lie within the invertible range of tan, i.e (0.0, pi). +Note: The input of atanh will lie within (-1, 1). The output of atan will lie within (-inf, +inf). Returns: A variable. Has the same type as x. */ @@ -389,6 +390,7 @@ VARP _Cosh(VARP x) return _Unary(x, UnaryOpOperation_COSH); } + /*Computes sinh of x element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float @@ -404,7 +406,7 @@ VARP _Sinh(VARP x) /*Computes the Gauss error function of `x` element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float -Note: The output of atan will lie within the invertible range of tan, i.e (0.0, pi). +Note: The output of atan will lie within (-1.0, 1.0). The input will lie in (-inf, inf) Returns: A variable. Has the same type as x. */ @@ -428,7 +430,7 @@ VARP _Erfc(VARP x) /*Computes the inverse function for erf, for `x` element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Int or Halide_Type_Float -Note: The output of atan will lie within the invertible range of tan, i.e (0.0, pi). +Note: The input of atan will lie within (-1, 1). Returns: A variable. Has the same type as x. */ @@ -514,6 +516,7 @@ A variable. Has the same type as x. VARP _Tanh(VARP x) { return _Unary(x, UnaryOpOperation_TANH); } + /*Computes sigmoid of x element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Float @@ -524,6 +527,7 @@ VARP _Sigmoid(VARP x) { return _Unary(x, UnaryOpOperation_SIGMOID); } + /*Computes ((exponential of x) - 1) element-wise. Args: x: A variable. Must be one of the following types: Halide_Type_Float @@ -534,7 +538,6 @@ VARP _Expm1(VARP x) { return _Unary(x, UnaryOpOperation_EXPM1); } - /*Returns x + y element-wise. Args: x: A variable. Must be one of the following types: diff --git a/include/MNN/MNNDefine.h b/include/MNN/MNNDefine.h index 3d04e2bfa..40d816300 100644 --- a/include/MNN/MNNDefine.h +++ b/include/MNN/MNNDefine.h @@ -69,6 +69,6 @@ MNN_ERROR("Check failed: %s ==> %s\n", #success, #log); \ #define STR(x) STR_IMP(x) #define MNN_VERSION_MAJOR 2 #define MNN_VERSION_MINOR 7 -#define MNN_VERSION_PATCH 1 +#define MNN_VERSION_PATCH 2 #define MNN_VERSION STR(MNN_VERSION_MAJOR) "." STR(MNN_VERSION_MINOR) "." STR(MNN_VERSION_PATCH) #endif /* MNNDefine_h */ diff --git a/include/MNN/expr/MathOp.hpp b/include/MNN/expr/MathOp.hpp index 83e31e0a8..b01c42ffb 100644 --- a/include/MNN/expr/MathOp.hpp +++ b/include/MNN/expr/MathOp.hpp @@ -61,7 +61,6 @@ MNN_PUBLIC VARP _Atanh(VARP x); MNN_PUBLIC VARP _Reciprocal(VARP x); MNN_PUBLIC VARP _Log1p(VARP x); MNN_PUBLIC VARP _Gelu(VARP x); -//Only one but not in UnaryOPs MNN_PUBLIC VARP _Tanh(VARP x); MNN_PUBLIC VARP _Sigmoid(VARP x); MNN_PUBLIC VARP _Erf(VARP x); diff --git a/llm/CMakeLists.txt b/llm/CMakeLists.txt new file mode 100644 index 000000000..25778d513 --- /dev/null +++ b/llm/CMakeLists.txt @@ -0,0 +1,21 @@ +# include dir +include_directories(${CMAKE_CURRENT_LIST_DIR}/include/) + +# source files +FILE(GLOB SRCS ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp) + +if (MSVC) + # compile static lib, surrpot Winwows + add_library(llm STATIC ${SRCS}) + target_link_libraries(llm ${MNN_DEPS}) +else() + # compile dynamic so, support Linux/Mac + add_library(llm SHARED ${SRCS}) + set_target_properties(llm PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + target_link_libraries(llm ${MNN_DEPS}) +endif() +target_compile_features(llm PRIVATE cxx_std_17) + +add_executable(llm_demo ${CMAKE_CURRENT_LIST_DIR}/llm_demo.cpp) +target_compile_features(llm_demo PRIVATE cxx_std_17) +target_link_libraries(llm_demo llm) \ No newline at end of file diff --git a/llm/include/llm.hpp b/llm/include/llm.hpp new file mode 100644 index 000000000..a77e1be6e --- /dev/null +++ b/llm/include/llm.hpp @@ -0,0 +1,133 @@ +// +// llm.hpp +// +// Created by MNN on 2023/08/25. +// ZhaodeWang +// + +#ifndef LLM_hpp +#define LLM_hpp + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "tokenizer.hpp" + +using namespace MNN; +using namespace Express; + +class MNN_PUBLIC Llm { +public: + Llm() { + // default tokenier is senrencepiece + tokenizer_.reset(new Sentencepiece); + } + static Llm* createLLM(const std::string& path); + VARP gen_embedding(const std::vector& input_ids); + void load(const std::string& model_dir); + int forward(const std::vector& input_ids); + std::vector tokenizer_encode(const std::string& input_str); + std::string decode(int id); + std::string response(const std::string& input_str, std::ostream* os = &std::cout); + float load_progress() { return load_progress_; } + void reset(); +private: + virtual std::vector tokenizer(const std::string& query) = 0; + virtual VARP gen_attention_mask(int seq_len) = 0; + virtual VARP gen_position_ids(int seq_len) = 0; + virtual bool is_stop(int token_id) = 0; +protected: + // model configs + bool is_single_ = false; + int layer_nums_ = 0; + int hidden_size_ = 4096; + std::vector key_value_shape_ = {}; + std::string model_name_ = ""; + // gen info + int gen_seq_len_ = 0; + int all_seq_len_ = 0; + int max_seq_len_ = 256; + float load_progress_ = 0.f; + // tokenizer + std::unique_ptr tokenizer_; +private: + // MNN Modules + std::shared_ptr runtime_manager_; + std::vector> modules_; + std::vector past_key_values_; + // model dir + std::string model_dir_; + // tokenizer + std::vector word_decoder_; + std::unordered_map word_encoder_; +}; + +// some llm models +class Chatglm_6b : public Llm { +public: + Chatglm_6b() { + model_name_ = "Chatglm_6b"; + layer_nums_ = 28; + key_value_shape_ = {2, 0, 1, 32, 128}; + } +private: + virtual std::vector tokenizer(const std::string& query) override; + virtual VARP gen_attention_mask(int seq_len) override; + virtual VARP gen_position_ids(int seq_len) override; + virtual bool is_stop(int token_id) override; + int context_len_ = 0; +}; + +class Chatglm2_6b : public Llm { +public: + Chatglm2_6b() { + model_name_ = "Chatglm2_6b"; + layer_nums_ = 28; + key_value_shape_ = {2, 0, 1, 2, 128}; + } +private: + virtual std::vector tokenizer(const std::string& query) override; + virtual VARP gen_attention_mask(int seq_len) override; + virtual VARP gen_position_ids(int seq_len) override; + virtual bool is_stop(int token_id) override; +}; + + +class Qwen_7b : public Llm { +public: + Qwen_7b() { + model_name_ = "Qwen_7b"; + layer_nums_ = 32; + key_value_shape_ = {2, 1, 0, 32, 128}; + tokenizer_.reset(new Tiktoken); + } +private: + virtual std::vector tokenizer(const std::string& query) override; + virtual VARP gen_attention_mask(int seq_len) override; + virtual VARP gen_position_ids(int seq_len) override; + virtual bool is_stop(int token_id) override; +}; + +class Llama2_7b : public Llm { +public: + Llama2_7b() { + model_name_ = "Llama2_7b"; + layer_nums_ = 32; + key_value_shape_ = {2, 1, 32, 0, 128}; + } +private: + virtual std::vector tokenizer(const std::string& query) override; + virtual VARP gen_attention_mask(int seq_len) override; + virtual VARP gen_position_ids(int seq_len) override; + virtual bool is_stop(int token_id) override; +}; + +#endif // LLM_hpp diff --git a/llm/include/tokenizer.hpp b/llm/include/tokenizer.hpp new file mode 100644 index 000000000..059ebf244 --- /dev/null +++ b/llm/include/tokenizer.hpp @@ -0,0 +1,87 @@ +// +// tokenizer.hpp +// +// Created by MNN on 2023/09/25. +// ZhaodeWang +// + +#ifndef TOKENIZER_hpp +#define TOKENIZER_hpp + +#include +#include +#include +#include +#include +#include + +class Tokenizer { +public: + Tokenizer() = default; + virtual bool load(const std::string& filename) = 0; + virtual std::vector encode(const std::string& str) = 0; + virtual std::string decode(int id) = 0; +}; + +class Sentencepiece : public Tokenizer { +public: + Sentencepiece() = default; + virtual bool load(const std::string& filename) override; + virtual std::vector encode(const std::string& str) override; + virtual std::string decode(int id) override; +private: + enum ModelType { + UNIGRAM = 1, + BPE = 2, + WORD = 3, + CHAR = 4 + }; + enum PieceType { + NORMAL = 1, + UNKNOWN = 2, + CONTROL = 3, + USER_DEFINED = 4, + UNUSED = 5, + BYTE = 6 + }; + struct SentencePiece { + std::string piece; + float score; + PieceType type = PieceType::NORMAL; + }; + using EncodeResult = std::vector>; +private: + // model train type + ModelType type_ = BPE; + // byte fall back enable + bool byte_fall_back_ = true; + // unknown id. + int unk_id_ = 0; + // pieces from model + std::vector sentence_pieces_; + // piece -> id map for normal pieces + std::unordered_map pieces_; + // piece -> id map for control, unknown, and byte pieces + std::unordered_map reserved_id_map_; +private: + float get_score(int id) const; + bool is_unused(int id) const; + bool is_control(int id) const; + int piece_to_id(const std::string& w) const; + std::string byte_to_piece(unsigned char c) const; + EncodeResult bpe_encode(std::string_view str, float alpha = 0.f); +}; + +class Tiktoken : public Tokenizer { +public: + Tiktoken() = default; + virtual bool load(const std::string& filename) override; + virtual std::vector encode(const std::string& str) override; + virtual std::string decode(int id) override; +private: + std::vector decoder_; + std::vector tokens_; + std::vector token_ids_; +}; + +#endif // TOKENIZER_hpp \ No newline at end of file diff --git a/llm/llm_demo.cpp b/llm/llm_demo.cpp new file mode 100644 index 000000000..ab88e0247 --- /dev/null +++ b/llm/llm_demo.cpp @@ -0,0 +1,22 @@ +// +// llm_demo.cpp +// +// Created by MNN on 2023/03/24. +// ZhaodeWang +// + +#include "llm.hpp" +#include + +int main(int argc, const char* argv[]) { + if (argc < 2) { + std::cout << "Usage: ./llm_demo.out " << std::endl; + return 0; + } + std::string model_dir = argv[1]; + std::cout << "model path is " << model_dir << std::endl; + std::unique_ptr llm(Llm::createLLM(model_dir)); + llm->load(model_dir); + llm->response("你好"); + return 0; +} diff --git a/llm/src/llm.cpp b/llm/src/llm.cpp new file mode 100644 index 000000000..c80e30b7a --- /dev/null +++ b/llm/src/llm.cpp @@ -0,0 +1,375 @@ +// +// llm.cpp +// +// Created by MNN on 2023/08/25. +// ZhaodeWang +// + +#include + +#include "llm.hpp" +#include + +Llm* Llm::createLLM(const std::string& path) { + auto size = path.size(); + // end with '.mnn' is single model file, otherwise split block models + bool is_single = (size > 4 && + path[size - 4] == '.' && + path[size - 3] == 'm' && + path[size - 2] == 'n' && + path[size - 1] == 'n'); + // default is chatglm2 + Llm* llm = new Chatglm2_6b; + if (path.find("chatglm2") != std::string::npos) { + // llm = new Chatglm2_6b; + } else if (path.find("chatglm") != std::string::npos) { + llm = new Chatglm_6b; + } else if (path.find("codegeex2") != std::string::npos) { + llm = new Chatglm2_6b; + llm->model_name_ = "Codegeex2_6b"; + } else if (path.find("qwen") != std::string::npos) { + llm = new Qwen_7b; + } else if (path.find("llama2") != std::string::npos) { + llm = new Llama2_7b; + } else if (path.find("baichuan") != std::string::npos) { + llm = new Llama2_7b; + llm->model_name_ = "Baichuan2_7b"; + } + llm->is_single_ = is_single; + return llm; +} + +std::string Llm::response(const std::string& query, std::ostream* os) { + // init status + if (is_single_) { + key_value_shape_.insert(key_value_shape_.begin(), layer_nums_); + past_key_values_.push_back(_Input(key_value_shape_, NCHW)); + } else { + for (int i = 0; i < layer_nums_; i++) { + past_key_values_.push_back(_Input(key_value_shape_, NCHW)); + } + } + // response + auto st = std::chrono::system_clock::now(); + auto input_ids = tokenizer(query); + int token = forward(input_ids); + std::string output_str = decode(token); + *os << output_str << std::flush; + while (gen_seq_len_ < max_seq_len_) { + token = forward({token}); + if (is_stop(token)) { + *os << std::endl << std::flush; + break; + } + auto word = decode(token); + *os << word << std::flush; + output_str += word; + } + auto et = std::chrono::system_clock::now(); + auto duration = std::chrono::duration_cast(et - st); + printf("\n[speed: %f tok/s]\n", gen_seq_len_ / (duration.count() * 1e-6)); + return output_str; +} + +void Llm::reset() { + // TODO +} + +void Llm::load(const std::string& model_dir) { + model_dir_ = model_dir; + // init + ScheduleConfig config; + BackendConfig cpuBackendConfig; + config.type = MNN_FORWARD_CPU; + // config.type = MNN_FORWARD_CUDA; + config.numThread = 4; + cpuBackendConfig.precision = BackendConfig::Precision_Low; + cpuBackendConfig.memory = BackendConfig::Memory_Low; + config.backendConfig = &cpuBackendConfig; + runtime_manager_.reset(Executor::RuntimeManager::createRuntimeManager(config)); + // 1. load vocab + // std::string tokenizer_path = model_dir + "/tokenizer.model"; + std::string tokenizer_path = model_dir + "/tokenizer.txt"; + tokenizer_->load(tokenizer_path); + // 2. load model + Module::Config module_config; + module_config.shapeMutable = true; + module_config.rearrange = true; + load_progress_ = 0.f; + if (is_single_) { + modules_.resize(1); + std::string model_path = model_dir; + std::string external_path = model_dir + ".weight"; + printf("load %s ... ", model_path.c_str()); + runtime_manager_->setExternalFile(external_path); + modules_[0].reset(Module::load( + {"input_ids", "attention_mask", "position_ids", "past_key_values"}, + {"token_id", "presents"}, model_path.c_str(), runtime_manager_, &module_config)); + printf("Done!\n"); + fflush(stdout); + } else { + // 2. load models + modules_.resize(layer_nums_ + 2); + float step = 100.0 / modules_.size(); + char buffer[50]; + // load lm model + std::string lm_model_path = model_dir + "/lm.mnn"; + std::string embedding_model_path = model_dir + "/embedding.mnn"; + printf("[%3.0f%% ] load %s model ... ", load_progress_, lm_model_path.c_str()); + modules_[layer_nums_].reset(Module::load({}, {}, lm_model_path.c_str(), runtime_manager_, &module_config)); + printf("Done!\n"); + load_progress_ += step; + printf("[%3.0f%% ] load %s model ... ", load_progress_, embedding_model_path.c_str());fflush(stdout); + modules_[layer_nums_ + 1].reset(Module::load({}, {}, embedding_model_path.c_str(), runtime_manager_, &module_config)); + printf("Done!\n"); + load_progress_ += step; + // load glm_block models + for (int i = 0; i < layer_nums_; i++) { + load_progress_ += step; + std::string model_path = model_dir + "/block_" + std::to_string(i) + ".mnn"; + printf("[%3.0f%% ] load %s model ... ", load_progress_, model_path.c_str()); + modules_[i].reset(Module::load( + {"inputs_embeds", "attention_mask", "position_ids", "past_key_values"}, + {"hidden_states", "presents"}, model_path.c_str(), runtime_manager_, &module_config)); + printf("Done!\n"); + fflush(stdout); + } + } +} + +int Llm::forward(const std::vector& input_ids) { + int seq_len = input_ids.size(); + auto inputs_ids_ = _Const(input_ids.data(), {seq_len}, NCHW, halide_type_of()); + auto attention_mask = gen_attention_mask(seq_len); + auto position_ids = gen_position_ids(seq_len); + int id = -1; + if (is_single_) { + // single model + auto outputs = modules_.back()->onForward({inputs_ids_, attention_mask, position_ids, past_key_values_[0]}); + id = outputs[0]->readMap()[0]; + past_key_values_[0] = outputs[1]; + } else { + // split block models + auto hidden_states = modules_[layer_nums_ + 1]->onForward({inputs_ids_})[0]; + for (int i = 0; i < layer_nums_; i++) { + auto outputs = modules_[i]->onForward({hidden_states, attention_mask, position_ids, past_key_values_[i]}); + hidden_states = outputs[0]; + past_key_values_[i] = outputs[1]; + } + auto outputs = modules_[layer_nums_]->onForward({hidden_states}); + id = outputs[0]->readMap()[0]; + } + all_seq_len_ += seq_len; + gen_seq_len_++; + return id; +} + +VARP Llm::gen_embedding(const std::vector& input_ids) { + // disk embedding save memory + size_t seq_len = input_ids.size(); + auto embedding = _Input({static_cast(seq_len), 1, hidden_size_}, NCHW); + // auto embedding = _Input({1, static_cast(seq_len), hidden_size_}, NCHW); + size_t size = hidden_size_ * sizeof(int16_t); + std::string file_path = model_dir_ + "/slim_word_embeddings_bf16.bin"; + FILE* file = fopen(file_path.c_str(), "rb"); + std::unique_ptr buffer(new int16_t[hidden_size_]); + for (size_t i = 0; i < seq_len; i++) { + fseek(file, input_ids[i] * size, SEEK_SET); + fread(buffer.get(), 1, size, file); + auto ptr = embedding->writeMap() + i * hidden_size_ * 2; + for (int j = 0; j < hidden_size_; j++) { + ptr[j * 2] = 0; + ptr[j * 2 + 1] = buffer[j]; + } + } + fclose(file); + return embedding; +} + +std::vector Llm::tokenizer_encode(const std::string& input_str) { + auto ids = tokenizer_->encode(input_str); + return ids; +} + +std::string Llm::decode(int id) { + std::string word = tokenizer_->decode(id); + // Fix utf-8 garbled characters + if (word.length() == 6 && word[0] == '<' && word[word.length()-1] == '>' && word[1] == '0' && word[2] == 'x') { + int num = std::stoi(word.substr(3, 2), nullptr, 16); + word = static_cast(num); + } + return word; +} + +// Chatglm_6b +std::vector Chatglm_6b::tokenizer(const std::string& query) { + auto ids = tokenizer_encode(query); + context_len_ = ids.size(); + ids.push_back(130001); + ids.push_back(130004); + return ids; +} + +VARP Chatglm_6b::gen_attention_mask(int seq_len) { + auto attention_mask = _Input({1, 1, seq_len, seq_len}, NCHW, halide_type_of()); + auto ptr = attention_mask->writeMap(); + for (int i = 0; i < seq_len * seq_len; i++) { + ptr[i] = 0; + } + if (seq_len > 1) { + for (int i = 1; i < seq_len; i++) { + ptr[seq_len * i - 1] = 1; + } + } + return attention_mask; +} + +VARP Chatglm_6b::gen_position_ids(int seq_len) { + auto position_ids = _Input({1, 2, seq_len}, NCHW, halide_type_of()); + auto ptr = position_ids->writeMap(); + if (seq_len == 1) { + ptr[0] = 1; + ptr[1] = all_seq_len_ - context_len_; + } else { + for (int i = 0; i < seq_len; i++) { + ptr[i] = i; + ptr[seq_len + i] = 0; + } + ptr[2 * seq_len - 1] = 1; + } + return position_ids; +} + +bool Chatglm_6b::is_stop(int token_id) { + return token_id == 130005; +} + +// Chatglm2_6b +std::vector Chatglm2_6b::tokenizer(const std::string& query) { + auto prompt = "问:" + query + "\n答:"; + auto ids = tokenizer_encode(prompt); + ids.insert(ids.begin(), 64792); + ids.insert(ids.begin(), 64790); + return ids; +} + +VARP Chatglm2_6b::gen_attention_mask(int seq_len) { + auto attention_mask = _Input({1, 1, seq_len, seq_len}, NCHW, halide_type_of()); + auto ptr = attention_mask->writeMap(); + if (seq_len > 1) { + for (int i = 0; i < seq_len; i++) { + for (int j = 0; j < seq_len; j++) { + ptr[seq_len * i + j] = j > i; + } + } + } else { + ptr[0] = 0; + } + return attention_mask; +} + +VARP Chatglm2_6b::gen_position_ids(int seq_len) { + auto position_ids = _Input({seq_len}, NCHW, halide_type_of()); + auto ptr = position_ids->writeMap(); + if (seq_len == 1) { + ptr[0] = gen_seq_len_; + } else { + for (int i = 0; i < seq_len; i++) { + ptr[i] = i; + } + } + return position_ids; +} + +bool Chatglm2_6b::is_stop(int token_id) { + return token_id <= 2; +} + +// Qwen_7b +std::vector Qwen_7b::tokenizer(const std::string& query) { + auto ids = tokenizer_encode(query); + // auto prompt = "\n<|im_start|>user\n" + query + "<|im_end|>\n<|im_start|>assistant\n"; + ids.insert(ids.begin(), {198, 151644, 872, 198}); + ids.insert(ids.end(), {151645, 198, 151644, 77091, 198}); + return ids; +} + +VARP Qwen_7b::gen_attention_mask(int seq_len) { + auto attention_mask = _Input({1, 1, seq_len, seq_len}, NCHW, halide_type_of()); + auto ptr = attention_mask->writeMap(); + for (int i = 0; i < seq_len; i++) { + for (int j = 0; j < seq_len; j++) { + ptr[seq_len * i + j] = j <= i; + } + } + return attention_mask; +} + +VARP Qwen_7b::gen_position_ids(int seq_len) { + auto position_ids = _Input({seq_len}, NCHW, halide_type_of()); + auto ptr = position_ids->writeMap(); + if (seq_len == 1) { + ptr[0] = all_seq_len_; + } else { + for (int i = 0; i < seq_len; i++) { + ptr[i] = i; + } + } + return position_ids; +} + +bool Qwen_7b::is_stop(int token_id) { + return token_id >= 151645; +} + +// Llama2_7b +std::vector Llama2_7b::tokenizer(const std::string& query) { + auto ids = tokenizer_encode(query); + if (model_name_ == "Baichuan2_7b") { + // baichuan2: {query}: 195, query, 196 + ids.insert(ids.begin(), 195); + ids.push_back(196); + return ids; + } + // llama2: [INST]{query}[/INST]: 1, 5539, 25580, 29962, query, 12452, 25580, 29962 + ids.insert(ids.begin(), {1, 5539, 25580, 29962}); + ids.insert(ids.end(), {12452, 25580, 29962}); + return ids; +} + +VARP Llama2_7b::gen_attention_mask(int seq_len) { + if (seq_len == 1) { + auto attention_mask = _Input({1, 1, 1, all_seq_len_ + 1}, NCHW, halide_type_of()); + auto ptr = attention_mask->writeMap(); + for (int i = 0; i < all_seq_len_ + 1; i++) { + ptr[i] = 0; + } + return attention_mask; + } else { + auto attention_mask = _Input({1, 1, seq_len, seq_len}, NCHW, halide_type_of()); + auto ptr = attention_mask->writeMap(); + for (int i = 0; i < seq_len; i++) { + for (int j = 0; j < seq_len; j++) { + ptr[seq_len * i + j] = (j > i) * std::numeric_limits::lowest(); + } + } + return attention_mask; + } +} + +VARP Llama2_7b::gen_position_ids(int seq_len) { + auto position_ids = _Input({1, seq_len}, NCHW, halide_type_of()); + auto ptr = position_ids->writeMap(); + if (seq_len == 1) { + ptr[0] = all_seq_len_; + } else { + for (int i = 0; i < seq_len; i++) { + ptr[i] = i; + } + } + return position_ids; +} + +bool Llama2_7b::is_stop(int token_id) { + return token_id == 2; +} \ No newline at end of file diff --git a/llm/src/tokenizer.cpp b/llm/src/tokenizer.cpp new file mode 100644 index 000000000..0fee3e87e --- /dev/null +++ b/llm/src/tokenizer.cpp @@ -0,0 +1,393 @@ +// +// tokenizer.cpp +// +// Created by MNN on 2023/09/25. +// ZhaodeWang +// + +#include "tokenizer.hpp" +#include +#include +#include +#include +#include + +// base64 +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +static inline size_t one_char_len(const char *src) { + return "\1\1\1\1\1\1\1\1\1\1\1\1\2\2\3\4"[(*src & 0xFF) >> 4]; +} + +static std::string base64_decode(const std::string& str) { + int in_len = str.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( str[in_] != '=') && is_base64(str[in_])) { + char_array_4[i++] = str[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) { + char_array_4[i] = base64_chars.find(char_array_4[i]); + } + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + for (i = 0; (i < 3); i++) { + ret.push_back(char_array_3[i]); + } + i = 0; + } + } + if (i) { + for (j = i; j < 4; j++) { + char_array_4[j] = 0; + } + for (j = 0; j < 4; j++) { + char_array_4[j] = base64_chars.find(char_array_4[j]); + } + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + for (j = 0; (j < i - 1); j++) { + ret.push_back(char_array_3[j]); + } + } + return ret; +} + +bool Sentencepiece::load(const std::string& filename) { + std::ifstream tok_file(filename); + std::string line, token; + float score; + int index = 0, type; + while (std::getline(tok_file, line)) { + std::istringstream line_str(line); + line_str >> token >> score >> type; + token = base64_decode(token); + auto piece_type = static_cast(type); + SentencePiece piece {token, score, piece_type}; + sentence_pieces_.emplace_back(std::move(piece)); + if (piece_type == PieceType::NORMAL) { + pieces_.insert({token, index}); + } else { + reserved_id_map_.insert({token, index}); + if (piece_type == PieceType::UNKNOWN) { + unk_id_ = index; + } + } + index++; + } + tok_file.close(); + return true; +} + +int Sentencepiece::piece_to_id(const std::string& piece) const { + auto it = reserved_id_map_.find(piece); + if (it != reserved_id_map_.end()) { + return it->second; + } + auto it2 = pieces_.find(piece); + if (it2 != pieces_.end()) { + return it2->second; + } + return unk_id_; +} + +std::string Sentencepiece::byte_to_piece(unsigned char c) const { + const int len = ::snprintf(nullptr, 0, "<0x%02X>", c); + std::string s; + s.resize(len); + ::snprintf(&s[0], s.size() + 1, "<0x%02X>", c); + return s; +} + +// ref: https://github.com/google/sentencepiece/blob/master/src/bpe_model.cc +Sentencepiece::EncodeResult Sentencepiece::bpe_encode(std::string_view normalized, float alpha) { + // util class begin + struct SymbolPair { + int left; // left index of this pair + int right; // right index of this pair + float score; // score of this pair. large is better. + size_t size; // length of this piece + }; + + class SymbolPairComparator { + public: + const bool operator()(SymbolPair *h1, SymbolPair *h2) { + return (h1->score < h2->score || (h1->score == h2->score && h1->left > h2->left)); + } + }; + + struct Symbol { + int prev; // prev index of this symbol. -1 for BOS. + int next; // next index of tihs symbol. -1 for EOS. + bool freeze = false; // this symbol is never be merged. + std::string_view piece; + }; + // util class end + + using Agenda = std::priority_queue, SymbolPairComparator>; + Agenda agenda; + std::vector symbols; + symbols.reserve(normalized.size()); + // Reverse merge rules. key: merged symbol, value: pair of original symbols. + std::unordered_map> rev_merge; + // SymbolPair holder. + std::vector> symbol_pair_holder; + // Lookup new symbol pair at [left, right] and inserts it to agenda. + auto MaybeAddNewSymbolPair = [this, &symbol_pair_holder, &symbols, &agenda, &rev_merge](int left, int right) { + if (left == -1 || right == -1 || symbols[left].freeze || symbols[right].freeze) { + return; + } + const std::string_view piece(symbols[left].piece.data(), symbols[left].piece.size() + symbols[right].piece.size()); + std::string piece_str(piece); + const auto it = pieces_.find(piece_str); + if (it == pieces_.end()) { + return; + } + symbol_pair_holder.emplace_back(new SymbolPair); + auto *h = symbol_pair_holder.back().get(); + h->left = left; + h->right = right; + h->score = get_score(it->second); + h->size = piece.size(); + agenda.push(h); + + // Makes `rev_merge` for resegmentation. + if (is_unused(it->second)) { + rev_merge[piece] = std::make_pair(symbols[left].piece, symbols[right].piece); + } + }; + // Splits the input into character sequence + int index = 0; + while (!normalized.empty()) { + Symbol s; + // const int mblen = matcher_->PrefixMatch(normalized, &s.freeze); + int mblen = std::min(normalized.size(), one_char_len(normalized.data())); + s.piece = std::string_view(normalized.data(), mblen); + s.prev = index == 0 ? -1 : index - 1; + normalized.remove_prefix(mblen); + s.next = normalized.empty() ? -1 : index + 1; + ++index; + symbols.emplace_back(s); + } + + if (symbols.empty()) { + return {}; + } + // Lookup all bigrams. + for (size_t i = 1; i < symbols.size(); ++i) { + MaybeAddNewSymbolPair(i - 1, i); + } + + // BPE-dropout: https://arxiv.org/pdf/1910.13267.pdf + // std::mt19937 *rand_gen = nullptr; + std::mt19937 rand_gen; + auto skip_merge = [&]() { + if (alpha <= 0.0) return false; + if (alpha >= 1.0) return true; + // if (rand_gen == nullptr) rand_gen = random::GetRandomGenerator(); + std::uniform_real_distribution<> gen(0.0, 1.0); + return gen(rand_gen) < alpha; + }; + + // Main loop. + while (!agenda.empty()) { + SymbolPair *top = agenda.top(); + agenda.pop(); + + // `top` is no longer available. + if (symbols[top->left].piece.empty() || symbols[top->right].piece.empty() || + symbols[top->left].piece.size() + symbols[top->right].piece.size() != top->size) { + continue; + } + + if (skip_merge()) continue; + // Replaces symbols with `top` rule. + symbols[top->left].piece = std::string_view( + symbols[top->left].piece.data(), + symbols[top->left].piece.size() + symbols[top->right].piece.size()); + + // Updates prev/next pointers. + symbols[top->left].next = symbols[top->right].next; + if (symbols[top->right].next >= 0) { + symbols[symbols[top->right].next].prev = top->left; + } + symbols[top->right].piece = std::string_view(""); + + // Adds new symbol pairs which are newly added after symbol replacement. + MaybeAddNewSymbolPair(symbols[top->left].prev, top->left); + MaybeAddNewSymbolPair(top->left, symbols[top->left].next); + } + + std::function resegment; + resegment = [this, &resegment, &rev_merge](std::string_view w, EncodeResult *output) -> void { + std::string w_str(w); + const int id = piece_to_id(w_str); + // std::cout << "piece: " << w << ", id = " << id << std::endl; + if (id == -1 || !is_unused(id)) { + output->emplace_back(w, id); + return; + } + const auto p = rev_merge.find(w); + if (p == rev_merge.end()) { + // This block will never be called, as `rev_merge` stores all the + // resegmentation info for unused id. + output->emplace_back(w, id); + return; + } + // Recursively resegment left and right symbols. + resegment(p->second.first, output); + resegment(p->second.second, output); + }; + EncodeResult output; + for (int index = 0; index != -1; index = symbols[index].next) { + resegment(symbols[index].piece, &output); + } + return output; +} + +std::vector Sentencepiece::encode(const std::string& str) { + std::vector ids; + auto result = bpe_encode(str); + size_t consumed = 0; + for (const auto &p : result) { + const std::string_view w = p.first; // piece + const int id = p.second; // id + const bool is_unk = (id == unk_id_); + if (is_unk && byte_fall_back_) { + // Decomposes an unknown piece into UTF-8 bytes + for (int i = 0; i < w.size(); ++i) { + // Create a byte piece + const char b = w[i]; + const auto piece = byte_to_piece(b); + auto sp_id = piece_to_id(piece); + ids.push_back(sp_id); + } + } else { + ids.push_back(id); + } + } + return ids; +} + +std::string Sentencepiece::decode(int id) { + auto piece = sentence_pieces_[id].piece; + int pos = piece.find("▁"); + if (pos != -1) { + piece.replace(pos, pos + 3, " "); + } + return piece; +} + +float Sentencepiece::get_score(int id) const { + return sentence_pieces_[id].score; +} + +bool Sentencepiece::is_unused(int id) const { + return sentence_pieces_[id].type == PieceType::UNUSED; +} + +bool Sentencepiece::is_control(int id) const { + return sentence_pieces_[id].type == PieceType::CONTROL; +} + +const int CHARACTER_VOCABULARY_SIZE = 256; + +bool Tiktoken::load(const std::string& filename) { + std::ifstream tok_file(filename); + int index = -1, start = 0, rough_count = 0; + std::string token; + while (tok_file >> token) { + token = base64_decode(token); + decoder_.push_back(token); + rough_count += token.size(); + } + tok_file.close(); + tokens_.resize(rough_count * CHARACTER_VOCABULARY_SIZE, -1); + token_ids_.resize(rough_count * CHARACTER_VOCABULARY_SIZE, -1); + for (int n = 0; n < decoder_.size(); n++) { + token = decoder_[n]; + int root = 0; + for (int i = 0; i < token.size(); i++) { + unsigned char x = token[i]; + // record the token id at the parent of leaf node + if (i == token.size() - 1) { + token_ids_[root + x] = n; + } + // trace down a tree node. + // insert a subtree when needed. + if (tokens_[root + x] == -1) { + start += CHARACTER_VOCABULARY_SIZE; + tokens_[root + x] = start; + root = start; + } else { + root = tokens_[root + x]; + } + } + } + tokens_.resize(start + CHARACTER_VOCABULARY_SIZE); + token_ids_.resize(start + CHARACTER_VOCABULARY_SIZE); + return true; +} + +// ref: https://github.com/youkaichao/fast_bpe_tokenizer +std::vector Tiktoken::encode(const std::string& str) { + std::vector ids; + if (str.empty()) { + return ids; + } + int i = 0; + int root = 0; + int root_token_id = -1; + int last_found_position = -1; + int last_found_token_id = -1; + while (i < str.size()) { + unsigned char x = str[i]; + bool should_fall_back = false; + if (tokens_[root + x] != -1) { + root_token_id = token_ids_[root + x]; + root = tokens_[root + x]; + if (root_token_id != -1) { + // a token ends at position i + last_found_position = i; + last_found_token_id = root_token_id; + } + i++; + if (i == str.size()) { + should_fall_back = true; + } + } else { + // assert(last_found_position != -1); + should_fall_back = true; + } + if (should_fall_back) { + i = last_found_position + 1; + ids.push_back(last_found_token_id); + // start searching from the root again + root = 0; + root_token_id = -1; + last_found_position = -1; + last_found_token_id = -1; + } + } + return ids; +} + +std::string Tiktoken::decode(int id) { + if (id >= decoder_.size()) { + return ""; + } + return decoder_[id]; +} \ No newline at end of file diff --git a/package_scripts/win/build_lib_release.ps1 b/package_scripts/win/build_lib_release.ps1 index 71ca3289c..2d19f944f 100644 --- a/package_scripts/win/build_lib_release.ps1 +++ b/package_scripts/win/build_lib_release.ps1 @@ -14,7 +14,8 @@ Param( [Parameter(Mandatory=$true)][String]$path, [String]$backends, [Switch]$x86, - [Switch]$cibuild + [Switch]$cibuild, + [Switch]$internalbuild ) $erroractionpreference = "stop" @@ -48,6 +49,10 @@ if ($backends -ne $null) { } } +if ($internalbuild) { + $CMAKE_ARGS = "$CMAKE_ARGS -DMNN_INTERNAL=ON" +} + Remove-Item build -Recurse -ErrorAction Ignore mkdir build pushd build diff --git a/project/ios/MNN.xcodeproj/project.pbxproj b/project/ios/MNN.xcodeproj/project.pbxproj index f6531b318..ac90f452c 100644 --- a/project/ios/MNN.xcodeproj/project.pbxproj +++ b/project/ios/MNN.xcodeproj/project.pbxproj @@ -743,6 +743,8 @@ 958375352A496E5C007C0A3E /* MNNLineDepthWiseInt8AddBiasScale_ARMV82_Unit3X3.S in Sources */ = {isa = PBXBuildFile; fileRef = 958375342A496E5C007C0A3E /* MNNLineDepthWiseInt8AddBiasScale_ARMV82_Unit3X3.S */; }; 958B046429D2C89D00FC3AEF /* GemmInt8Executor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 958B046329D2C89D00FC3AEF /* GemmInt8Executor.cpp */; }; 958B046629D2C8AF00FC3AEF /* GemmInt8Executor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 958B046529D2C8AF00FC3AEF /* GemmInt8Executor.hpp */; }; + 95CE1DFF2AC57F6200EFB51E /* MNNReluWithSlopeChannelInt8.S in Sources */ = {isa = PBXBuildFile; fileRef = 95CE1DFE2AC57F6200EFB51E /* MNNReluWithSlopeChannelInt8.S */; }; + 95CE1E012AC57F7600EFB51E /* MNNReluWithSlopeChannelInt8.S in Sources */ = {isa = PBXBuildFile; fileRef = 95CE1E002AC57F7600EFB51E /* MNNReluWithSlopeChannelInt8.S */; }; C43C81FA251894A600A0FF84 /* CommonOptFunctionNeon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C43C81F8251894A500A0FF84 /* CommonOptFunctionNeon.cpp */; }; C43C81FE251894BD00A0FF84 /* CPUPlugin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C43C81FB251894BD00A0FF84 /* CPUPlugin.cpp */; }; C43C81FF251894BD00A0FF84 /* ThreadPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C43C81FC251894BD00A0FF84 /* ThreadPool.cpp */; }; @@ -1570,6 +1572,8 @@ 958375342A496E5C007C0A3E /* MNNLineDepthWiseInt8AddBiasScale_ARMV82_Unit3X3.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = MNNLineDepthWiseInt8AddBiasScale_ARMV82_Unit3X3.S; path = arm/arm64/MNNLineDepthWiseInt8AddBiasScale_ARMV82_Unit3X3.S; sourceTree = ""; }; 958B046329D2C89D00FC3AEF /* GemmInt8Executor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GemmInt8Executor.cpp; sourceTree = ""; }; 958B046529D2C8AF00FC3AEF /* GemmInt8Executor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = GemmInt8Executor.hpp; sourceTree = ""; }; + 95CE1DFE2AC57F6200EFB51E /* MNNReluWithSlopeChannelInt8.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = MNNReluWithSlopeChannelInt8.S; sourceTree = ""; }; + 95CE1E002AC57F7600EFB51E /* MNNReluWithSlopeChannelInt8.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = MNNReluWithSlopeChannelInt8.S; sourceTree = ""; }; C43C81F8251894A500A0FF84 /* CommonOptFunctionNeon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommonOptFunctionNeon.cpp; sourceTree = ""; }; C43C81FB251894BD00A0FF84 /* CPUPlugin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPUPlugin.cpp; sourceTree = ""; }; C43C81FC251894BD00A0FF84 /* ThreadPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadPool.cpp; sourceTree = ""; }; @@ -2504,6 +2508,7 @@ 92FF013A23AA0B4E00AC97F6 /* arm32 */ = { isa = PBXGroup; children = ( + 95CE1DFE2AC57F6200EFB51E /* MNNReluWithSlopeChannelInt8.S */, CE125CC72A52BF6B003698C9 /* MNNBilinearLineC8.S */, CE125CC62A52BF6B003698C9 /* MNNBilinearSampleC8.S */, CEE9B94F2A3AA4C4006438F2 /* MNNCubicLineC16.S */, @@ -2584,6 +2589,7 @@ 92FF017C23AA0B4E00AC97F6 /* arm64 */ = { isa = PBXGroup; children = ( + 95CE1E002AC57F7600EFB51E /* MNNReluWithSlopeChannelInt8.S */, CEE9B9572A3AA4D4006438F2 /* MNNBilinearLineC8.S */, CEE9B9582A3AA4D4006438F2 /* MNNBilinearSampleC8.S */, CEE9B9562A3AA4D4006438F2 /* MNNCubicLineC16.S */, @@ -3540,6 +3546,7 @@ 489D7AB32550FDC900AD896A /* MetalPReLU.mm in Sources */, 19D0FE7028534C4500B74B1A /* MetalSoftmax.mm in Sources */, 4AF4FB24269ED235005BA97B /* SparseConvInt8TiledExecutor.cpp in Sources */, + 95CE1DFF2AC57F6200EFB51E /* MNNReluWithSlopeChannelInt8.S in Sources */, 48FB9DCE24AB080C008E1A2D /* MNNPackC8.S in Sources */, 4D9A937A26255BDA00F9B43C /* CoreMLActivation.cpp in Sources */, 950B28FE2A0C9B310002F454 /* MNNScaleAndAddBiasInt8.S in Sources */, @@ -3618,6 +3625,7 @@ 92FF043E23AA0B7100AC97F6 /* ShapeBatchToSpaceND.cpp in Sources */, 48C84B88250F711700EE7666 /* IfModule.cpp in Sources */, 481FA84F259C27B30047F01F /* GeometryTensorArray.cpp in Sources */, + 95CE1E012AC57F7600EFB51E /* MNNReluWithSlopeChannelInt8.S in Sources */, 4DCF538A2892B15200B5B393 /* histograms.cpp in Sources */, 48C84B86250F711700EE7666 /* StaticModule.cpp in Sources */, 92FF043223AA0B7100AC97F6 /* ShapeWhere.cpp in Sources */, diff --git a/pymnn/pip_package/build_deps.py b/pymnn/pip_package/build_deps.py index f44f31b3f..4eff87833 100644 --- a/pymnn/pip_package/build_deps.py +++ b/pymnn/pip_package/build_deps.py @@ -50,7 +50,7 @@ def build_deps(): if IS_WINDOWS: os.system('cmake -G "Ninja" ' + extra_opts +' -DMNN_BUILD_TRAIN=ON -DMNN_BUILD_CONVERTER=on -DMNN_BUILD_TORCH=OFF\ -DMNN_BUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DMNN_WIN_RUNTIME_MT=ON\ - -DMNN_BUILD_OPENCV=ON -DMNN_IMGCODECS=ON -DMNN_AAPL_FMWK=OFF -DMNN_SEP_BUILD=OFF .. && ninja MNN MNNTrain MNNConvert') + -DMNN_BUILD_OPENCV=ON -DMNN_IMGCODECS=ON -DMNN_AAPL_FMWK=OFF -DMNN_SEP_BUILD=OFF .. && ninja MNN MNNTrain MNNConvertDeps') elif IS_LINUX: extra_opts += '-DMNN_TENSORRT=ON \ -DCMAKE_LIBRARY_PATH=/usr/local/cuda/lib64/stubs/ ' if USE_TRT else ' ' @@ -60,7 +60,7 @@ def build_deps(): os.system('cmake ' + extra_opts + '-DMNN_BUILD_CONVERTER=on -DMNN_BUILD_TRAIN=ON -DCMAKE_BUILD_TYPE=Release \ -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_AAPL_FMWK=OFF -DMNN_SEP_BUILD=OFF -DMNN_BUILD_OPENCV=ON -DMNN_IMGCODECS=ON \ - -DMNN_USE_THREAD_POOL=ON -DMNN_OPENMP=OFF .. && make MNN MNNTrain MNNConvert -j4') + -DMNN_USE_THREAD_POOL=ON -DMNN_OPENMP=OFF .. && make MNN MNNTrain MNNConvertDeps -j4') else: extra_opts += ' -DMNN_INTERNAL=ON ' if IS_INTERNAL_BUILD else ' ' extra_opts += ' -DMNN_BUILD_TORCH=ON ' if IS_BUILD_TORCH else ' ' @@ -68,7 +68,7 @@ def build_deps(): os.system('cmake ' + extra_opts + '-DMNN_BUILD_CONVERTER=on -DMNN_BUILD_TRAIN=ON -DCMAKE_BUILD_TYPE=Release \ -DMNN_BUILD_SHARED_LIBS=OFF -DMNN_AAPL_FMWK=OFF -DMNN_SEP_BUILD=OFF\ -DMNN_BUILD_OPENCV=ON -DMNN_IMGCODECS=ON \ - .. && make MNN MNNTrain MNNConvert -j4') + .. && make MNN MNNTrain MNNConvertDeps -j4') ################################################################################ # Building dependent libraries ################################################################################ diff --git a/pymnn/src/MNNTools.cc b/pymnn/src/MNNTools.cc index 81c2a319b..7a8d8ffa4 100644 --- a/pymnn/src/MNNTools.cc +++ b/pymnn/src/MNNTools.cc @@ -10,6 +10,7 @@ #include "calibration.hpp" #include "logkit.h" #include +#include #include using namespace MNN; using namespace std; @@ -38,52 +39,24 @@ static PyObject* PyTool_Converter(PyObject *self, PyObject *argsTuple) { Py_RETURN_TRUE; } -static PyObject* PyTool_Quantization(PyObject *self, PyObject *args) { - const char* modelFile = NULL; - const char* preTreatConfig = NULL; - const char* dstFile = NULL; - if (!PyArg_ParseTuple(args, "sss", &modelFile, &dstFile, &preTreatConfig)) { - return NULL; +static PyObject* PyTool_Quantization(PyObject *self, PyObject *argsTuple) { + int tupleSize = PyTuple_GET_SIZE(argsTuple); + if (tupleSize < 1) { + MNN_ERROR("Invalid input for Converter\n"); + return nullptr; } - DLOG(INFO) << ">>> modelFile: " << modelFile; - DLOG(INFO) << ">>> preTreatConfig: " << preTreatConfig; - DLOG(INFO) << ">>> dstFile: " << dstFile; - std::unique_ptr netT; - { - std::ifstream input(modelFile); - std::ostringstream outputOs; - outputOs << input.rdbuf(); - netT = MNN::UnPackNet(outputOs.str().c_str()); + PyObject* args = PyTuple_GET_ITEM(argsTuple, 0); + int argSize = PyList_Size(args); + std::vector argsCpp(argSize); + std::vector argsContant(argSize); + for (int i=0; i modelForInference(new uint8_t[size]); - memcpy(modelForInference.get(), ocontent, size); - - std::unique_ptr modelOriginal(new uint8_t[size]); - memcpy(modelOriginal.get(), ocontent, size); - - netT.reset(); - netT = MNN::UnPackNet(modelOriginal.get()); - - // quantize model's weight - DLOG(INFO) << "Calibrate the feature and quantize model..."; - std::shared_ptr calibration( - new Calibration(netT.get(), modelForInference.get(), size, preTreatConfig, std::string(modelFile), std::string(dstFile))); - calibration->runQuantizeModel(); - calibration->dumpTensorScales(dstFile); - DLOG(INFO) << "Quantize model done!"; - + quant_main(argSize, argsCpp.data()); Py_RETURN_TRUE; - } + static PyMethodDef module_methods[] = { { "mnnconvert", (PyCFunction)PyTool_Converter, METH_VARARGS, NULL }, { "mnnquant", (PyCFunction)PyTool_Quantization, METH_VARARGS, NULL }, diff --git a/schema/current/TensorflowOp_generated.h b/schema/current/TensorflowOp_generated.h index 621054779..4320f4c64 100644 --- a/schema/current/TensorflowOp_generated.h +++ b/schema/current/TensorflowOp_generated.h @@ -1477,6 +1477,7 @@ struct UnaryOpT : public flatbuffers::NativeTable { typedef UnaryOp TableType; UnaryOpOperation opType; DataType T; + std::vector tableInt8; UnaryOpT() : opType(UnaryOpOperation_ABS), T(DataType_DT_INVALID) { @@ -1494,10 +1495,15 @@ struct UnaryOp FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { DataType T() const { return static_cast(GetField(6, 0)); } + const flatbuffers::Vector *tableInt8() const { + return GetPointer *>(8); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, 4) && VerifyField(verifier, 6) && + VerifyOffset(verifier, 8) && + verifier.VerifyVector(tableInt8()) && verifier.EndTable(); } UnaryOpT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -1514,6 +1520,9 @@ struct UnaryOpBuilder { void add_T(DataType T) { fbb_.AddElement(6, static_cast(T), 0); } + void add_tableInt8(flatbuffers::Offset> tableInt8) { + fbb_.AddOffset(8, tableInt8); + } explicit UnaryOpBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -1529,8 +1538,10 @@ struct UnaryOpBuilder { inline flatbuffers::Offset CreateUnaryOp( flatbuffers::FlatBufferBuilder &_fbb, UnaryOpOperation opType = UnaryOpOperation_ABS, - DataType T = DataType_DT_INVALID) { + DataType T = DataType_DT_INVALID, + flatbuffers::Offset> tableInt8 = 0) { UnaryOpBuilder builder_(_fbb); + builder_.add_tableInt8(tableInt8); builder_.add_T(T); builder_.add_opType(opType); return builder_.Finish(); @@ -3881,6 +3892,7 @@ inline void UnaryOp::UnPackTo(UnaryOpT *_o, const flatbuffers::resolver_function (void)_resolver; { auto _e = opType(); _o->opType = _e; }; { auto _e = T(); _o->T = _e; }; + { auto _e = tableInt8(); if (_e) { _o->tableInt8.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->tableInt8[_i] = _e->Get(_i); } } }; } inline flatbuffers::Offset UnaryOp::Pack(flatbuffers::FlatBufferBuilder &_fbb, const UnaryOpT* _o, const flatbuffers::rehasher_function_t *_rehasher) { @@ -3893,10 +3905,12 @@ inline flatbuffers::Offset CreateUnaryOp(flatbuffers::FlatBufferBuilder struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const UnaryOpT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; auto _opType = _o->opType; auto _T = _o->T; + auto _tableInt8 = _o->tableInt8.size() ? _fbb.CreateVector(_o->tableInt8) : 0; return MNN::CreateUnaryOp( _fbb, _opType, - _T); + _T, + _tableInt8); } inline TopKV2T *TopKV2::UnPack(const flatbuffers::resolver_function_t *_resolver) const { @@ -5196,7 +5210,8 @@ inline const flatbuffers::TypeTable *ReduceJoinTypeTable() { inline const flatbuffers::TypeTable *UnaryOpTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_INT, 0, 0 }, - { flatbuffers::ET_INT, 0, 1 } + { flatbuffers::ET_INT, 0, 1 }, + { flatbuffers::ET_CHAR, 1, -1 } }; static const flatbuffers::TypeFunction type_refs[] = { UnaryOpOperationTypeTable, @@ -5204,10 +5219,11 @@ inline const flatbuffers::TypeTable *UnaryOpTypeTable() { }; static const char * const names[] = { "opType", - "T" + "T", + "tableInt8" }; static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names }; return &tt; } diff --git a/schema/default/TensorflowOp.fbs b/schema/default/TensorflowOp.fbs index 209752c8b..0774de60e 100644 --- a/schema/default/TensorflowOp.fbs +++ b/schema/default/TensorflowOp.fbs @@ -159,6 +159,7 @@ enum UnaryOpOperation : int { table UnaryOp { opType:UnaryOpOperation; T:DataType; + tableInt8:[int8]; } table TopKV2 { diff --git a/source/backend/arm82/asm/arm32/MNNGeluFP16.S b/source/backend/arm82/asm/arm32/MNNGeluFP16.S index 8700148e6..e422ea082 100644 --- a/source/backend/arm82/asm/arm32/MNNGeluFP16.S +++ b/source/backend/arm82/asm/arm32/MNNGeluFP16.S @@ -62,6 +62,13 @@ vadd.f16 q3, q3, q1 vmul.f16 q2, q2, q14 vmul.f16 q3, q3, q14 +vmov.f16 q4, #5.0 +vmov.f16 q5, #-5.0 +vmax.f16 q2, q2, q5 +vmin.f16 q2, q2, q4 +vmax.f16 q3, q3, q5 +vmin.f16 q3, q3, q4 + // tanh(value) vmul.f16 q4, q2, q2 // q4: value*value vmul.f16 q5, q3, q3 // q5: value*value diff --git a/source/backend/arm82/asm/arm64/MNNGeluFP16.S b/source/backend/arm82/asm/arm64/MNNGeluFP16.S index 11e7c4ec7..b47f6dedc 100644 --- a/source/backend/arm82/asm/arm64/MNNGeluFP16.S +++ b/source/backend/arm82/asm/arm64/MNNGeluFP16.S @@ -62,6 +62,13 @@ fadd v3.8h, v3.8h, v1.8h fmul v2.8h, v2.8h, v14.8h fmul v3.8h, v3.8h, v14.8h +fmov v6.8h, #-5 +fmov v7.8h, #5 +fmin v2.8h, v2.8h, v7.8h +fmin v3.8h, v3.8h, v7.8h +fmax v2.8h, v2.8h, v6.8h +fmax v3.8h, v3.8h, v6.8h + // tanh(value) fmul v4.8h, v2.8h, v2.8h // q4: value*value fmul v5.8h, v3.8h, v3.8h // q5: value*value diff --git a/source/backend/cpu/BinaryUtils.hpp b/source/backend/cpu/BinaryUtils.hpp index 27c352335..be05589ad 100644 --- a/source/backend/cpu/BinaryUtils.hpp +++ b/source/backend/cpu/BinaryUtils.hpp @@ -187,14 +187,14 @@ struct BinaryBitwiseXor { } }; -template +template void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, int elementSize, int needBroadcastIndex) { Func compute; const int sizeDivUnit = elementSize / pack; const int remainCount = elementSize - sizeDivUnit * pack; - auto src0 = (const float*)(inputRaw0); - auto src1 = (const float*)(inputRaw1); - auto dst = (float*)outputRaw; + auto src0 = (const U*)(inputRaw0); + auto src1 = (const U*)(inputRaw1); + auto dst = (U*)outputRaw; if (-1 == needBroadcastIndex) { if (sizeDivUnit > 0) { @@ -208,18 +208,18 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i } } if (remainCount > 0) { - float tempSrc0[pack]; - float tempSrc1[pack]; - float tempDst[pack]; - ::memcpy(tempSrc0, src0, remainCount * sizeof(float)); - ::memcpy(tempSrc1, src1, remainCount * sizeof(float)); + U tempSrc0[pack]; + U tempSrc1[pack]; + U tempDst[pack]; + ::memcpy(tempSrc0, src0, remainCount * sizeof(U)); + ::memcpy(tempSrc1, src1, remainCount * sizeof(U)); V a = V::load(tempSrc0); V b = V::load(tempSrc1); V::save(tempDst, compute(a, b)); - ::memcpy(dst, tempDst, remainCount * sizeof(float)); + ::memcpy(dst, tempDst, remainCount * sizeof(U)); } } else if (0 == needBroadcastIndex) { - const float srcValue0 = src0[0]; + const U srcValue0 = src0[0]; V a = V(srcValue0); if (sizeDivUnit > 0) { for (int i = 0; i < sizeDivUnit; ++i) { @@ -232,15 +232,15 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i } } if (remainCount > 0) { - float tempSrc1[pack]; - float tempDst[pack]; - ::memcpy(tempSrc1, src1, remainCount * sizeof(float)); + U tempSrc1[pack]; + U tempDst[pack]; + ::memcpy(tempSrc1, src1, remainCount * sizeof(U)); V b = V::load(tempSrc1); V::save(tempDst, compute(a, b)); - ::memcpy(dst, tempDst, remainCount * sizeof(float)); + ::memcpy(dst, tempDst, remainCount * sizeof(U)); } } else { - const float srcValue1 = src1[0]; + const auto srcValue1 = static_cast(src1[0]); V b = V(srcValue1); if (sizeDivUnit > 0) { for (int i = 0; i < sizeDivUnit; ++i) { @@ -253,12 +253,12 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i } } if (remainCount > 0) { - float tempSrc0[pack]; - float tempDst[pack]; - ::memcpy(tempSrc0, src0, remainCount * sizeof(float)); + U tempSrc0[pack]; + U tempDst[pack]; + ::memcpy(tempSrc0, src0, remainCount * sizeof(U)); V a = V::load(tempSrc0); V::save(tempDst, compute(a, b)); - ::memcpy(dst, tempDst, remainCount * sizeof(float)); + ::memcpy(dst, tempDst, remainCount * sizeof(U)); } } } @@ -304,6 +304,42 @@ struct VecBinarySqd { return (x-y)*(x-y); } }; + +template +struct VecBinaryLess { + Vec operator()(Vec& x, Vec& y) const { + return x < y; + } +}; + +template +struct VecBinaryGreater { + Vec operator()(Vec& x, Vec& y) const { + return x > y; + } +}; + +template +struct VecBinaryLessEqual { + Vec operator()(Vec& x, Vec& y) const { + return x <= y; + } +}; + +template +struct VecBinaryGreaterEqual { + Vec operator()(Vec& x, Vec& y) const { + return x >= y; + } +}; + +template +struct VecBinaryEqual { + Vec operator()(Vec& x, Vec& y) const { + return x == y; + } +}; + namespace MNN { template void execute(void* outputRaw, const void* inputRaw0, const void* inputRaw1, int elementSize, int broadcastIndex) { @@ -375,21 +411,31 @@ void executeInt8 (int8_t* outputRaw, const int8_t* inputRaw0, const int8_t* inpu } } -template +template MNNBinaryExecute selectVector(int type) { switch (type) { case BinaryOpOperation_ADD: - return executeVec, V, pack>; + return executeVec, V, pack, U>; case BinaryOpOperation_SUB: - return executeVec, V, pack>; + return executeVec, V, pack, U>; case BinaryOpOperation_MUL: - return executeVec, V, pack>; + return executeVec, V, pack, U>; case BinaryOpOperation_MINIMUM: - return executeVec, V, pack>; + return executeVec, V, pack, U>; case BinaryOpOperation_MAXIMUM: - return executeVec, V, pack>; + return executeVec, V, pack, U>; case BinaryOpOperation_SquaredDifference: - return executeVec, V, pack>; + return executeVec, V, pack, U>; + case BinaryOpOperation_LESS: + return executeVec, V, pack, U>; + case BinaryOpOperation_LESS_EQUAL: + return executeVec, V, pack, U>; + case BinaryOpOperation_GREATER: + return executeVec, V, pack, U>; + case BinaryOpOperation_GREATER_EQUAL: + return executeVec, V, pack, U>; + case BinaryOpOperation_EQUAL: + return executeVec, V, pack, U>; } return nullptr; } diff --git a/source/backend/cpu/CPUBinary.cpp b/source/backend/cpu/CPUBinary.cpp index 078a8d113..674ecccfd 100644 --- a/source/backend/cpu/CPUBinary.cpp +++ b/source/backend/cpu/CPUBinary.cpp @@ -17,6 +17,7 @@ #include "BinaryUtils.hpp" #include "math/Vec.hpp" using Vec4 = MNN::Math::Vec; +using Vec4Int = MNN::Math::Vec; namespace MNN { @@ -93,7 +94,7 @@ ErrorCode CPUBinary::onExecute(const std::vector& inputs, const std::ve } MNNBinaryExecute CPUBinary::selectForFloat(int type) { - auto vecFunction = selectVector(type); + auto vecFunction = selectVector(type); if (nullptr != vecFunction) { return vecFunction; } @@ -130,6 +131,10 @@ MNNBinaryExecute CPUBinary::selectForFloat(int type) { } MNNBinaryExecute CPUBinary::selectForInt(int type) { + auto vecFunction = selectVector(type); + if (nullptr != vecFunction) { + return vecFunction; + } switch (type) { case BinaryOpOperation_MUL: return execute>; diff --git a/source/backend/cpu/CPULayerNorm.cpp b/source/backend/cpu/CPULayerNorm.cpp index 1c90603ee..2e3b0f476 100644 --- a/source/backend/cpu/CPULayerNorm.cpp +++ b/source/backend/cpu/CPULayerNorm.cpp @@ -66,11 +66,10 @@ ErrorCode CPULayerNorm::onExecute(const std::vector &inputs, const float* beta = mIniGammaBeta ? mBeta->host() : nullptr; if (mInpZero.data()) { - auto core = static_cast(backend())->int8Functions(); - const int8_t* input = inputs[0]->host(); int8_t* output = outputs[0]->host(); MNN_CONCURRENCY_BEGIN(tId, mOutterSize) { + auto core = static_cast(backend())->int8Functions(); QuanPrePostParameters params; params.maxValue = mMaxMinValue[0]; params.minValue = mMaxMinValue[1]; diff --git a/source/backend/cpu/CPUPool.cpp b/source/backend/cpu/CPUPool.cpp index 4a3b56325..15767c94e 100644 --- a/source/backend/cpu/CPUPool.cpp +++ b/source/backend/cpu/CPUPool.cpp @@ -19,8 +19,12 @@ namespace MNN { class CPUPool : public Execution { public: - CPUPool(Backend *b, const Pool *parameter, void* func, int bytes) : MNN::Execution(b), mParameter(parameter) { - mCompute = (decltype(mCompute))func; + CPUPool(Backend *b, const Pool *parameter, void* func, int bytes, bool returnRedice) : MNN::Execution(b), mParameter(parameter) { + if(returnRedice){ + mComputeRedice = (decltype(mComputeRedice))func; + }else{ + mCompute = (decltype(mCompute))func; + } mBytes = bytes; } virtual ~CPUPool() = default; @@ -63,16 +67,30 @@ class CPUPool : public Execution { if (layer->pads() != nullptr && padType == PoolPadType_CAFFE) { padType = PoolPadType_VALID; } - mFunction = std::make_pair(threadNumber, [=](int tId) { - for (int channel = (int)tId; channel < totalDepth; channel += threadNumber) { - auto inputData = input->host(); - auto outputData = output->host(); - // run - mCompute(inputData + channel * inputPlaneStride * mBytes, input->width(), input->height(), - outputData + outputPlaneStride * channel * mBytes, output->width(), output->height(), kernelWidth, - kernelHeight, strideWidth, strideHeight, padWidth, padHeight, padType, countType); - } - }); + if(outputs.size() == 2){ + mFunction = std::make_pair(threadNumber, [=](int tId) { + for (int channel = (int)tId; channel < totalDepth; channel += threadNumber) { + auto inputData = input->host(); + auto outputData = output->host(); + auto rediceData = outputs[1]->host(); + // run + mComputeRedice(inputData + channel * inputPlaneStride * mBytes, input->width(), input->height(), + outputData + outputPlaneStride * channel * mBytes, output->width(), output->height(), kernelWidth, + kernelHeight, strideWidth, strideHeight, padWidth, padHeight, padType, countType, rediceData + outputPlaneStride * channel * mBytes); + } + }); + }else{ + mFunction = std::make_pair(threadNumber, [=](int tId) { + for (int channel = (int)tId; channel < totalDepth; channel += threadNumber) { + auto inputData = input->host(); + auto outputData = output->host(); + // run + mCompute(inputData + channel * inputPlaneStride * mBytes, input->width(), input->height(), + outputData + outputPlaneStride * channel * mBytes, output->width(), output->height(), kernelWidth, + kernelHeight, strideWidth, strideHeight, padWidth, padHeight, padType, countType); + } + }); + } return NO_ERROR; } virtual ErrorCode onExecute(const std::vector &inputs, const std::vector &outputs) override { @@ -88,6 +106,9 @@ class CPUPool : public Execution { void(*mCompute)(const void* channelInput, int inputWidth, int inputHeight, void *channelOutput, int outputWidth, int outputHeight, int kernelWidth, int kernelHeight, int strideWidth, int strideHeight, int padWidth, int padHeight, int padType, int countType); + void(*mComputeRedice)(const void* channelInput, int inputWidth, int inputHeight, void *channelOutput, + int outputWidth, int outputHeight, int kernelWidth, int kernelHeight, int strideWidth, + int strideHeight, int padWidth, int padHeight, int padType, int countType, void *rediceOutput); std::pair > mFunction; int mBytes; }; @@ -96,21 +117,26 @@ class CPUPoolCreator : public CPUBackend::Creator { virtual Execution *onCreate(const std::vector &inputs, const std::vector &outputs, const MNN::Op *op, Backend *backend) const override { void* func = nullptr; + bool returnRedice = false; if (inputs[0]->getType() == halide_type_of()) { if (op->main_as_Pool()->type() == PoolType_AVEPOOL) { func = (void*)(poolingAvg); } else { func = (void*)(poolingMax); } - return new CPUPool(backend, op->main_as_Pool(), func, 1); + return new CPUPool(backend, op->main_as_Pool(), func, 1, returnRedice); } auto core = static_cast(backend)->functions(); if (op->main_as_Pool()->type() == PoolType_AVEPOOL) { func = (void*)(core->MNNPoolingAvg); } else { func = (void*)(core->MNNPoolingMax); + if(outputs.size() == 2){ + func = (void*)(core->MNNPoolingMaxWithRedice); + returnRedice = true; + } } - return new CPUPool(backend, op->main_as_Pool(), func, core->bytes); + return new CPUPool(backend, op->main_as_Pool(), func, core->bytes, returnRedice); } }; diff --git a/source/backend/cpu/CPUPool.hpp b/source/backend/cpu/CPUPool.hpp index 945d6d7f5..71978bc8d 100644 --- a/source/backend/cpu/CPUPool.hpp +++ b/source/backend/cpu/CPUPool.hpp @@ -158,6 +158,70 @@ static void poolingMax(const T *channelInput, int inputWidth, int inputHeight, T } } } +template +static void poolingMaxWithRedice(const T *channelInput, int inputWidth, int inputHeight, T *channelOutput, + int outputWidth, int outputHeight, int kernelWidth, int kernelHeight, int strideWidth, + int strideHeight, int padWidth, int padHeight, MNN::PoolPadType padType, MNN::AvgPoolCountType countType, int *rediceOutput) { + + const int inputStep4 = 4 * inputWidth; + const int inputSize4 = inputStep4 * inputHeight; + const int strideInputStep4 = strideHeight * inputStep4; + const int outputStep4 = 4 * outputWidth; + const int strideWidth4 = 4 * strideWidth; + + const T *lineInput = channelInput + (-padHeight) * inputStep4 + (-padWidth) * 4; + T *lineOutput = channelOutput; + int *lineRediceOutput = rediceOutput; + + for (int oh = 0, ih = -padHeight; oh < outputHeight; + oh++, ih += strideHeight, lineOutput += outputStep4, lineRediceOutput += outputStep4, lineInput += strideInputStep4) { + const T *offsetInput = lineInput; + T *offsetOutput = lineOutput; + int *offsetRediceOutput = lineRediceOutput; + for (int ow = 0, iw = -padWidth; ow < outputWidth; ++ow, iw += strideWidth, offsetOutput += 4, offsetRediceOutput += 4, offsetInput += strideWidth4) { + T max0 = float(MAXVALUE); + T max1 = float(MAXVALUE); + T max2 = float(MAXVALUE); + T max3 = float(MAXVALUE); + int indice0 = 0, indice1 = 0, indice2 = 0, indice3 = 0; + const T *kernelInput = offsetInput; + for (int kh = 0; kh < kernelHeight && (kh + ih) >= 0 && (kh + ih) < inputHeight; kh++, kernelInput += inputStep4) { + const T *cursorInput = kernelInput; + for (int kw = 0; kw < kernelWidth && (kw + iw) >= 0 && (kw + iw) < inputWidth; kw++, cursorInput += 4) { + T in0 = cursorInput[0]; + T in1 = cursorInput[1]; + T in2 = cursorInput[2]; + T in3 = cursorInput[3]; + int indice = (kh + ih) * inputWidth + kw + iw; + if(in0 > max0){ + max0 = in0; + indice0 = indice; + } + if(in1 > max1){ + max1 = in1; + indice1 = indice; + } + if(in2 > max2){ + max2 = in2; + indice2 = indice; + } + if(in3 > max3){ + max3 = in3; + indice3 = indice; + } + } + } + offsetOutput[0] = max0; + offsetOutput[1] = max1; + offsetOutput[2] = max2; + offsetOutput[3] = max3; + offsetRediceOutput[0] = indice0; + offsetRediceOutput[1] = indice1; + offsetRediceOutput[2] = indice2; + offsetRediceOutput[3] = indice3; + } + } +} template static void poolingAvgPad(const T *offsetInput, T *offsetOutput, int inputWidth, int inputHeight, diff --git a/source/backend/cpu/CPURaster.cpp b/source/backend/cpu/CPURaster.cpp index d49b7795f..3bec068fb 100644 --- a/source/backend/cpu/CPURaster.cpp +++ b/source/backend/cpu/CPURaster.cpp @@ -256,35 +256,6 @@ static void _transpose4Bit(int32_t* dstO, const int32_t* srcO, const Tensor::Ins } typedef void (*BlitProc)(uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds); -static void _4BitcopyWithStride(uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { - auto src = (uint32_t*)srcO; - auto dst = (uint32_t*)dstO; - for (int i=0; i &inputs, const std::ve ::memset(output->host(), mZeroPoint, static_cast(backend())->getTensorSize(output) * bytes); } auto byteC4 = bytes * core->pack; - auto C4proc = _4BitcopyWithStride; + auto C4proc = core->MNN4BitcopyWithStride; switch (byteC4) { case 16: C4proc = _4BitcopyWithStrideC4; @@ -324,7 +295,7 @@ void CPURaster::executeFaster(const std::vector &inputs, const std::ve C4proc = _2BitcopyWithStrideC4; break; case 4: - C4proc = _4BitcopyWithStride; + C4proc = core->MNN4BitcopyWithStride; break; default: C4proc = core->MNNSelectBlitFunction(byteC4); @@ -371,17 +342,30 @@ void CPURaster::executeFaster(const std::vector &inputs, const std::ve MNN_CONCURRENCY_END(); } -static BlitProc _selectUnitProc(int bytes) { - auto proc = _1BitcopyWithStride; +static BlitProc _selectUnitProc(int bytes, int stride, int ds) { + auto core = MNNGetCoreFunctions(); + auto proc = core->MNN1BitcopyFast; switch (bytes) { case 4: - proc = _4BitcopyWithStride; + if (ds == 1 && (stride == 1 || stride == 0)) { + proc = core->MNN4BitcopyFast; + } else { + proc = core->MNN4BitcopyWithStride; + } break; case 2: - proc = _2BitcopyWithStride; + if (ds == 1 && (stride == 1 || stride == 0)) { + proc = core->MNN2BitcopyFast; + } else { + proc = core->MNN2BitcopyWithStride; + } break; case 1: - proc = _1BitcopyWithStride; + if (ds == 1 && (stride == 1 || stride == 0)) { + proc = core->MNN1BitcopyFast; + } else { + proc = core->MNN1BitcopyWithStride; + } break; default: MNN_ASSERT(false); @@ -493,7 +477,8 @@ static bool _reduceblit(const Tensor::InsideDescribe::Region& slice, int bytes, return false; } -static void _blit(const Tensor::InsideDescribe::Region& slice, int bytes, const uint8_t* srcPtr, uint8_t* dstPtr, void(*proc)(uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds)) { +static void _blit(const Tensor::InsideDescribe::Region& slice, int bytes, const uint8_t* srcPtr, uint8_t* dstPtr) { + auto proc = _selectUnitProc(bytes, slice.src.stride[2], slice.dst.stride[2]); #define MNN_BLIT_SUPPORT_REDUCE #ifdef MNN_BLIT_SUPPORT_REDUCE if (_reduceblit(slice, bytes, srcPtr, dstPtr)) { @@ -530,7 +515,7 @@ static void _blit(const Tensor::InsideDescribe::Region& slice, int bytes, const } for (int z=0; z &____inputs, const st for (auto& iter : mTempInput) { tensorConvert(iter.first, iter.second, bytes); } - auto proc = _selectUnitProc(bytes); threadNum = ALIMIN(threadNum, (int)mTempInputCopy.size()); MNN_CONCURRENCY_BEGIN(tId, threadNum) { for (int u=tId; u &____inputs, const st auto& slice = *(iter.second); auto srcPtr = iter.first->host() + slice.src.offset * bytes; auto dstPtr = (uint8_t*)mOutputPtr + slice.dst.offset * bytes; - _blit(slice, bytes, srcPtr, dstPtr, proc); + _blit(slice, bytes, srcPtr, dstPtr); } } MNN_CONCURRENCY_END(); @@ -808,8 +792,7 @@ class CPULoop : public Execution { if (halide_type_float == input->getType().code && bytes == 4) { bytes = cpubackend->functions()->bytes; } - auto proc = _selectUnitProc(bytes); - _blit(reg, bytes, input->host(), output->host(), proc); + _blit(reg, bytes, input->host(), output->host()); } } @@ -847,7 +830,6 @@ class CPULoop : public Execution { if (halide_type_float == input->getType().code && bytes == 4) { bytes = static_cast(backend())->functions()->bytes; } - auto proc = _selectUnitProc(bytes); auto step0 = cmd->steps()->data()[0]; auto step1 = cmd->steps()->data()[1]; auto loopNumber = mLoop->loopNumber(); @@ -858,7 +840,7 @@ class CPULoop : public Execution { auto dstOffset = dstIter * step0 + dstView->offset(); if (dstOffset >= 0) { if (srcOffset >= 0 && srcOffset < inputSize) { - _blit(reg, bytes, input->host() + bytes * srcOffset, output->host() + bytes * dstOffset, proc); + _blit(reg, bytes, input->host() + bytes * srcOffset, output->host() + bytes * dstOffset); } else { _zero(reg, bytes, output->host() + bytes * dstOffset); } @@ -869,12 +851,12 @@ class CPULoop : public Execution { } auto bytes = static_cast(backend())->functions()->bytes; auto func = [&](int iter, int tId) { - auto blit = _selectUnitProc(bytes); int fuseOutputStride[3]; const int32_t* outputStride = nullptr; auto fuseBuffer = mFuseBuffer + mMaxFuseBufferSize * tId; for (int index=0; indexcommands()->size(); ++index) { auto cmd = mLoop->commands()->GetAs(index); + auto blit = _selectUnitProc(bytes, cmd->view()->GetAs(1)->stride()->data()[2], 1); auto op = cmd->op(); int iterIndexsize = cmd->iterIndexes()->size(); @@ -921,11 +903,10 @@ class CPULoop : public Execution { ::memcpy(reg.size, cmd->size()->data(), 3 * sizeof(int32_t)); ::memcpy(reg.src.stride, srcView->stride()->data(), 3 * sizeof(int32_t)); ::memcpy(reg.dst.stride, outputStride, 3 * sizeof(int32_t)); - auto proc = _selectUnitProc(bytes); auto step0 = cmd->steps()->data()[0]; auto step1 = cmd->steps()->data()[1]; auto loopNumber = mLoop->loopNumber(); - _blit(reg, bytes, (const uint8_t*)src, (uint8_t*)dst, proc); + _blit(reg, bytes, (const uint8_t*)src, (uint8_t*)dst); break; } auto proc = static_cast(backend())->functions()->MNNSelectUnaryFunctionForFloat(op->main_as_UnaryOp()->opType(), static_cast(backend())->precisionMode()); @@ -986,6 +967,8 @@ class CPULoop : public Execution { MNN_ASSERT(stride0[2] == 1); auto src1 = mContainer[tId].stackPtr[cmd->indexes()->data()[2]]; auto stride2 = cmd->view()->GetAs(2)->stride()->data(); + auto blit1 = _selectUnitProc(bytes, stride1[2], 1); + auto blit2 = _selectUnitProc(bytes, stride2[2], 1); if (cmd->size()->data()[2] == 1 || (stride1[2] == 1 && stride2[2] == 1)) { for (int z=0; zsize()->data()[0]; ++z) { auto src0Z = src0 + z * stride1[0] * bytes; @@ -1009,8 +992,8 @@ class CPULoop : public Execution { auto src0Y = src0Z + y * stride1[1] * bytes; auto src1Y = src1Z + y * stride2[1] * bytes; auto dstY = dstZ + y * stride0[1] * bytes; - blit(cache0, src0Y, cmd->size()->data()[2], stride1[2], 1); - blit(cache1, src1Y, cmd->size()->data()[2], stride2[2], 1); + blit1(cache0, src0Y, cmd->size()->data()[2], stride1[2], 1); + blit2(cache1, src1Y, cmd->size()->data()[2], stride2[2], 1); proc(dstY, cache0, cache1, cmd->size()->data()[2], -1); } } diff --git a/source/backend/cpu/CPUReduction.cpp b/source/backend/cpu/CPUReduction.cpp index d5cbde821..9a726d783 100644 --- a/source/backend/cpu/CPUReduction.cpp +++ b/source/backend/cpu/CPUReduction.cpp @@ -126,6 +126,7 @@ class SumReduce : public Reduction { protected: virtual void onReduce(const float* src, float* dst, int inside, int outside, int axisSize) const override { auto numberThread = ((CPUBackend*)backend())->threadNumber(); + auto core = static_cast(backend())->functions(); MNN_CONCURRENCY_BEGIN(tId, numberThread) { for (int oi = tId; oi < outside; oi+=numberThread) { auto srcOutSide = src + oi * axisSize * inside; @@ -141,8 +142,12 @@ class SumReduce : public Reduction { auto srcInside = srcOutSide + ii; auto dstInside = dstOutSide + ii; float summer = 0.0f; - for (int a = 0; a < axisSize; ++a) { - summer += srcInside[a * inside]; + if (inside == 1) { + core->MNNAccumulateSequenceNumber(&summer, srcInside, axisSize); + } else { + for (int a = 0; a < axisSize; ++a) { + summer += srcInside[a * inside]; + } } *dstInside = summer; } diff --git a/source/backend/cpu/CPURelu.cpp b/source/backend/cpu/CPURelu.cpp index 2200009cb..fb5aaa335 100644 --- a/source/backend/cpu/CPURelu.cpp +++ b/source/backend/cpu/CPURelu.cpp @@ -164,6 +164,40 @@ CPUPRelu::~CPUPRelu() { } } +ErrorCode CPUPRelu::onResize(const std::vector& inputs, const std::vector& outputs) { + auto core = static_cast(backend())->functions(); + if (CPUBackend::getDataType(inputs[0]) == DataType_DT_INT8 || inputs[0]->getType().bytes() == 1) { + mUseInt8 = 1; + float inputScale = TensorUtils::getDescribe(inputs[0])->quantAttr->scale; + float outputScale = TensorUtils::getDescribe(outputs[0])->quantAttr->scale; + if (outputScale == 0) { + outputScale = 0; + } else { + outputScale = 1.0f / outputScale; + } + ssize_t inputZero = static_cast(TensorUtils::getDescribe(inputs[0])->quantAttr->zero); + ssize_t outputZero = static_cast(TensorUtils::getDescribe(outputs[0])->quantAttr->zero); + ssize_t maxValue = static_cast(TensorUtils::getDescribe(inputs[0])->quantAttr->max); + ssize_t minValue = static_cast(TensorUtils::getDescribe(inputs[0])->quantAttr->min); + float inputScales[1] = {inputScale}; + float outputScales[1] = {outputScale}; + ssize_t inputZeros[1] = {inputZero}; + ssize_t outputZeros[1] = {outputZero}; + mQuanScalesInput.resize(1); + mQuanScalesOutput.resize(1); + mQuanZerosInput.resize(1); + mQuanZerosOutput.resize(1); + mQuanScalesInput = {inputScale}; + mQuanScalesOutput = {outputScale}; + mQuanZerosInput = {inputZero}; + mQuanZerosOutput = {outputZero}; + auto p = mSlope.host(); + for (int i = 0; i < mSlope.buffer().dim[0].extent; ++i) { + p[i] = p[i] * inputScale * outputScale; + } + } + return NO_ERROR; +} ErrorCode CPUPRelu::onExecute(const std::vector& inputs, const std::vector& outputs) { auto& ib = inputs[0]->buffer(); @@ -173,6 +207,7 @@ ErrorCode CPUPRelu::onExecute(const std::vector& inputs, const std::vec sizeQuad *= ib.dim[i].extent; } auto core = static_cast(backend())->functions(); + auto coreInt8 = static_cast(backend())->int8Functions(); const int channel = ib.dim[1].extent; const int batch = ib.dim[0].extent; const int depthQuad = UP_DIV(channel, core->pack); @@ -180,6 +215,24 @@ ErrorCode CPUPRelu::onExecute(const std::vector& inputs, const std::vec uint8_t* dstO = (uint8_t*)ob.host; auto totalCount = batch * depthQuad; auto numberThread = ((CPUBackend*)backend())->threadNumber(); + if (mUseInt8) { + + MNN_CONCURRENCY_BEGIN(tId, numberThread) { + QuanPrePostParameters params; + params.maxValue = static_cast(TensorUtils::getDescribe(inputs[0])->quantAttr->max); + params.minValue = static_cast(TensorUtils::getDescribe(inputs[0])->quantAttr->min); + params.inputScale = mQuanScalesInput.data(); + params.inputZeroPoint = mQuanZerosInput.data(); + params.outputScale = mQuanScalesOutput.data(); + params.outputZeroPoint = mQuanZerosOutput.data(); + for (int b=tId; bMNNReluWithSlopeChannelInt8((int8_t*)(dstO + sizeQuad * core->pack * b), (const int8_t*)(srcO + sizeQuad * core->pack * b), (const float*)(mSlope.host() + core->bytes * core->pack * c), sizeQuad, 1, ¶ms); + } + } + MNN_CONCURRENCY_END(); + return NO_ERROR; + } MNN_CONCURRENCY_BEGIN(tId, numberThread) { for (int b=tId; b &inputs, const std::vector &outputs) override; - + virtual ErrorCode onResize(const std::vector &inputs, const std::vector &outputs) override; private: Tensor mSlope; + int mUseInt8 = 0; + std::vector mQuanScalesInput; + std::vector mQuanScalesOutput; + std::vector mQuanZerosInput; + std::vector mQuanZerosOutput; + std::shared_ptr mParams; }; class CPURelu6 : public Execution { diff --git a/source/backend/cpu/CPUTopKV2.cpp b/source/backend/cpu/CPUTopKV2.cpp index 6f5bbe059..fe96d18a6 100644 --- a/source/backend/cpu/CPUTopKV2.cpp +++ b/source/backend/cpu/CPUTopKV2.cpp @@ -11,99 +11,39 @@ #include "core/Macro.h" #include "core/Concurrency.h" #include "backend/cpu/compute/CommonOptFunction.h" - +#include namespace MNN { -template -class TopContainer { -public: - TopContainer() = delete; - TopContainer(int32_t k, int32_t rowSize, bool largest) : mK(k), mLargest(largest) { - mContainer.reserve(std::min(k, rowSize) + 1); - } - - void startCollecting(const T* values) { - mValues = values; - mContainer.clear(); - } - void push(int32_t a) { - std::function comparator; - if (mLargest) { - comparator = [this](int32_t a, int32_t b) { return compareDescend(a, b); }; - } else { - comparator = [this](int32_t a, int32_t b) { return compareAscend(a, b); }; - } - if (mContainer.size() <= mK) { - mContainer.push_back(a); - if (mContainer.size() == mK + 1) { - std::make_heap(mContainer.begin(), mContainer.end(), comparator); - std::pop_heap(mContainer.begin(), mContainer.end(), comparator); - } - } else if (comparator(a, mContainer.front())) { - mContainer.back() = a; - std::push_heap(mContainer.begin(), mContainer.end(), comparator); - std::pop_heap(mContainer.begin(), mContainer.end(), comparator); - } - } - - const std::vector& sortedResult() { - std::function comparator; - if (mLargest) { - comparator = [this](int32_t a, int32_t b) { return compareDescend(a, b); }; - } else { - comparator = [this](int32_t a, int32_t b) { return compareAscend(a, b); }; - } - if (mContainer.size() <= mK) { - std::sort(mContainer.begin(), mContainer.end(), comparator); - } else { - std::sort_heap(mContainer.begin(), mContainer.end() - 1, comparator); - mContainer.resize(mK); - } - return mContainer; - } - -private: - int32_t mK; - bool mLargest; - std::vector mContainer; - const T* mValues = nullptr; - - bool compareDescend(int32_t a, int32_t b) const { - if (mValues[b] < mValues[a]) { - return true; - } else if (mValues[b] > mValues[a]) { - return false; - } else { - return a < b; - } - } - bool compareAscend(int32_t a, int32_t b) const { - if (mValues[b] < mValues[a]) { - return false; - } else if (mValues[b] > mValues[a]) { - return true; - } else { - return a < b; - } - } -}; - template void findTopK(int32_t rowSize, int32_t numRows, const T* data, int32_t k, int32_t* outputIndexes, T* outputValues, bool largest) { - TopContainer topc(k, rowSize, largest); + struct DataType { + T value; + int index; + }; + std::vector cacheData(rowSize); + auto compareL = [](const DataType& A, const DataType& B) { + return A.value > B.value; + }; + auto compareM = [](const DataType& A, const DataType& B) { + return A.value < B.value; + }; for (int row = 0; row < numRows; row++) { const T* valuesRow = data + row * rowSize; - topc.startCollecting(valuesRow); - for (int c = 0; c < rowSize; c++) { - topc.push(c); - } - int32_t* indexesRow = outputIndexes + row * k; T* outputRow = outputValues + row * k; - - const auto& topK = topc.sortedResult(); - std::copy(topK.begin(), topK.end(), indexesRow); - std::transform(topK.begin(), topK.end(), outputRow, [valuesRow](const int32_t loc) { return valuesRow[loc]; }); + for (int i=0; i +#include "math/Vec.hpp" +#include "core/TensorUtils.hpp" namespace MNN { -CPUUnary::CPUUnary(Backend *b, MNNUnaryExecute proc) : MNN::Execution(b), mProc(proc) { - // nothing to do +using VecType = Math::Vec; +CPUUnary::CPUUnary(Backend *b, MNNUnaryExecute proc, MNNUnaryExecuteInt8 procInt8, const Op* op) : MNN::Execution(b), mProc(proc), mProcInt8(procInt8){ + if (op->main_as_UnaryOp()->tableInt8()) { + mTableBuffer.resize(255); + ::memcpy(mTableBuffer.data(), op->main_as_UnaryOp()->tableInt8()->data(), 255); + } } ErrorCode CPUUnary::onResize(const std::vector &inputs, const std::vector &outputs) { MNN_ASSERT(1 == outputs.size()); MNN_ASSERT(inputs[0]->getType() == halide_type_of() || inputs[0]->getType() == halide_type_of()); + if (mProcInt8) { + auto quantIn = TensorUtils::getDescribe(inputs[0])->quantAttr; + auto quantOut = TensorUtils::getDescribe(outputs[0])->quantAttr; + float outpScale = quantOut->scale == 0.f ? 0.f: 1.0f / quantOut->scale; + mInpScale.push_back(quantIn->scale); + mOupScale.push_back(outpScale); + mInpZeroPoint.push_back(quantIn->zero); + mOupZeroPoint.push_back(quantOut->zero); + mMaxMinValue = {static_cast(quantOut->min), static_cast(quantOut->max)}; + } return NO_ERROR; } @@ -30,9 +46,266 @@ static void _Neg(void* out, const void* inp, int realSize) { MNNScaleAndAddBiasScalar((float*)out, (const float*)inp, 0.0f, -1.0f, realSize); } +static void _NegInt8(void* out, const void* inp, int realSize, QuanPrePostParameters* params) { + int sizeDiv16 = realSize / 16; + int start = 0; +#ifdef MNN_USE_NEON + int8_t* outPtr = (int8_t*)out; + int8_t* inPtr = (int8_t*)inp; + int8x8_t inZeroPoint = vdup_n_s8(params->inputZeroPoint[0]); + int8x8_t outZeroPoint = vdup_n_s8(params->outputZeroPoint[0]); + float32x4_t inpScale = vdupq_n_f32(params->inputScale[0]); + float32x4_t outScale = vdupq_n_f32(params->outputScale[0]); + if (sizeDiv16 > 0) { + for (int i = 0;i < sizeDiv16; ++i) { + int8x16_t negValue = vld1q_s8(inPtr); + int16x8_t val16_0 = vmovl_s8(vget_low_s8(negValue)); + int16x8_t val16_1 = vmovl_s8(vget_high_s8(negValue)); + val16_0 = vsubw_s8(val16_0, inZeroPoint); + val16_1 = vsubw_s8(val16_1, inZeroPoint); + int32x4_t val32_00 = vmovl_s16(vget_low_s16(val16_0)); + int32x4_t val32_01 = vmovl_s16(vget_high_s16(val16_0)); + int32x4_t val32_10 = vmovl_s16(vget_low_s16(val16_1)); + int32x4_t val32_11 = vmovl_s16(vget_high_s16(val16_1)); + float32x4_t valF_00 = vcvtq_f32_s32(val32_00); + float32x4_t valF_01 = vcvtq_f32_s32(val32_01); + float32x4_t valF_10 = vcvtq_f32_s32(val32_10); + float32x4_t valF_11 = vcvtq_f32_s32(val32_11); + valF_00 = vmulq_f32(valF_00, inpScale); + valF_01 = vmulq_f32(valF_01, inpScale); + valF_10 = vmulq_f32(valF_10, inpScale); + valF_11 = vmulq_f32(valF_11, inpScale); + valF_00 = vnegq_f32(valF_00); + valF_01 = vnegq_f32(valF_01); + valF_10 = vnegq_f32(valF_10); + valF_11 = vnegq_f32(valF_11); + valF_00 = vmulq_f32(valF_00, outScale); + valF_01 = vmulq_f32(valF_01, outScale); + valF_10 = vmulq_f32(valF_10, outScale); + valF_11 = vmulq_f32(valF_11, outScale); + int32x4_t val_00 = vcvtq_s32_f32(valF_00); + int32x4_t val_01 = vcvtq_s32_f32(valF_01); + int32x4_t val_10 = vcvtq_s32_f32(valF_10); + int32x4_t val_11 = vcvtq_s32_f32(valF_11); + int16x4_t v16_0 = vqmovn_s32(val_00); + int16x4_t v16_1 = vqmovn_s32(val_01); + int16x4_t v16_2 = vqmovn_s32(val_10); + int16x4_t v16_3 = vqmovn_s32(val_11); + int16x8_t v16_4 = vcombine_s16(v16_0, v16_1); + int16x8_t v16_5 = vcombine_s16(v16_2, v16_3); + v16_4 = vaddw_s8(v16_4, outZeroPoint); + v16_5 = vaddw_s8(v16_5, outZeroPoint); + int8x8_t v8_0 = vqmovn_s16(v16_4); + int8x8_t v8_1 = vqmovn_s16(v16_5); + + vst1_s8(outPtr, v8_0); + vst1_s8(outPtr + 8, v8_1); + inPtr += 16; + outPtr += 16; + } + start = 16 * sizeDiv16; + } +#endif +#ifdef MNN_USE_SSE + uint8_t* dst = (uint8_t*)out; + uint8_t* src = (uint8_t*)inp; + int offset = 128; +#else + int8_t* dst = (int8_t*)out; + int8_t* src = (int8_t*)inp; + int offset = 0; +#endif + int inzero_ = static_cast(params->inputZeroPoint[0]); + int outzero_ = static_cast(params->outputZeroPoint[0]); + float inscale_ = params->inputScale[0]; + float outscale_ = params->outputScale[0]; + int min_ = static_cast(params->minValue); + int max_ = static_cast(params->maxValue); + for (int i = start; i < realSize; ++i) { + int value = -(src[i] - inzero_ - offset) * inscale_ * outscale_ + outzero_; + if (value > max_) { + value = max_; + } + if (value < min_) { + value = min_; + } + dst[i] = value + offset; + } +} + static void _ABS(void* out, const void* inp, int realSize) { MNNReluWithSlopeCommon((float*)out, (const float*)inp, realSize, -1.0f); } +static void _ABSInt8(void* out, const void* inp, int realSize, QuanPrePostParameters* params) { + int sizeDiv16 = realSize / 16; + int start = 0; +#ifdef MNN_USE_NEON + int8_t* outPtr = (int8_t*)out; + int8_t* inPtr = (int8_t*)inp; + int8x8_t inZeroPoint = vdup_n_s8(params->inputZeroPoint[0]); + int8x8_t outZeroPoint = vdup_n_s8(params->outputZeroPoint[0]); + float32x4_t inpScale = vdupq_n_f32(params->inputScale[0]); + float32x4_t outScale = vdupq_n_f32(params->outputScale[0]); + if (sizeDiv16 > 0) { + for (int i = 0;i < sizeDiv16; ++i) { + int8x16_t absValue = vld1q_s8(inPtr); + int16x8_t val16_0 = vmovl_s8(vget_low_s8(absValue)); + int16x8_t val16_1 = vmovl_s8(vget_high_s8(absValue)); + val16_0 = vsubw_s8(val16_0, inZeroPoint); + val16_1 = vsubw_s8(val16_1, inZeroPoint); + int32x4_t val32_00 = vmovl_s16(vget_low_s16(val16_0)); + int32x4_t val32_01 = vmovl_s16(vget_high_s16(val16_0)); + int32x4_t val32_10 = vmovl_s16(vget_low_s16(val16_1)); + int32x4_t val32_11 = vmovl_s16(vget_high_s16(val16_1)); + float32x4_t valF_00 = vcvtq_f32_s32(val32_00); + float32x4_t valF_01 = vcvtq_f32_s32(val32_01); + float32x4_t valF_10 = vcvtq_f32_s32(val32_10); + float32x4_t valF_11 = vcvtq_f32_s32(val32_11); + valF_00 = vmulq_f32(valF_00, inpScale); + valF_01 = vmulq_f32(valF_01, inpScale); + valF_10 = vmulq_f32(valF_10, inpScale); + valF_11 = vmulq_f32(valF_11, inpScale); + valF_00 = vabsq_f32(valF_00); + valF_01 = vabsq_f32(valF_01); + valF_10 = vabsq_f32(valF_10); + valF_11 = vabsq_f32(valF_11); + valF_00 = vmulq_f32(valF_00, outScale); + valF_01 = vmulq_f32(valF_01, outScale); + valF_10 = vmulq_f32(valF_10, outScale); + valF_11 = vmulq_f32(valF_11, outScale); + int32x4_t val_00 = vcvtq_s32_f32(valF_00); + int32x4_t val_01 = vcvtq_s32_f32(valF_01); + int32x4_t val_10 = vcvtq_s32_f32(valF_10); + int32x4_t val_11 = vcvtq_s32_f32(valF_11); + int16x4_t v16_0 = vqmovn_s32(val_00); + int16x4_t v16_1 = vqmovn_s32(val_01); + int16x4_t v16_2 = vqmovn_s32(val_10); + int16x4_t v16_3 = vqmovn_s32(val_11); + int16x8_t v16_4 = vcombine_s16(v16_0, v16_1); + int16x8_t v16_5 = vcombine_s16(v16_2, v16_3); + v16_4 = vaddw_s8(v16_4, outZeroPoint); + v16_5 = vaddw_s8(v16_5, outZeroPoint); + int8x8_t v8_0 = vqmovn_s16(v16_4); + int8x8_t v8_1 = vqmovn_s16(v16_5); + + vst1_s8(outPtr, v8_0); + vst1_s8(outPtr + 8, v8_1); + inPtr += 16; + outPtr += 16; + } + start = 16 * sizeDiv16; + } +#endif +#ifdef MNN_USE_SSE + uint8_t* dst = (uint8_t*)out; + uint8_t* src = (uint8_t*)inp; + int offset = 128; +#else + int8_t* dst = (int8_t*)out; + int8_t* src = (int8_t*)inp; + int offset = 0; +#endif + int inzero_ = static_cast(params->inputZeroPoint[0]); + int outzero_ = static_cast(params->outputZeroPoint[0]); + for (int i = start; i < realSize; ++i) { + auto value = abs((src[i] - inzero_ - offset) * params->inputScale[0]); + value = value * params->outputScale[0] + outzero_; + if (value > params->maxValue) { + value = params->maxValue; + } + if (value < params->minValue) { + value = params->minValue; + } + dst[i] = value + offset; + } +} + +static void _SignInt8(void* out, const void* inp, int realSize, QuanPrePostParameters* params) { + int sizeDiv16 = realSize / 16; + int start = 0; +#ifdef MNN_USE_NEON + int8_t* outPtr = (int8_t*)out; + int8_t* inPtr = (int8_t*)inp; + int8x16_t one = vdupq_n_s8(1); + int8x16_t negone = vdupq_n_s8(-1); + int16x8_t zero = vdupq_n_s16(0); + int8x8_t inZeroPoint = vdup_n_s8(params->inputZeroPoint[0]); + int8x8_t outZeroPoint = vdup_n_s8(params->outputZeroPoint[0]); + float32x4_t outScale = vdupq_n_f32(params->outputScale[0]); + if (sizeDiv16 > 0) { + for (int i = 0;i < sizeDiv16; ++i) { + int8x16_t value = vld1q_s8(inPtr); + int16x8_t vallow = vmovl_s8(vget_low_s8(value)); + int16x8_t valhi = vmovl_s8(vget_high_s8(value)); + vallow = vsubw_s8(vallow, inZeroPoint); + valhi = vsubw_s8(valhi, inZeroPoint); + uint16x8_t lomask1 = vcgtq_s16(vallow, zero); + uint16x8_t lomask_1 = vcltq_s16(vallow, zero); + uint16x8_t himask1 = vcgtq_s16(valhi, zero); + uint16x8_t himask_1 = vcltq_s16(valhi, zero); + vallow = vbslq_s16(lomask1, vallow, one); + vallow = vbslq_s16(lomask_1, vallow, negone); + valhi = vbslq_s16(himask1, valhi, one); + valhi = vbslq_s16(himask_1, valhi, negone); + int32x4_t val32_00 = vmovl_s16(vget_low_s16(vallow)); + int32x4_t val32_01 = vmovl_s16(vget_high_s16(vallow)); + int32x4_t val32_10 = vmovl_s16(vget_low_s16(valhi)); + int32x4_t val32_11 = vmovl_s16(vget_high_s16(valhi)); + float32x4_t valF_00 = vcvtq_f32_s32(val32_00); + float32x4_t valF_01 = vcvtq_f32_s32(val32_01); + float32x4_t valF_10 = vcvtq_f32_s32(val32_10); + float32x4_t valF_11 = vcvtq_f32_s32(val32_11); + valF_00 = vmulq_f32(valF_00, outScale); + valF_01 = vmulq_f32(valF_01, outScale); + valF_10 = vmulq_f32(valF_10, outScale); + valF_11 = vmulq_f32(valF_11, outScale); + int32x4_t val_00 = vcvtq_s32_f32(valF_00); + int32x4_t val_01 = vcvtq_s32_f32(valF_01); + int32x4_t val_10 = vcvtq_s32_f32(valF_10); + int32x4_t val_11 = vcvtq_s32_f32(valF_11); + int16x4_t v16_0 = vqmovn_s32(val_00); + int16x4_t v16_1 = vqmovn_s32(val_01); + int16x4_t v16_2 = vqmovn_s32(val_10); + int16x4_t v16_3 = vqmovn_s32(val_11); + int16x8_t v16_4 = vcombine_s16(v16_0, v16_1); + int16x8_t v16_5 = vcombine_s16(v16_2, v16_3); + v16_4 = vaddw_s8(v16_4, outZeroPoint); + v16_5 = vaddw_s8(v16_5, outZeroPoint); + int8x8_t v8_0 = vqmovn_s16(v16_4); + int8x8_t v8_1 = vqmovn_s16(v16_5); + vst1_s8(outPtr, v8_0); + vst1_s8(outPtr + 8, v8_1); + inPtr += 16; + outPtr += 16; + } + start = 16 * sizeDiv16; + } +#endif +#ifdef MNN_USE_SSE + uint8_t* dst = (uint8_t*)out; + uint8_t* src = (uint8_t*)inp; + int offset = 128; +#else + int8_t* dst = (int8_t*)out; + int8_t* src = (int8_t*)inp; + int offset = 0; +#endif + int inzero_ = static_cast(params->inputZeroPoint[0]); + int outzero_ = static_cast(params->outputZeroPoint[0]); + for (int i = start; i < realSize; ++i) { + auto value = src[i] - offset - inzero_; + if (value > 0) { + int f = 1 * params->outputScale[0] + outzero_; + dst[i] = f + offset; + } else if (value < 0) { + int f = -1 * params->outputScale[0] + outzero_; + dst[i] = f + offset; + } else { + dst[i] = outzero_ + offset; + } + } +} + static void _Square(void* out, const void* inp, int realSize) { MNNMatrixProdCommon((float*)out, (const float*)inp, (const float*)inp, realSize, 0, 0, 0, 1); } @@ -153,6 +426,20 @@ static MNNUnaryExecute selectForInt(int type) { } return nullptr; } + +MNNUnaryExecuteInt8 CPUUnary::selectForInt8(int type) { + switch (type) { + case UnaryOpOperation_ABS: + return _ABSInt8; + case UnaryOpOperation_NEG: + return _NegInt8; + case UnaryOpOperation_SIGN: + return _SignInt8; + default: + break; + } + return nullptr; +} ErrorCode CPUUnary::onExecute(const std::vector &inputs, const std::vector &outputs) { auto input = inputs[0]; auto output = outputs[0]; @@ -164,6 +451,57 @@ ErrorCode CPUUnary::onExecute(const std::vector &inputs, const std::ve if (halide_type_float == output->getType().code) { outBytes = static_cast(backend())->functions()->bytes; } + if (mTableBuffer.data()) { +#ifdef MNN_USE_SSE + uint8_t* srcO = inputPtr; + uint8_t* dstO = outputPtr; + int offset = 128; +#else + int8_t* srcO = (int8_t*)inputPtr; + int8_t* dstO = (int8_t*)outputPtr; + int offset = 0; +#endif + MNN_CONCURRENCY_BEGIN(tId, schedule.second) { + int start = schedule.first * (int)tId; + int realSize = schedule.first; + if (tId == schedule.second -1 ) { + realSize = size - start; + } + if (realSize > 0) { + auto inp = srcO + start; + auto out = dstO + start; + for (int i = 0; i < realSize; ++i) { + int idx = inp[i] - offset + 127; + out[i] = offset + mTableBuffer[idx]; + } + } + } + MNN_CONCURRENCY_END(); + return NO_ERROR; + } + if (mProcInt8) { + QuanPrePostParameters params; + params.inputScale = mInpScale.data(); + params.outputScale = mOupScale.data(); + params.inputZeroPoint= mInpZeroPoint.data(); + params.outputZeroPoint = mOupZeroPoint.data(); + params.maxValue = mMaxMinValue[1]; + params.minValue = mMaxMinValue[0]; + MNN_CONCURRENCY_BEGIN(tId, schedule.second) { + int start = schedule.first * (int)tId; + int realSize = schedule.first; + if (tId == schedule.second -1 ) { + realSize = size - start; + } + if (realSize > 0) { + auto inp = inputPtr + start; + auto out = outputPtr + start; + mProcInt8(out, inp, realSize, ¶ms); + } + } + MNN_CONCURRENCY_END(); + return NO_ERROR; + } MNN_CONCURRENCY_BEGIN(tId, schedule.second) { int start = schedule.first * (int)tId; int realSize = schedule.first; @@ -184,18 +522,22 @@ class CPUUnaryCreator : public CPUBackend::Creator { public: virtual Execution *onCreate(const std::vector &inputs, const std::vector &outputs, const MNN::Op *op, Backend *backend) const override { + auto core = static_cast(backend)->functions(); auto precision = static_cast(backend)->precisionMode(); auto type = inputs[0]->getType(); MNNUnaryExecute proc = nullptr; - if (type.code == halide_type_int) { + MNNUnaryExecuteInt8 procInt8 = nullptr; + if (CPUBackend::getDataType(inputs[0]) == DataType_DT_INT8 || inputs[0]->getType().bytes() == 1) { + procInt8 = core->MNNSelectUnaryFunctionForInt8(op->main_as_UnaryOp()->opType()); + } else if (type.code == halide_type_int) { proc = selectForInt(op->main_as_UnaryOp()->opType()); } else if (type.code == halide_type_float) { - proc = static_cast(backend)->functions()->MNNSelectUnaryFunctionForFloat(op->main_as_UnaryOp()->opType(), static_cast(backend)->precisionMode()); + proc = core->MNNSelectUnaryFunctionForFloat(op->main_as_UnaryOp()->opType(), static_cast(backend)->precisionMode()); } - if (nullptr == proc) { + if (nullptr == proc && nullptr == procInt8 && nullptr == op->main_as_UnaryOp()->tableInt8()) { return nullptr; } - return new CPUUnary(backend, proc); + return new CPUUnary(backend, proc, procInt8, op); } }; diff --git a/source/backend/cpu/CPUUnary.hpp b/source/backend/cpu/CPUUnary.hpp index 532aaf92d..d6690184a 100644 --- a/source/backend/cpu/CPUUnary.hpp +++ b/source/backend/cpu/CPUUnary.hpp @@ -15,14 +15,22 @@ namespace MNN { class CPUUnary : public Execution { public: - CPUUnary(Backend *b, MNNUnaryExecute proc); + CPUUnary(Backend *b, MNNUnaryExecute proc, MNNUnaryExecuteInt8 procInt8, const Op* op); virtual ~CPUUnary() = default; virtual ErrorCode onResize(const std::vector &inputs, const std::vector &outputs) override; virtual ErrorCode onExecute(const std::vector &inputs, const std::vector &outputs) override; static MNNUnaryExecute selectForFloat(int type, int precision); + static MNNUnaryExecuteInt8 selectForInt8(int type); protected: MNNUnaryExecute mProc; + MNNUnaryExecuteInt8 mProcInt8; + std::vector mInpScale; + std::vector mOupScale; + std::vector mInpZeroPoint; + std::vector mOupZeroPoint; + std::vector mMaxMinValue; + std::vector mTableBuffer; }; } // namespace MNN #endif /* CPUUnary_hpp */ diff --git a/source/backend/cpu/arm/arm32/MNNGelu.S b/source/backend/cpu/arm/arm32/MNNGelu.S index 69b755835..33db1ffb6 100644 --- a/source/backend/cpu/arm/arm32/MNNGelu.S +++ b/source/backend/cpu/arm/arm32/MNNGelu.S @@ -59,8 +59,16 @@ vadd.f32 q2, q2, q0 vmul.f32 q3, q3, q15 vadd.f32 q3, q3, q1 -vmul.f32 q2, q2, q14 -vmul.f32 q3, q3, q14 +vmul.f32 q2, q2, q14 // value +vmul.f32 q3, q3, q14 // value + +// if value > 5, then value=5; if value<-5, then value=-5 +vmov.f32 q7, #5.0 +vmov.f32 q6, #-5.0 +vmax.f32 q2, q2, q6 +vmax.f32 q3, q3, q6 +vmin.f32 q2, q2, q7 +vmin.f32 q3, q3, q7 // tanh(value) vmul.f32 q4, q2, q2 // q4: value*value diff --git a/source/backend/cpu/arm/arm32/MNNReluWithSlopeChannelInt8.S b/source/backend/cpu/arm/arm32/MNNReluWithSlopeChannelInt8.S new file mode 100644 index 000000000..4595733b8 --- /dev/null +++ b/source/backend/cpu/arm/arm32/MNNReluWithSlopeChannelInt8.S @@ -0,0 +1,170 @@ +// +// MNNReluWithSlopeChannel.S +// MNN +// +// Created by MNN on 2019/02/04. +// Copyright © 2018, Alibaba Group Holding Limited +// + +/* + struct QuanPrePostParameters{ + float* inputScale; + float* outputScale; + ssize_t* inputZeroPoint; + ssize_t* outputZeroPoint; + ssize_t minValue; + ssize_t maxValue; +}; + */ + +#ifdef __arm__ +#ifndef __aarch64__ +#include "MNNAsmGlobal.h" + +.text +.align 5 + + +asm_function MNNReluWithSlopeChannelInt8 +// MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params) +// Auto load: +// r0: dst, r1: src, r2: slope, r3: planeNumber +// Load from sp: +// r4: depthQuad, r5: params +// Load from r5: r8: inputZeroPoint, r6: outputZeroPoint, r10: minValue, r11: maxValue +push {r4-r8, r10-r11, lr} +ldr r4, [sp, #32] +ldr r5, [sp, #36] + +vpush {q4-q7} + +ldr r8, [r5, #8] +ldr r6, [r5, #12] +ldr r10, [r5, #16] +ldr r11, [r5, #20] + +cmp r4, #0 +beq PReluEnd +cmp r3, #0 +beq PReluEnd + +vmov.f32 q12, #0.5 +vmov.f32 q13, #-0.5 +.macro ROUND_TWO x0, x1 + vcgt.f32 q10, \x0, #0 + vcgt.f32 q11, \x1, #0 + vbsl.f32 q10, q12, q13 + vbsl.f32 q11, q12, q13 + vadd.f32 \x0, q10, \x0 + vadd.f32 \x1, q11, \x1 + vcvt.s32.f32 \x0, \x0 + vcvt.s32.f32 \x1, \x1 +.endm + +.macro ROUND_ONE x0 + vcgt.f32 q10, \x0, #0 + vbsl.f32 q10, q12, q13 + vadd.f32 \x0, q10, \x0 + vcvt.s32.f32 \x0, \x0 +.endm + +vld1.8 d30[0], [r8] +vdup.8 d30, d30[0] // inputZeroPoint + +vld1.8 d31[0], [r6] +vdup.8 d31, d31[0] // outputZeroPoint + +PReluZLoop: +vld1.32 {q14}, [r2]! + +mov r5, r3 +cmp r5, #3 + +ble PReluL1 + +PReluL4Loop: +vld1.8 {q0}, [r1]! +vmovl.s8 q1, d0 +vmovl.s8 q2, d1 +vsubw.s8 q1, q1, d30 +vsubw.s8 q2, q2, d30 +vmovl.s16 q3, d2 +vmovl.s16 q4, d3 +vmovl.s16 q5, d4 +vmovl.s16 q6, d5 + +vclt.s8 q1, q0, #0 + +vcvt.f32.s32 q3, q3 +vcvt.f32.s32 q4, q4 +vcvt.f32.s32 q5, q5 +vcvt.f32.s32 q6, q6 + +vmul.f32 q3, q3, q14 +vmul.f32 q4, q4, q14 +vmul.f32 q5, q5, q14 +vmul.f32 q6, q6, q14 + +ROUND_TWO q3, q4 +ROUND_TWO q5, q6 + +vdup.8 q10, r10 +vdup.8 q11, r11 + +vqmovn.s32 d14, q3 +vqmovn.s32 d15, q4 +vqmovn.s32 d16, q5 +vqmovn.s32 d17, q6 +vaddw.s8 q7, q7, d31 +vaddw.s8 q8, q8, d31 +vqmovn.s16 d18, q7 +vqmovn.s16 d19, q8 +vmax.s8 q9, q9, q10 +vmin.s8 q9, q9, q11 + +vbit.8 q0, q9, q1 +vst1.8 {q0}, [r0]! + +sub r5, r5, #4 +cmp r5, #4 +bge PReluL4Loop + +PReluL1: +cmp r5, #0 +beq PReluL1End + +PReluL1Loop: +vld1.32 {d0[0]}, [r1]! +vmovl.s8 q1, d0 +vsubw.s8 q1, q1, d30 + +vmovl.s16 q2, d2 +vclt.s8 d10, d0, #0 + +vcvt.f32.s32 q2, q2 +vmul.f32 q2, q2, q14 + +ROUND_ONE q2 + +vqmovn.s32 d4, q2 +vaddw.s8 q2, q2, d31 +vqmovn.s16 d4, q2 + +vbit.8 d0, d4, d10 +vst1.32 {d0[0]}, [r0]! + +subs r5, r5, #1 +bne PReluL1Loop + +PReluL1End: + +subs r4, r4, #1 +bne PReluZLoop + + +PReluEnd: +vpop {q4-q7} +pop {r4-r8, r10-r11, pc} + +#endif +#endif diff --git a/source/backend/cpu/arm/arm32/bf16/MNNGelu_BF16.S b/source/backend/cpu/arm/arm32/bf16/MNNGelu_BF16.S index 57ed3228f..15dcfdcad 100644 --- a/source/backend/cpu/arm/arm32/bf16/MNNGelu_BF16.S +++ b/source/backend/cpu/arm/arm32/bf16/MNNGelu_BF16.S @@ -65,6 +65,13 @@ vadd.f32 q3, q3, q1 vmul.f32 q2, q2, q14 vmul.f32 q3, q3, q14 +vmov.f32 q7, #5.0 +vmov.f32 q6, #-5.0 +vmax.f32 q2, q2, q6 +vmax.f32 q3, q3, q6 +vmin.f32 q2, q2, q7 +vmin.f32 q3, q3, q7 + // tanh(value) vmul.f32 q4, q2, q2 // q4: value*value vmul.f32 q5, q3, q3 // q5: value*value diff --git a/source/backend/cpu/arm/arm64/MNNGelu.S b/source/backend/cpu/arm/arm64/MNNGelu.S index 2131ba5b1..1f7d42936 100644 --- a/source/backend/cpu/arm/arm64/MNNGelu.S +++ b/source/backend/cpu/arm/arm64/MNNGelu.S @@ -45,6 +45,9 @@ dup v10.4s, w9 // v10: [28.f]x4 dup v9.4s, w10 // v9: [3150.f]x4 dup v8.4s, w11 // v8: [62370.f]x4 +fmov v30.4s, #5 +fmov v31.4s, #-5 + GeluZLoop: ld1 {v0.4s, v1.4s}, [x1], #32 // v0, v1: fp32x4 @@ -63,6 +66,11 @@ fmul v2.4s, v2.4s, v14.4s fmul v3.4s, v3.4s, v14.4s // tanh(value) +fmax v2.4s, v31.4s, v2.4s +fmax v3.4s, v31.4s, v3.4s +fmin v2.4s, v30.4s, v2.4s +fmin v3.4s, v30.4s, v3.4s + fmul v4.4s, v2.4s, v2.4s // q4: value*value fmul v5.4s, v3.4s, v3.4s // q5: value*value // a diff --git a/source/backend/cpu/arm/arm64/MNNReluWithSlopeChannelInt8.S b/source/backend/cpu/arm/arm64/MNNReluWithSlopeChannelInt8.S new file mode 100644 index 000000000..e1622504c --- /dev/null +++ b/source/backend/cpu/arm/arm64/MNNReluWithSlopeChannelInt8.S @@ -0,0 +1,147 @@ +// +// MNNReluWithSlopeChannel.S +// MNN +// +// Created by MNN on 2023/07/06. +// Copyright © 2018, Alibaba Group Holding Limited +// +/* + struct QuanPrePostParameters{ + float* inputScale; + float* outputScale; + ssize_t* inputZeroPoint; + ssize_t* outputZeroPoint; + ssize_t minValue; + ssize_t maxValue; +}; + */ +#ifdef __aarch64__ +#include "MNNAsmGlobal.h" + +.text +.align 5 + +asm_function MNNReluWithSlopeChannelInt8 +// MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params) +// Auto load: +// x0: dst, x1: src, x2: slope, x3: planeNumber, x4: depthQuad, x5: params +// Load from x5: x8: inputZeroPoint, x9: outputZeroPoint, x10: minValue, x11: maxValue + +ldr x8, [x5, #16] +ldr x9, [x5, #24] +ldr x10, [x5, #32] +ldr x11, [x5, #40] + + +stp d14, d15, [sp, #-64]! +stp d12, d13, [sp, #16] +stp d10, d11, [sp, #32] +stp d8, d9, [sp, #48] + +cmp x3, #0 +beq End +cmp x4, #0 +beq End + +ld1r {v29.8b}, [x8] // inputZeroPoint +ld1r {v28.8b}, [x9] // outputZeroPoint +dup v26.16b, w10 +dup v27.16b, w11 +/* +Quant parameters +*/ + + +PReluZLoop: +ld1 {v31.4s}, [x2], #16 // slope +mov x5, x3 +cmp x5, #3 +ble PReluL1 + +PReluL4Loop: +ld1 {v0.16b}, [x1], #16 +cmlt v30.16b, v0.16b, #0 // mask0: x<0 + +sxtl v1.8h, v0.8b +sxtl2 v2.8h, v0.16b +ssubw v1.8h, v1.8h, v29.8b +ssubw v2.8h, v2.8h, v29.8b +sxtl v3.4s, v1.4h +sxtl2 v4.4s, v1.8h +sxtl v5.4s, v2.4h +sxtl2 v6.4s, v2.8h + +scvtf v3.4s, v3.4s +scvtf v4.4s, v4.4s +scvtf v5.4s, v5.4s +scvtf v6.4s, v6.4s + +fmul v3.4s, v3.4s, v31.4s +fmul v4.4s, v4.4s, v31.4s +fmul v5.4s, v5.4s, v31.4s +fmul v6.4s, v6.4s, v31.4s + +fcvtas v3.4s, v3.4s +fcvtas v4.4s, v4.4s +fcvtas v5.4s, v5.4s +fcvtas v6.4s, v6.4s + +sqxtn v7.4h, v3.4s +sqxtn2 v7.8h, v4.4s +sqxtn v8.4h, v5.4s +sqxtn2 v8.8h, v6.4s + +saddw v7.8h, v7.8h, v28.8b +saddw v8.8h, v8.8h, v28.8b + +sqxtn v9.8b, v7.8h +sqxtn2 v9.16b, v8.8h +smax v9.16b, v9.16b, v26.16b +smin v9.16b, v9.16b, v27.16b + +bit v0.16b, v9.16b, v30.16b +st1 {v0.16b}, [x0], #16 + +sub x5, x5, #4 +cmp x5, #4 +bge PReluL4Loop + +PReluL1: +cmp x5, #0 + +beq PReluL1End + +PReluL1Loop: +ld1 {v0.s}[0], [x1], #4 +cmlt v30.8b, v0.8b, #0 + +sxtl v1.8h, v0.8b +ssubw v1.8h, v1.8h, v29.8b +sxtl v1.4s, v1.4h +scvtf v1.4s, v1.4s +fmul v1.4s, v1.4s, v31.4s +fcvtas v1.4s, v1.4s +sqxtn v1.4h, v1.4s +saddw v1.8h, v1.8h, v28.8b +sqxtn v1.8b, v1.8h +smax v1.8b, v1.8b, v26.8b +smin v1.8b, v1.8b, v27.8b + +bit v0.8b, v1.8b, v30.8b +st1 {v0.s}[0], [x0], #4 +subs x5, x5, #1 +bne PReluL1Loop + +PReluL1End: + +subs x4, x4, #1 +bne PReluZLoop + +End: + ldp d8, d9, [sp, #48] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #16] + ldp d14, d15, [sp], #64 + ret + +#endif \ No newline at end of file diff --git a/source/backend/cpu/arm/arm64/bf16/MNNGelu_BF16.S b/source/backend/cpu/arm/arm64/bf16/MNNGelu_BF16.S index c876cf5a6..8c81022f3 100644 --- a/source/backend/cpu/arm/arm64/bf16/MNNGelu_BF16.S +++ b/source/backend/cpu/arm/arm64/bf16/MNNGelu_BF16.S @@ -45,6 +45,9 @@ dup v10.4s, w9 // v10: [28.f]x4 dup v9.4s, w10 // v9: [3150.f]x4 dup v8.4s, w11 // v8: [62370.f]x4 +fmov v30.4s, #5 +fmov v31.4s, #-5 + GeluZLoop: ld1 {v0.4h, v1.4h}, [x1], #16 // v0, v1: 4xint16_t @@ -65,6 +68,11 @@ fadd v3.4s, v3.4s, v1.4s fmul v2.4s, v2.4s, v14.4s fmul v3.4s, v3.4s, v14.4s +fmax v2.4s, v31.4s, v2.4s +fmax v3.4s, v31.4s, v3.4s +fmin v2.4s, v30.4s, v2.4s +fmin v3.4s, v30.4s, v3.4s + // tanh(value) fmul v4.4s, v2.4s, v2.4s // q4: value*value fmul v5.4s, v3.4s, v3.4s // q5: value*value diff --git a/source/backend/cpu/arm/arm64/low_memory/MNNPackedMatMulRemain_int8.S b/source/backend/cpu/arm/arm64/low_memory/MNNPackedMatMulRemain_int8.S index 6e917f847..fa5648519 100644 --- a/source/backend/cpu/arm/arm64/low_memory/MNNPackedMatMulRemain_int8.S +++ b/source/backend/cpu/arm/arm64/low_memory/MNNPackedMatMulRemain_int8.S @@ -17,12 +17,14 @@ asm_function MNNPackedMatMulRemain_int8 //void MNNPackedMatMulRemain_int4(float* C, const float* A, const float* B, size_t eSize, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); //Auto x0: C, x1:A, x2:B, x3:eSize, x4:parameter, x5:postParameters, x6:bias, x7: k, x8: b ldr x8, [sp] -sub sp, sp, #64 -str x19, [sp, #0] -str x20, [sp, #8] -str x21, [sp, #16] -str x22, [sp, #24] -str x23, [sp, #32] +stp d14, d15, [sp, #(-16 * 8)]! +stp d12, d13, [sp, #16] +stp d10, d11, [sp, #32] +stp d8, d9, [sp, #48] +stp x25, x26, [sp, #(16 * 4)] +stp x23, x24, [sp, #(16 * 5)] +stp x21, x22, [sp, #(16 * 6)] +stp x19, x20, [sp, #(16 * 7)] mov x22, x7 // alpha mov x23, x8 // bias @@ -35,7 +37,8 @@ ldr x19, [x4, #40] // bExtraStride add x10, x10, #3 lsr x10, x10, #2 - +lsl x25, x9, #3 // l*hPack +add x25, x25, x19 cbz x5, Start ld1 {v5.4s}, [x5] dup v6.4s, v5.s[2] // Min Value @@ -196,7 +199,6 @@ LoopE8: add x13, x13, x19 sub x8, x8, #2 cmp x8, #2 - cbz x5, StoreLH8 AddBiasLH8: ld1 {v0.4s, v1.4s}, [x20], #32 @@ -255,7 +257,7 @@ LoopE8: fmin v29.4s, v29.4s, v7.4s fmin v30.4s, v30.4s, v7.4s fmin v31.4s, v31.4s, v7.4s - + StoreLH8: stp q16, q17, [x0] stp q18, q19, [x0, #(32 * 1)] @@ -438,7 +440,7 @@ LoopE8: subs x12, x12, #4 bne LoopLR LoopLREnd: - + cbz x5, StoreLH8x4 AddBiasLH8x4: ld1 {v0.4s}, [x20] @@ -471,7 +473,7 @@ LoopE8: fmin v21.4s, v21.4s, v7.4s fmin v22.4s, v22.4s, v7.4s fmin v23.4s, v23.4s, v7.4s - + StoreLH8x4: stp q16, q17, [x0] @@ -800,96 +802,151 @@ LoopE1: mov x14, x22 mov x16, x23 - cmp x8, #2 - blt E1LH4 + E1H16: + cmp x8, #4 + blt E1LH8 + E1LoopH16: + mov x15, x1 + add x24, x13, x25 + ld1 {v24.4s, v25.4s, v26.4s, v27.4s}, [x14], #64 // Weight dequantize scale + mov x12, x9 + ld1 {v28.4s, v29.4s, v30.4s, v31.4s}, [x20], #64 // Bias + + E1LoopH16L: + ld1 {v0.s}[0], [x15], x11 // Input Float + ld1 {v0.s}[1], [x15], x11 + ld1 {v1.16b}, [x13], #16 // Weight Int8 + ld1 {v2.16b}, [x24], #16 + ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [x16] // bias for dequantizing weight. + ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [x16] + subs x12, x12, #2 // Compute 2 point along L. + + // Dequantize Int8 weight. + sxtl v3.8h, v1.8b + sxtl2 v4.8h, v1.16b + sxtl v5.8h, v2.8b + sxtl2 v15.8h, v2.16b + + sxtl2 v8.4s, v3.8h + sxtl v9.4s, v4.4h + sxtl2 v10.4s, v4.8h + sxtl v11.4s, v5.4h + sxtl2 v12.4s, v5.8h + sxtl v13.4s, v15.4h + sxtl2 v14.4s, v15.8h + sxtl v1.4s, v3.4h + + + scvtf v8.4s, v8.4s + scvtf v9.4s, v9.4s + scvtf v10.4s, v10.4s + scvtf v11.4s, v11.4s + scvtf v12.4s, v12.4s + scvtf v13.4s, v13.4s + scvtf v14.4s, v14.4s + scvtf v1.4s, v1.4s + + fmla v17.4s, v8.4s, v25.4s + fmla v20.4s, v9.4s, v24.4s + fmla v21.4s, v10.4s, v25.4s + fmla v18.4s, v11.4s, v26.4s + fmla v19.4s, v12.4s, v27.4s + fmla v22.4s, v13.4s, v26.4s + fmla v23.4s, v14.4s, v27.4s + fmla v16.4s, v1.4s, v24.4s + + fmla v28.4s, v16.4s, v0.s[0] + fmla v29.4s, v17.4s, v0.s[0] + fmla v28.4s, v20.4s, v0.s[1] + fmla v29.4s, v21.4s, v0.s[1] + fmla v30.4s, v18.4s, v0.s[0] + fmla v31.4s, v19.4s, v0.s[0] + fmla v30.4s, v22.4s, v0.s[1] + fmla v31.4s, v23.4s, v0.s[1] + + bne E1LoopH16L + E1LoopH16LEnd: + sub x8, x8, #4 + add x13, x24, x19 + add x16, x16, #64 + + cbz x5, StoreE1H16 + + PostTreatE1H16: + fmax v28.4s, v28.4s, v6.4s + fmax v29.4s, v29.4s, v6.4s + fmin v30.4s, v30.4s, v7.4s + fmin v31.4s, v31.4s, v7.4s + + StoreE1H16: + cmp x8, #4 + st1 {v28.4s}, [x0], x7 + st1 {v29.4s}, [x0], x7 + st1 {v30.4s}, [x0], x7 + st1 {v31.4s}, [x0], x7 + bge E1LoopH16 E1LH8: + cmp x8, #2 + blt E1LH4 E1LoopH8: mov x15, x1 ld1 {v24.4s, v25.4s}, [x14], #32 // alpha - ld1 {v26.4s, v27.4s}, [x16], #32 // bias - subs x12, x9, #2 - // ld1 {v3.4s, v4.4s}, [x13], #32 - ld1 {v0.16b}, [x13], #16 - sxtl v11.8h, v0.8b - sxtl2 v12.8h, v0.16b - sxtl v8.4s, v11.4h - sxtl2 v9.4s, v11.8h - sxtl v10.4s, v12.4h - sxtl2 v11.4s, v12.8h - scvtf v12.4s, v8.4s - scvtf v13.4s, v9.4s - mov v8.16b, v26.16b - mov v9.16b, v27.16b - fmla v8.4s, v12.4s, v24.4s - fmla v9.4s, v13.4s, v25.4s - scvtf v12.4s, v10.4s - scvtf v13.4s, v11.4s - mov v10.16b, v26.16b - mov v11.16b, v27.16b - fmla v10.4s, v12.4s, v24.4s - fmla v11.4s, v13.4s, v25.4s - ld1 {v0.s}[0], [x15], x11 - ld1 {v0.s}[1], [x15], x11 - fmul v16.4s, v8.4s, v0.s[0] - fmul v20.4s, v9.4s, v0.s[0] - fmla v16.4s, v10.4s, v0.s[1] - fmla v20.4s, v11.4s, v0.s[1] - beq E1LoopLEnd + mov x12, x9 + ld1 {v27.4s, v28.4s}, [x20], #32 // Bias - E1LoopL: - subs x12, x12, #2 - // ld1 {v3.4s, v4.4s}, [x13], #32 + E1LoopH8L: ld1 {v0.16b}, [x13], #16 - sxtl v11.8h, v0.8b - sxtl2 v12.8h, v0.16b - sxtl v8.4s, v11.4h - sxtl2 v9.4s, v11.8h - sxtl v10.4s, v12.4h - sxtl2 v11.4s, v12.8h - scvtf v12.4s, v8.4s - scvtf v13.4s, v9.4s - mov v8.16b, v26.16b - mov v9.16b, v27.16b - fmla v8.4s, v12.4s, v24.4s - fmla v9.4s, v13.4s, v25.4s - scvtf v12.4s, v10.4s - scvtf v13.4s, v11.4s - mov v10.16b, v26.16b - mov v11.16b, v27.16b - fmla v10.4s, v12.4s, v24.4s - fmla v11.4s, v13.4s, v25.4s - ld1 {v0.s}[0], [x15], x11 - ld1 {v0.s}[1], [x15], x11 - fmla v16.4s, v8.4s, v0.s[0] - fmla v20.4s, v9.4s, v0.s[0] - fmla v16.4s, v10.4s, v0.s[1] - fmla v20.4s, v11.4s, v0.s[1] - bne E1LoopL + ld1 {v29.s}[0], [x15], x11 // Input Float + ld1 {v29.s}[1], [x15], x11 + ld1 {v8.4s, v9.4s},[x16] // bias for dequantizing weight. + ld1 {v10.4s, v11.4s}, [x16] - E1LoopLEnd: + subs x12, x12, #2 + // Weight Int8->Int32. + sxtl v30.8h, v0.8b + sxtl2 v31.8h, v0.16b + sxtl v1.4s, v30.4h + sxtl2 v2.4s, v30.8h + sxtl v3.4s, v31.4h + sxtl2 v4.4s, v31.8h + scvtf v1.4s, v1.4s + scvtf v2.4s, v2.4s + scvtf v3.4s, v3.4s + scvtf v4.4s, v4.4s + + // Dequantize Weight. + fmla v8.4s, v1.4s, v24.4s + fmla v9.4s, v2.4s, v25.4s + fmla v10.4s, v3.4s, v24.4s + fmla v11.4s, v4.4s, v25.4s + + fmla v27.4s, v8.4s, v29.s[0] + fmla v28.4s, v9.4s, v29.s[0] + fmla v27.4s, v10.4s, v29.s[1] + fmla v28.4s, v11.4s, v29.s[1] + + bne E1LoopH8L + + E1LoopH8LEnd: add x13, x13, x19 + add x16, x16, #32 sub x8, x8, #2 cmp x8, #2 cbz x5, StoreLH1x8 - AddBiasLH1x8: - ld1 {v0.4s, v1.4s}, [x20], #32 - - fmla v16.4s, v0.4s, v5.s[1] - fmla v20.4s, v1.4s, v5.s[1] PostTreatLH1x8: - fmax v16.4s, v16.4s, v6.4s - fmax v20.4s, v20.4s, v6.4s - fmin v16.4s, v16.4s, v7.4s - fmin v20.4s, v20.4s, v7.4s + fmax v27.4s, v27.4s, v6.4s + fmax v28.4s, v28.4s, v6.4s + fmin v27.4s, v27.4s, v7.4s + fmin v28.4s, v28.4s, v7.4s StoreLH1x8: - st1 {v16.4s}, [x0], x7 - st1 {v20.4s}, [x0], x7 + st1 {v27.4s}, [x0], x7 + st1 {v28.4s}, [x0], x7 bge E1LoopH8 @@ -979,6 +1036,7 @@ LoopE1: fmax v16.4s, v16.4s, v6.4s fmin v16.4s, v16.4s, v7.4s + StoreLH1x4: st1 {v16.4s}, [x0] @@ -991,12 +1049,14 @@ LoopE1: End: -ldr x19, [sp, #0] -ldr x20, [sp, #8] -ldr x21, [sp, #16] -ldr x22, [sp, #24] -ldr x23, [sp, #32] -add sp, sp, #64 +ldp x19, x20, [sp, #(16 * 7)] +ldp x21, x22, [sp, #(16 * 6)] +ldp x23, x24, [sp, #(16 * 5)] +ldp x25, x26, [sp, #(16 * 4)] +ldp d8, d9, [sp, #48] +ldp d10, d11, [sp, #32] +ldp d12, d13, [sp, #16] +ldp d14, d15, [sp], #(16 * 8) ret diff --git a/source/backend/cpu/bf16/BF16Functions.cpp b/source/backend/cpu/bf16/BF16Functions.cpp index e62494bef..3f792a3ce 100644 --- a/source/backend/cpu/bf16/BF16Functions.cpp +++ b/source/backend/cpu/bf16/BF16Functions.cpp @@ -865,6 +865,7 @@ bool BF16Functions::init() { gInstance->MNNComputeMatMulForE_1 = _MNNComputeMatMulForE_1_BF16; gInstance->MNNPoolingAvg = (decltype(gInstance->MNNPoolingAvg))(poolingAvg); gInstance->MNNPoolingMax = (decltype(gInstance->MNNPoolingMax))(poolingMax); + gInstance->MNNPoolingMaxWithRedice = (decltype(gInstance->MNNPoolingMaxWithRedice))(poolingMaxWithRedice); #if defined(MNN_USE_SSE) gInstance->MNNPackForMatMul_B = _SSE_MNNPackForMatMul_B_BF16; diff --git a/source/backend/cpu/compute/CommonOptFunction.cpp b/source/backend/cpu/compute/CommonOptFunction.cpp index 44cb5b8e9..23a5f2b55 100644 --- a/source/backend/cpu/compute/CommonOptFunction.cpp +++ b/source/backend/cpu/compute/CommonOptFunction.cpp @@ -173,6 +173,262 @@ void MNNUnpackC2Common(T* dst, const T* src, size_t area, size_t depth, int* are } } +void MNN4BitcopyWithStride (uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { + auto src = (uint32_t*)srcO; + auto dst = (uint32_t*)dstO; + for (int i = 0; i < size; ++i) { + dst[0] = *src; + dst += ds; + src += stride; + } +} + +void MNN4BitcopyFast (uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { + // ds=1, stride=0||1 + auto src = (float*)srcO; + auto dst = (float*)dstO; + int cnt = size; + if (stride == 1) { // stride=1 +#ifdef MNN_USE_NEON + for (; cnt >= 8; cnt -= 8) { + auto v4 = vld1q_f32(src); + auto u4 = vld1q_f32(src + 4); + vst1q_f32(dst, v4); + vst1q_f32(dst + 4, u4); + dst += 8; + src += 8; + } + for (; cnt >= 4; cnt -= 4) { + auto v4 = vld1q_f32(src); + vst1q_f32(dst, v4); + dst += 4; + src += 4; + } +#elif defined(MNN_USE_SSE) + for (; cnt >= 8; cnt -= 8) { + __m128 v4 = _mm_loadu_ps(src); + __m128 u4 = _mm_loadu_ps(src + 4); + _mm_storeu_ps(dst, v4); + _mm_storeu_ps(dst + 4, u4); + dst += 8; + src += 8; + } + for (; cnt >= 4; cnt -= 4) { + __m128 v4 = _mm_loadu_ps(src); + _mm_storeu_ps(dst, v4); + dst += 4; + src += 4; + } +#endif + } else { // stride=0 + int i = 0; + float val = *src; +#ifdef MNN_USE_NEON + auto val4 = vdupq_n_f32(val); + for (; cnt >= 8; cnt -= 8) { + vst1q_f32(dst, val4); + vst1q_f32(dst + 4, val4); + dst += 8; + } + for (; cnt >= 4; cnt -= 4) { + vst1q_f32(dst, val4); + dst += 4; + } +#elif defined(MNN_USE_SSE) + __m128 val4 = _mm_set_ps(val, val, val, val); + for (; cnt >= 8; cnt -= 8) { + _mm_storeu_ps(dst, val4); + _mm_storeu_ps((dst + 4), val4); + dst += 8; + } + for (; cnt >= 4; cnt -= 4) { + _mm_storeu_ps(dst, val4); + dst += 4; + } +#endif + } + for (; cnt > 0; --cnt) { + dst[0] = *src; + dst += ds; + src += stride; + } +} + +void MNN2BitcopyWithStride(uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { + auto src = (uint16_t*)srcO; + auto dst = (uint16_t*)dstO; + for (int i=0; i= 8; cnt-=8) { + auto val8 = vld1q_u16(src); + vst1q_u16(dst, val8); + src += 8; + dst += 8; + } + for (; cnt >= 4; cnt-=4) { + auto val4 = vld1_u16(src); + vst1_u16(dst, val4); + src += 4; + dst += 4; + } +#elif defined(MNN_USE_SSE) + for (; cnt >= 8; cnt-=8) { + auto tmp = _mm_loadu_ps((float*)src); + _mm_storeu_ps((float*)dst, tmp); + src += 8; + dst += 8; + } +#endif + } else { // stride=0 +#ifdef MNN_USE_NEON + auto val4 = vdup_n_u16(val); + auto val8 = vdupq_n_u16(val); + for (; cnt >= 8; cnt-=8) { + vst1q_u16(dst, val8); + dst += 8; + } + for (; cnt >= 4; cnt-=4) { + vst1_u16(dst, val4); + dst += 4; + } +#elif defined(MNN_USE_SSE) + uint16_t arr[8] = {val, val, val, val, val, val, val, val}; + auto val8 = _mm_loadu_ps((float*)arr); + for (; cnt >= 8; cnt-=8) { + _mm_storeu_ps((float*)dst, val8); + dst += 8; + } +#endif + } + for (; cnt > 0; --cnt) { + *dst = *src; + src += stride; + dst += ds; + } +} + +void MNN1BitcopyWithStride (uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { + for (int i = 0; i < size; ++i) { + dstO[0] = *srcO; + dstO += ds; + srcO += stride; + } + +} + +void MNN1BitCopyFast (uint8_t* dstO, const uint8_t* srcO, int size, int stride, int ds) { + int cnt = size; + uint8_t val = *srcO; + if (stride == 1) { +#ifdef MNN_USE_SSE + for (; cnt >= 16; cnt-=16) { + auto tmp = _mm_loadu_ps((float*)srcO); + _mm_storeu_ps((float*)dstO, tmp); + srcO += 16; + dstO += 16; + } +#elif defined(MNN_USE_NEON) + for (; cnt >= 16; cnt-=16) { + auto val16 = vld1q_u8(srcO); + vst1q_u8(dstO, val16); + srcO += 16; + dstO += 16; + } + for (; cnt >= 8; cnt-=8) { + auto val8 = vld1_u8(srcO); + vst1_u8(dstO, val8); + srcO += 8; + dstO += 8; + } +#endif + } else { // stride=0 +#ifdef MNN_USE_SSE + std::vector arr(16, val); + auto val16 = _mm_loadu_ps((float*)arr.data()); + + for (; cnt >= 16; cnt-=16) { + _mm_storeu_ps((float*)dstO, val16); + dstO += 16; + } +#elif defined(MNN_USE_NEON) + auto val16 = vdupq_n_u8(val); + auto val8 = vdup_n_u8(val); + for (; cnt >= 16; cnt-=16) { + vst1q_u8(dstO, val16); + dstO += 16; + } + for (; cnt >= 8; cnt-=8) { + vst1_u8(dstO, val8); + dstO += 8; + } +#endif + } + for (; cnt > 0; --cnt) { + dstO[0] = *srcO; + dstO += ds; + srcO += stride; + } +} + +void MNNAccumulateSequenceNumber (float* dst, const float* src, int size) { + // mode: 0:Add, 1:Sub, 2:Min, 3:Max + int size8 = (size / 8) * 8; + int i = 0; + float sum = 0.f; + float tmp[4]; +#ifdef MNN_USE_NEON + if (size >= 8) { + auto sum4_1 = vdupq_n_f32(0.f); + auto sum4_2 = vdupq_n_f32(0.f); + for (; i < size8; i += 8) { + auto v4 = vld1q_f32(src); + auto u4 = vld1q_f32(src + 4); + sum4_1 = vaddq_f32(sum4_1, v4); + sum4_2 = vaddq_f32(sum4_2, u4); + src += 8; + } + sum4_1 = vaddq_f32(sum4_1, sum4_2); + for (int j = 0;j < 4; ++j) { + sum += sum4_1[j]; + } + } +#elif defined(MNN_USE_SSE) + if (size >= 8) { + auto sum4_1 = _mm_set_ps1(0.f); + auto sum4_2 = _mm_set_ps1(0.f); + + for (; i < size8; i += 8) { + auto v4 = _mm_loadu_ps(src); + auto u4 = _mm_loadu_ps(src + 4); + sum4_1 = _mm_add_ps(sum4_1, v4); + sum4_2 = _mm_add_ps(sum4_2, u4); + src += 8; + } + + sum4_1 = _mm_add_ps(sum4_1, sum4_2); + _mm_storeu_ps(tmp, sum4_1); + sum += (tmp[0] + tmp[1] + tmp[2] + tmp[3]); + } +#endif + for (; i < size; ++i) { + sum += (*src); + src += 1; + } + *dst = sum; +} + #ifndef MNN_USE_NEON void MNNGetMatMulPackMode(int* eP, int *lP, int* hP) { @@ -2309,7 +2565,7 @@ void MNNReluWithSlopeCommon(float* dst, const float* src, size_t size, float slo } void MNNHardSwishCommon(float* dst, const float* src, size_t size) { - int sizeQuad = size / 4; + int sizeQuad = static_cast(size / 4); int start = 0; #ifdef MNN_USE_SSE if (sizeQuad > 0) { @@ -2347,7 +2603,7 @@ void MNNGeluStandardCommon(float* dst, const float* src, size_t size) { } void MNNGeluCommon(float* dst, const float* src, size_t size) { - int sizeQuad = size / 8; + int sizeQuad = static_cast(size / 8); int start = 0; #if defined(MNN_USE_SSE) || defined(MNN_USE_NEON) if (sizeQuad > 0) { @@ -2989,10 +3245,13 @@ void MNNCoreFunctionInit() { gCoreFunction->MNNDeconvRunForUnitDepthWise = MNNDeconvRunForUnitDepthWise; gCoreFunction->MNNSelectBinaryFunctionForFloat = CPUBinary::selectForFloat; gCoreFunction->MNNSelectUnaryFunctionForFloat = CPUUnary::selectForFloat; + gCoreFunction->MNNSelectUnaryFunctionForInt8 = CPUUnary::selectForInt8; gCoreFunction->MNNReluWithSlopeChannel = MNNReluWithSlopeChannel; gCoreFunction->MNNPoolingAvg = (decltype(gCoreFunction->MNNPoolingAvg))(poolingAvg); // Set min value as 1 << 24 gCoreFunction->MNNPoolingMax = (decltype(gCoreFunction->MNNPoolingMax))(poolingMax); + + gCoreFunction->MNNPoolingMaxWithRedice = (decltype(gCoreFunction->MNNPoolingMaxWithRedice))(poolingMaxWithRedice); // ImageProcess Functions gCoreFunction->MNNRGBAToBGRA = MNNRGBAToBGRA; gCoreFunction->MNNNV21ToRGBA = MNNNV21ToRGBA; @@ -3005,6 +3264,15 @@ void MNNCoreFunctionInit() { gCoreFunction->MNNSamplerC4Nearest = MNNSamplerC4Nearest; gCoreFunction->MNNSamplerC4Bilinear = MNNSamplerC4Bilinear; + gCoreFunction->MNN4BitcopyWithStride = MNN4BitcopyWithStride; + gCoreFunction->MNN1BitcopyWithStride = MNN1BitcopyWithStride; + gCoreFunction->MNN2BitcopyWithStride = MNN2BitcopyWithStride; + gCoreFunction->MNN4BitcopyFast = MNN4BitcopyFast; + gCoreFunction->MNN2BitcopyFast = MNN2BitcopyFast; + gCoreFunction->MNN1BitcopyFast = MNN1BitCopyFast; + + gCoreFunction->MNNAccumulateSequenceNumber = MNNAccumulateSequenceNumber; + cpuinfo_arm_isa gCPUInfo; cpuinfo_arm_init(&gCPUInfo); gCoreFunction->supportFp16arith = gCPUInfo.fp16arith; diff --git a/source/backend/cpu/compute/CommonOptFunction.h b/source/backend/cpu/compute/CommonOptFunction.h index a06bdfb77..6bc0ebad9 100644 --- a/source/backend/cpu/compute/CommonOptFunction.h +++ b/source/backend/cpu/compute/CommonOptFunction.h @@ -175,6 +175,7 @@ void MNNRoiAlignAvg(float* dst, const float* src, const std::vector #include "core/BufferAllocator.hpp" #include "backend/cpu/CPUBackend.hpp" @@ -18,20 +19,42 @@ namespace MNN { Convolution1x1Strassen::Convolution1x1Strassen(const Convolution2DCommon *common, Backend *b, const float *originWeight, - size_t originWeightSize, const float *bias, size_t biasSize) + size_t originWeightSize, const float *bias, size_t biasSize, std::shared_ptr quantInfo) : CPUConvolution(common, b) { auto outputCount = (int)biasSize; - auto mSrcCount = (int)originWeightSize / outputCount; + int ePack, lPack, hPack; + auto core = static_cast(b)->functions(); + core->MNNGetMatMulPackMode(&ePack, &lPack, &hPack); mResource.reset(new CPUConvolution::Resource); mResource->backend = b; - if (!mResource->copyBiasAlign(bias, biasSize)) { + auto mSrcCount = (int)originWeightSize / outputCount; + if (!mResource->copyBiasAlign(bias, (int)biasSize)) { MNN_ERROR("Not Enough Memory\n"); mValid = false; return; } - auto core = static_cast(b)->functions(); - int ePack, lPack, hPack; - core->MNNGetMatMulPackMode(&ePack, &lPack, &hPack); +#ifdef MNN_LOW_MEMORY + if (originWeightSize == 0 || nullptr == originWeight) { // Use Int8 Weight. + MNN_ASSERT(nullptr != quantInfo.get()); + + originWeightSize = quantInfo->weight.size(); + int lSize = (int)originWeightSize / (int)biasSize * common->kernelX() * common->kernelY(); + auto hU = UP_DIV(outputCount, hPack); + auto lU = UP_DIV(lSize, lPack); + mSrcCount = (int)originWeightSize / outputCount; + + mResource->mWeight.reset(Tensor::createDevice(std::vector{UP_DIV(outputCount, hPack), UP_DIV(mSrcCount, lPack) * lPack, hPack})); + mValid = b->onAcquireBuffer(mResource->mWeight.get(), Backend::STATIC); + if (!mValid) { + MNN_ERROR("Not Enough Memory\n"); + return; + } + + DenseConvolutionTiledExecutor::initQuantizeResource(quantInfo, mResource, hU, hPack, lU, lPack, outputCount, (int)originWeightSize / (int)biasSize, common->kernelX() * common->kernelY(), core->bytes); + return; + } +#endif + // Use Float Weight. mResource->mWeight.reset(Tensor::createDevice(std::vector{UP_DIV(outputCount, hPack), UP_DIV(mSrcCount, lPack) * lPack, hPack})); mValid = b->onAcquireBuffer(mResource->mWeight.get(), Backend::STATIC); if (!mValid) { @@ -104,6 +127,16 @@ ErrorCode Convolution1x1Strassen::onResize(const std::vector &inputs, int maxDepth = 5; auto icAlign = UP_DIV(ic, lPack) * lPack; auto weightTensor = mResource->mWeight.get(); + uint8_t* dequantAlpha = nullptr; + uint8_t* dequantBias = nullptr; + int dequantBits = 32; +#ifdef MNN_LOW_MEMORY + if (mResource && mResource->mDequantize.bits <= 8) { + dequantAlpha = mResource->mDequantize.mScaleBias->host(); + dequantBias = dequantAlpha + mResource->hU * mResource->hP * bytes; + dequantBits = mResource->mDequantize.bits; + } +#endif if (matrixSizeE > CONVOLUTION_TILED_NUMBER * 8 * numberThread && matrixSizeE > ocC4) { // Divide in plane, in this case the divide equal numberThread int divideStep = UP_DIV(matrixSizeE, numberThread); @@ -121,7 +154,7 @@ ErrorCode Convolution1x1Strassen::onResize(const std::vector &inputs, unit.offset[2] = 0; unit.offset[0] = core->pack * planeStart * bytes; unit.offset[3] = core->pack * planeStart * bytes; - unit.mStracssenComputor.reset(new StrassenMatrixComputor(backend(), false, maxDepth)); + unit.mStracssenComputor.reset(new StrassenMatrixComputor(backend(), false, maxDepth, dequantAlpha, dequantBias, dequantBits)); int e = planeSize; int l = ic; int h = oc; @@ -165,7 +198,7 @@ ErrorCode Convolution1x1Strassen::onResize(const std::vector &inputs, unit.offset[0] = 0; unit.offset[3] = core->pack * matrixSizeE * ocStart * bytes; - unit.mStracssenComputor.reset(new StrassenMatrixComputor(backend(), false, maxDepth)); + unit.mStracssenComputor.reset(new StrassenMatrixComputor(backend(), false, maxDepth, dequantAlpha, dequantBias, dequantBits)); int e = matrixSizeE; int l = ic; int h = std::min(ocSize * core->pack, ocWeightSize * hPack); diff --git a/source/backend/cpu/compute/Convolution1x1Strassen.hpp b/source/backend/cpu/compute/Convolution1x1Strassen.hpp index 1d5bcb918..b2c828ac7 100644 --- a/source/backend/cpu/compute/Convolution1x1Strassen.hpp +++ b/source/backend/cpu/compute/Convolution1x1Strassen.hpp @@ -16,7 +16,7 @@ namespace MNN { class Convolution1x1Strassen : public CPUConvolution { public: Convolution1x1Strassen(const Convolution2DCommon *common, Backend *b, const float *originWeight, - size_t originWeightSize, const float *bias, size_t biasSize); + size_t originWeightSize, const float *bias, size_t biasSize, std::shared_ptr); Convolution1x1Strassen(std::shared_ptr resource, const Convolution2DCommon *common, Backend* b); virtual ~Convolution1x1Strassen(); diff --git a/source/backend/cpu/compute/ConvolutionFloatFactory.cpp b/source/backend/cpu/compute/ConvolutionFloatFactory.cpp index 8fe542259..1dc35b25f 100644 --- a/source/backend/cpu/compute/ConvolutionFloatFactory.cpp +++ b/source/backend/cpu/compute/ConvolutionFloatFactory.cpp @@ -25,8 +25,7 @@ namespace MNN { static Execution* _createUnit(const Tensor* input, const Tensor* output, Backend* backend, - const Convolution2D* conv2d, const float* originWeight, size_t originWeightSize, - const float* bias, size_t biasSize, std::shared_ptr weightQuantInfo, bool supportSparse) { + const Convolution2D* conv2d, const float* originWeight, size_t originWeightSize, const float* bias, size_t biasSize, std::shared_ptr weightQuantInfo, bool supportSparse) { auto cpuBackend = (CPUBackend*)backend; #ifdef MNN_LOW_MEMORY bool lowMemory = cpuBackend->memoryMode() == BackendConfig::Memory_Low; @@ -46,14 +45,14 @@ static Execution* _createUnit(const Tensor* input, const Tensor* output, Backend } } #endif - if (lowMemory || originWeightSize == 0) { - return new DenseConvolutionTiledExecutor(common, backend, originWeight, originWeightSize, bias, biasSize, weightQuantInfo); - } bool fastWay = common->kernelY() == 1 && common->kernelX() == 1 && output->width() == input->width() && output->height() == input->height() && common->strideX() == 1 && common->strideY() == 1; if (fastWay) { - return new Convolution1x1Strassen(common, backend, originWeight, originWeightSize, bias, biasSize); + return new Convolution1x1Strassen(common, backend, originWeight, originWeightSize, bias, biasSize, weightQuantInfo); + } + if (originWeightSize == 0) { + return new DenseConvolutionTiledExecutor(common, backend, originWeight, originWeightSize, bias, biasSize, weightQuantInfo); } if (!ConvolutionWinogradBridge::canUseWinograd(common)) { return new DenseConvolutionTiledExecutor(common, backend, originWeight, originWeightSize, bias, biasSize, nullptr); diff --git a/source/backend/cpu/compute/DenseConvolutionTiledExecutor.cpp b/source/backend/cpu/compute/DenseConvolutionTiledExecutor.cpp index 615d33ddb..7a5655063 100644 --- a/source/backend/cpu/compute/DenseConvolutionTiledExecutor.cpp +++ b/source/backend/cpu/compute/DenseConvolutionTiledExecutor.cpp @@ -27,7 +27,7 @@ void DenseConvolutionTiledExecutor::initWeight(float *dest, const float *source, function->MNNPackForMatMul_B(dest, cache, outputCount, kernelSize * depth, true); } -static bool _initQuantizeResource(std::shared_ptr int8Info, std::shared_ptr resource, int hU, int hP, int lU, int lP, int outputCount, int srcChannel, int kernelSize, int bytes) { +bool DenseConvolutionTiledExecutor::initQuantizeResource(std::shared_ptr int8Info, std::shared_ptr resource, int hU, int hP, int lU, int lP, int outputCount, int srcChannel, int kernelSize, int bytes) { int weightLength = hU * lU * hP * lP; resource->mWeight.reset(Tensor::createDevice( {weightLength})); @@ -40,8 +40,9 @@ static bool _initQuantizeResource(std::shared_ptr resource->hU = hU; resource->lP = lP; resource->hP = hP; - // Reorder weight MNN_ASSERT(lP == 1); + // Reorder weight + auto dstWInt8 = resource->mWeight->host(); auto srcWInt8 = int8Info->weight.get(); for (int y=0; y return true; } +void DenseConvolutionTiledExecutor::selectLowMemoryMatmulFunc(lowMemoryMatmulUnit* matmulUnit, lowMemoryMatmulRemain* matmulRemain, float* weightBytes, int32_t weightQuantBits, const CoreFunctions* core) { + if (weightQuantBits == 8) { + *matmulUnit = core->MNNPackedMatMul_int8; + *matmulRemain = core->MNNPackedMatMulRemain_int8; + *weightBytes = 1; + } + if (weightQuantBits == 4) { + *matmulUnit = core->MNNPackedMatMul_int4; + *matmulRemain = core->MNNPackedMatMulRemain_int4; + *weightBytes = 0.5; + } +} + DenseConvolutionTiledExecutor::DenseConvolutionTiledExecutor(const Convolution2DCommon* common, Backend* b, const float* originWeight, size_t originWeightSize, const float* bias, size_t biasSize, std::shared_ptr int8Info) @@ -139,7 +153,7 @@ DenseConvolutionTiledExecutor::DenseConvolutionTiledExecutor(const Convolution2D auto lU = UP_DIV(lSize, lP); if (useInt8Weight) { // Quantize weight to int8 - auto allocSuccess = _initQuantizeResource(int8Info, mResource, hU, hP, lU, lP, outputCount, srcCount, common->kernelX() * common->kernelY(), bytes); + auto allocSuccess = DenseConvolutionTiledExecutor::initQuantizeResource(int8Info, mResource, hU, hP, lU, lP, outputCount, srcCount, common->kernelX() * common->kernelY(), bytes); if (!allocSuccess) { mValid = false; return; @@ -414,16 +428,7 @@ ErrorCode DenseConvolutionTiledImpl::onResize(const std::vector& inputs const uint8_t* dequantBias = nullptr; #ifdef MNN_LOW_MEMORY if (mResource && mResource->mDequantize.bits <= 8) { - if (mResource->mDequantize.bits == 8) { - matmulUnit = core->MNNPackedMatMul_int8; - matmulRemain = core->MNNPackedMatMulRemain_int8; - weightBytes = 1; - } - if (mResource->mDequantize.bits == 4) { - matmulUnit = core->MNNPackedMatMul_int4; - matmulRemain = core->MNNPackedMatMulRemain_int4; - weightBytes = 0.5; - } + DenseConvolutionTiledExecutor::selectLowMemoryMatmulFunc(&matmulUnit, &matmulRemain, &weightBytes, mResource->mDequantize.bits, core); dequantAlpha = mResource->mDequantize.mScaleBias->host(); dequantBias = dequantAlpha + mResource->hU * mResource->hP * bytes; } diff --git a/source/backend/cpu/compute/DenseConvolutionTiledExecutor.hpp b/source/backend/cpu/compute/DenseConvolutionTiledExecutor.hpp index 1141473a3..910976944 100644 --- a/source/backend/cpu/compute/DenseConvolutionTiledExecutor.hpp +++ b/source/backend/cpu/compute/DenseConvolutionTiledExecutor.hpp @@ -15,6 +15,8 @@ #include "ConvolutionTiledExecutor.hpp" // Tiled Slide Window or Im2Col + GEMM namespace MNN { +typedef void(*lowMemoryMatmulUnit)(float* C, const float* A, const float* B, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); +typedef void(*lowMemoryMatmulRemain)(float* C, const float* A, const float* B, size_t eSize, const size_t* parameter, const float* postParameters, const float* bias, const float* k, const float* b); class DenseConvolutionTiledImpl : public ConvolutionTiledImpl { public: DenseConvolutionTiledImpl(const Convolution2DCommon *common, Backend *b, CPUConvolution::Resource* resource = nullptr) : ConvolutionTiledImpl(common, b) { @@ -47,6 +49,8 @@ class DenseConvolutionTiledExecutor : public ConvolutionTiledExecutor { const Tensor *outputTensor, int threadNumber, Backend* b) { return DenseConvolutionTiledImpl::bestTileConvolutionConfig(common, inputTensor, outputTensor, threadNumber, b); } + static bool initQuantizeResource(std::shared_ptr int8Info, std::shared_ptr resource, int hU, int hP, int lU, int lP, int outputCount, int srcChannel, int kernelSize, int bytes); + static void selectLowMemoryMatmulFunc(lowMemoryMatmulUnit* matmulUnit, lowMemoryMatmulRemain* matmulRemain, float* weightBytes, int32_t weightQuantBits, const CoreFunctions* core); struct DequantizeCache { std::shared_ptr weight; std::shared_ptr weightInt8; @@ -72,7 +76,6 @@ class ConvolutionTiledExecutorMultiInput : public Execution { std::shared_ptr mProxy; std::vector mInputs; }; - } // namespace MNN #endif /* DenseConvolutionTiledExecutor_hpp */ diff --git a/source/backend/cpu/compute/Int8FunctionsOpt.cpp b/source/backend/cpu/compute/Int8FunctionsOpt.cpp index d525cdde4..b8ba0bd1d 100644 --- a/source/backend/cpu/compute/Int8FunctionsOpt.cpp +++ b/source/backend/cpu/compute/Int8FunctionsOpt.cpp @@ -27,7 +27,7 @@ void MNNLineDepthWiseInt8AddBiasScaleUnit(int8_t* dst, const int8_t* src, const void MNNMaxPoolInt8(int8_t* dst, int8_t* src, size_t outputWidth, size_t inputWidth, size_t kernelx, size_t kernely, size_t stridesx); void MNNAvgPoolInt8(int8_t* dst, int8_t* src, size_t outputWidth, size_t inputWidth, size_t kernelx, size_t kernely, size_t stridesx, ssize_t paddingx, ssize_t factor); - +void MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params); #if defined(__aarch64__) // aarch32 sdot workaround void MNNGemmInt8AddBiasScale_ARMV82_Unit(int8_t* dst, const int8_t* src, const int8_t* weight, size_t src_depth_quad, size_t dst_step, size_t dst_depth_quad, const QuanPostTreatParameters* post, size_t realDstCount); @@ -1472,6 +1472,29 @@ static void MNNGemmInt8AddBiasScale_16x4_Unit(int8_t* dst, const int8_t* src, co } } +static void MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params) { + float mulVal = 0.f; + float inputScale = params->inputScale[0]; + float outputScale = params->outputScale[0]; + int32_t inputZero = static_cast(params->inputZeroPoint[0]); + int32_t outputZero = static_cast(params->outputZeroPoint[0]); + for (int j = 0;j < depthQuad; ++j) { + const float* slopeZ = slope + 4 * j; + const int8_t* srcZ = src + 4 * j * planeNumber; + int8_t* dstZ = dst + 4 * j * planeNumber; + for (int i = 0; i < planeNumber; ++i) { + for (int c = 0; c < 4; ++c) { + if (srcZ[4 * i + c] < 0) { + mulVal = (srcZ[4 * i + c] - inputZero) * slopeZ[c]; + dstZ[4 * i + c] = ALIMIN(ALIMAX(static_cast(roundf(mulVal)) + outputZero, params->minValue), params->maxValue); + } else { + dstZ[4 * i + c] = srcZ[4 * i + c]; + } + } + } + } +} + static void MNNGemmInt8AddBiasScale_16x4_Unit_FAST(int8_t* dst, const int8_t* src, const int8_t* weight, size_t src_depth_quad, size_t dst_step, size_t dst_depth_quad, const QuanPostTreatParameters* post, size_t realCount) { return MNNGemmInt8AddBiasScale_16x4_Unit(dst, src, weight, src_depth_quad, dst_step, dst_depth_quad, post, realCount); } @@ -2109,6 +2132,9 @@ void MNNCoreInt8FunctionInit() { // Norm gCoreFunc->MNNNormInt8 = MNNNormInt8; + // ReluWithSlopeChannel + gCoreFunc->MNNReluWithSlopeChannelInt8 = MNNReluWithSlopeChannelInt8; + #if defined(__aarch64__) auto core = MNNGetCoreFunctions(); if (core->supportSDot) { diff --git a/source/backend/cpu/compute/Int8FunctionsOpt.h b/source/backend/cpu/compute/Int8FunctionsOpt.h index 0274f1ed2..609f5826a 100644 --- a/source/backend/cpu/compute/Int8FunctionsOpt.h +++ b/source/backend/cpu/compute/Int8FunctionsOpt.h @@ -46,7 +46,6 @@ struct QuanPostTreatParameters { float roundValueNeg = -0.5f; }; - struct QuanPrePostParameters{ float* inputScale; float* outputScale; @@ -101,11 +100,13 @@ struct CoreInt8Functions { // Pooling void (*MNNMaxPoolInt8)(int8_t* dst, int8_t* src, size_t outputWidth, size_t inputWidth, size_t kernelx, size_t kernely, size_t stridesx); - void (*MNNAvgPoolInt8)(int8_t* dst, int8_t* src, size_t outputWidth, size_t inputWidth, size_t kernelx, size_t kernely, size_t stridesx, ssize_t paddingx, ssize_t factor); // Norm void (*MNNNormInt8)(int8_t* dst, const int8_t* src, const float* gamma, const float* beta, float epsilon, size_t size, QuanPrePostParameters* params); + + // Relu + void (*MNNReluWithSlopeChannelInt8)(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params); }; void MNNCoreInt8FunctionInit(); CoreInt8Functions* MNNGetInt8CoreFunctions(); diff --git a/source/backend/cpu/compute/StrassenMatmulComputor.cpp b/source/backend/cpu/compute/StrassenMatmulComputor.cpp index 1c72343c7..028b28f52 100644 --- a/source/backend/cpu/compute/StrassenMatmulComputor.cpp +++ b/source/backend/cpu/compute/StrassenMatmulComputor.cpp @@ -7,6 +7,7 @@ // #include "StrassenMatmulComputor.hpp" +#include "DenseConvolutionTiledExecutor.hpp" #include "CommonOptFunction.h" #include "backend/cpu/CPUBackend.hpp" #include @@ -41,9 +42,12 @@ class AutoMemory { BufferAllocator* mAllocator; }; -StrassenMatrixComputor::StrassenMatrixComputor(Backend* bn, bool multithread, int maxDepth) : mBackend(bn) { +StrassenMatrixComputor::StrassenMatrixComputor(Backend* bn, bool multithread, int maxDepth, uint8_t* dequantAlpha, uint8_t* dequantBias, int32_t dequantBits) : mBackend(bn) { mMaxDepth = maxDepth; mSupportMultiThread = multithread; + mDequantBias = dequantBias; + mDequantAlpha = dequantAlpha; + mDequantBits = dequantBits; }; StrassenMatrixComputor::~StrassenMatrixComputor() { // Do nothing @@ -70,8 +74,20 @@ ErrorCode StrassenMatrixComputor::_generateTrivalMatMul(int e, int l, int h, con int unitNumber = e / eP; int xCount = e - unitNumber * eP; auto eReal = aStride / core->bytes / core->pack; + auto matmulUnit = core->MNNPackedMatMul; + auto matmulRemain = core->MNNPackedMatMulRemain; + const float* dequantAlpha = nullptr; + const float* dequantBias = nullptr; + float weightBytes = 1; +#ifdef MNN_LOW_MEMORY + if (nullptr != mDequantAlpha && nullptr != mDequantBias) { + dequantAlpha = reinterpret_cast(mDequantAlpha); + dequantBias = reinterpret_cast(mDequantBias); + DenseConvolutionTiledExecutor::selectLowMemoryMatmulFunc(&matmulUnit, &matmulRemain, &weightBytes, mDequantBits, core); + } +#endif mFunctions.emplace_back( - std::make_pair([cStride, l, h, xCount, AT, BT, CT, COT, tileBufferBasic, unitNumber, bExtraStride, numberThread, eReal, eP, active, this](int tId) { + std::make_pair([cStride, l, h, xCount, AT, BT, CT, COT, tileBufferBasic, unitNumber, bExtraStride, numberThread, eReal, eP, active, matmulUnit, matmulRemain, dequantAlpha, dequantBias, this](int tId) { auto core = static_cast(backend())->functions(); size_t parameters[6]; parameters[0] = xCount * core->bytes; @@ -96,7 +112,7 @@ ErrorCode StrassenMatrixComputor::_generateTrivalMatMul(int e, int l, int h, con int32_t info[4]; int32_t stride[4]; stride[0] = eP; - stride[1] = parameters[1]; + stride[1] = (int32_t)parameters[1]; stride[2] = 0; stride[3] = 0; info[0] = 1; @@ -107,21 +123,21 @@ ErrorCode StrassenMatrixComputor::_generateTrivalMatMul(int e, int l, int h, con int xStart = i * eP; auto aStart = aHost + xStart * packUnit; core->MNNPackC4ForMatMul_A((float*)(tileHost), (const float**)(&aStart), info, stride); - core->MNNPackedMatMul((float*)(cHost + xStart * packUnit), (float*)tileHost, (float*)bHost, parameters, postParametersPtr, (const float*)biasPtr, nullptr, nullptr); + matmulUnit((float*)(cHost + xStart * packUnit), (float*)tileHost, (float*)bHost, parameters, postParametersPtr, (const float*)biasPtr, dequantAlpha, dequantBias); } if (tId != numberThread -1) { return; } if (xCount > 0) { stride[0] = xCount; - stride[1] = parameters[1]; + stride[1] = (int32_t)parameters[1]; info[2] = xCount; int xStart = unitNumber * eP; auto aStart = aHost + xStart * packUnit; // Copy core->MNNPackC4ForMatMul_A((float*)(tileHost), (const float**)(&aStart), info, stride); - core->MNNPackedMatMulRemain((float*)(cHost + xStart * packUnit), (float*)tileHost, (float*)bHost, xCount, parameters, postParametersPtr, (const float*)biasPtr, nullptr, nullptr); + matmulRemain((float*)(cHost + xStart * packUnit), (float*)tileHost, (float*)bHost, xCount, parameters, postParametersPtr, (const float*)biasPtr, dequantAlpha, dequantBias); } }, numberThread)); static_cast(backend())->getBufferAllocator()->free(tileBufferBasic); diff --git a/source/backend/cpu/compute/StrassenMatmulComputor.hpp b/source/backend/cpu/compute/StrassenMatmulComputor.hpp index 4fa690dcb..64fd45138 100644 --- a/source/backend/cpu/compute/StrassenMatmulComputor.hpp +++ b/source/backend/cpu/compute/StrassenMatmulComputor.hpp @@ -21,7 +21,7 @@ namespace MNN { */ class StrassenMatrixComputor { public: - StrassenMatrixComputor(Backend* bn, bool multithread, int maxDepth); + StrassenMatrixComputor(Backend* bn, bool multithread, int maxDepth, uint8_t* dequantAlpha=nullptr, uint8_t* dequantBias=nullptr, int32_t dequantBits=32); virtual ~StrassenMatrixComputor(); /* @@ -82,6 +82,10 @@ class StrassenMatrixComputor { Backend* mBackend; std::vector mStack; + + uint8_t* mDequantAlpha = nullptr; + uint8_t* mDequantBias = nullptr; + int32_t mDequantBits; }; } // namespace MNN diff --git a/source/backend/cpu/x86_x64/FunctionDispatcher.cpp b/source/backend/cpu/x86_x64/FunctionDispatcher.cpp index 20fbd8dab..26d59dd6b 100644 --- a/source/backend/cpu/x86_x64/FunctionDispatcher.cpp +++ b/source/backend/cpu/x86_x64/FunctionDispatcher.cpp @@ -125,6 +125,7 @@ void MNNInt8FunctionInit() { core->MNNAvgPoolInt8 = MNNAvgPoolUint8; core->MNNMaxPoolInt8 = MNNMaxPoolInt8_; core->MNNNormInt8 = _SSE_MNNNormInt8; + core->MNNReluWithSlopeChannelInt8 = _SSE_MNNReluWithSlopeChannelInt8; if (cpuFlags & libyuv::kCpuHasSSE41) { core->MNNFloat2Int8 = _SSE_MNNFloat2Int8; core->MNNInt8ScaleToFloat = _SSE_MNNInt8ScaleToFloat; diff --git a/source/backend/cpu/x86_x64/avx/GemmFunction.hpp b/source/backend/cpu/x86_x64/avx/GemmFunction.hpp index 3d45a2ec1..4d5738271 100644 --- a/source/backend/cpu/x86_x64/avx/GemmFunction.hpp +++ b/source/backend/cpu/x86_x64/avx/GemmFunction.hpp @@ -999,7 +999,7 @@ static void _AVX_MNNPackedMatMul_int4_16(TYPE* C, const TYPE* A, const uint8_t* auto hC4 = UP_DIV(h, 4); float ws_tmp[4]; for (int y = 0; y < hC4; ++y) { - auto weight = B + y * bStride; + auto weight = B + y * bStride / 2; auto dst = C + (y / 2) * cStride + 4 * (y % 2); auto alpha = _mm_loadu_ps(k + y * 4); auto bias = _mm_loadu_ps(b + y * 4); diff --git a/source/backend/cpu/x86_x64/avx/MathFunctions.cpp b/source/backend/cpu/x86_x64/avx/MathFunctions.cpp index 58e84b264..816f123c4 100644 --- a/source/backend/cpu/x86_x64/avx/MathFunctions.cpp +++ b/source/backend/cpu/x86_x64/avx/MathFunctions.cpp @@ -23,6 +23,8 @@ void _AVX_MNNGelu(float *dst, const float *src, size_t size, float* parameters) auto var10 = _mm256_set1_ps(0.5); auto varOne = _mm256_set1_ps(1.f); auto varNegOne = _mm256_set1_ps(-1.f); + auto clamp_min = _mm256_set1_ps(-5.0f); + auto clamp_max = _mm256_set1_ps(5.0f); for (int i = 0; i < size; i++) { auto x = _mm256_loadu_ps(src + i * 8); auto y = _mm256_mul_ps(x, x); @@ -30,6 +32,8 @@ void _AVX_MNNGelu(float *dst, const float *src, size_t size, float* parameters) y = _mm256_mul_ps(y, var1); y = _mm256_add_ps(y, x); y = _mm256_mul_ps(y, var2); + y = _mm256_max_ps(y, clamp_min); + y = _mm256_min_ps(y, clamp_max); // y = tanh(y) { auto y2 = _mm256_mul_ps(y, y); diff --git a/source/backend/cpu/x86_x64/avx/PackedFunction.cpp b/source/backend/cpu/x86_x64/avx/PackedFunction.cpp index c66824469..0bfb93cdc 100644 --- a/source/backend/cpu/x86_x64/avx/PackedFunction.cpp +++ b/source/backend/cpu/x86_x64/avx/PackedFunction.cpp @@ -179,7 +179,7 @@ void _AVX_MNNConvRunForLineDepthwise(float* dst, const float* src, const float* } static MNNBinaryExecute _AVX2_MNNSelectBinaryFunctionForFloat(int opType) { - auto vecF = MNN::selectVector(opType); + auto vecF = MNN::selectVector(opType); if (nullptr != vecF) { return vecF; } @@ -856,6 +856,7 @@ void _AVX_ExtraInit(void* functions) { coreFunction->MNNPoolingAvg = (decltype(coreFunction->MNNPoolingAvg))(MNN::poolingAvg); // Set min value as 1 << 24 coreFunction->MNNPoolingMax = (decltype(coreFunction->MNNPoolingMax))(MNN::poolingMax); + coreFunction->MNNPoolingMaxWithRedice = (decltype(coreFunction->MNNPoolingMaxWithRedice))(MNN::poolingMaxWithRedice); coreFunction->MNNSelectBinaryFunctionForFloat = _AVX2_MNNSelectBinaryFunctionForFloat; coreFunction->MNNCopyC4WithStride = _AVX_MNNCopyC4WithStride; coreFunction->MNNAddC4WithStride = _AVX_MNNAddC4WithStride; diff --git a/source/backend/cpu/x86_x64/avx/Vec8.hpp b/source/backend/cpu/x86_x64/avx/Vec8.hpp index 2161bfa4e..501768e69 100644 --- a/source/backend/cpu/x86_x64/avx/Vec8.hpp +++ b/source/backend/cpu/x86_x64/avx/Vec8.hpp @@ -73,6 +73,7 @@ r4 = t4, r5 = t5, r6 = t6, r7 = t7;\ struct Vec8 { using VecType = Vec8; __m256 value; + __m256 one = _mm256_set1_ps(1.0f); VecType operator+(const VecType& lr) const { VecType dst = { _mm256_add_ps(value, lr.value) }; return dst; @@ -102,6 +103,31 @@ struct Vec8 { value = lr.value; return *this; } + VecType operator==(const VecType& lr) const { + __m256 mask = _mm256_cmp_ps(value, lr.value, 0); + VecType dst = { _mm256_and_ps(one, mask) } ; + return dst; + } + VecType operator>(const VecType& lr) { + __m256 mask = _mm256_cmp_ps(lr.value, value, 0x01); + VecType dst = { _mm256_and_ps(one, mask) } ; + return dst; + } + VecType operator>=(const VecType& lr) { + __m256 mask = _mm256_cmp_ps(value, lr.value, 0x0D); + VecType dst = { _mm256_and_ps(one, mask) } ; + return dst; + } + VecType operator<(const VecType& lr) { + __m256 mask = _mm256_cmp_ps(value, lr.value, 0x01); + VecType dst = { _mm256_and_ps(one, mask) } ; + return dst; + } + VecType operator<=(const VecType& lr) { + __m256 mask = _mm256_cmp_ps(value, lr.value, 0x02); + VecType dst = { _mm256_and_ps(one, mask) } ; + return dst; + } VecType operator-() { VecType dst; #if defined(_MSC_VER) diff --git a/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp b/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp index 06b451e23..c1e51006c 100644 --- a/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp +++ b/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp @@ -690,6 +690,7 @@ void _AVX512_ExtraInit(void* functions) { coreFunction->MNNPoolingAvg = (decltype(coreFunction->MNNPoolingAvg))(MNN::poolingAvg); // Set min value as 1 << 24 coreFunction->MNNPoolingMax = (decltype(coreFunction->MNNPoolingMax))(MNN::poolingMax); + coreFunction->MNNPoolingMaxWithRedice = (decltype(coreFunction->MNNPoolingMaxWithRedice))(MNN::poolingMaxWithRedice); coreFunction->MNNSelectBinaryFunctionForFloat = _AVX512_MNNSelectBinaryFunctionForFloat; coreFunction->MNNCopyC4WithStride = _AVX512_MNNCopyC4WithStride; coreFunction->MNNAddC4WithStride = _AVX512_MNNAddC4WithStride; diff --git a/source/backend/cpu/x86_x64/avxfma/MathFunctions.cpp b/source/backend/cpu/x86_x64/avxfma/MathFunctions.cpp index a1af0bea8..eeaab5cc5 100644 --- a/source/backend/cpu/x86_x64/avxfma/MathFunctions.cpp +++ b/source/backend/cpu/x86_x64/avxfma/MathFunctions.cpp @@ -23,12 +23,16 @@ void _AVX_MNNGeluFMA(float *dst, const float *src, size_t size, float* parameter auto var10 = _mm256_set1_ps(0.5); auto varOne = _mm256_set1_ps(1.f); auto varNegOne = _mm256_set1_ps(-1.f); + auto clamp_min = _mm256_set1_ps(-5.0f); + auto clamp_max = _mm256_set1_ps(5.0f); for (int i = 0; i < size; i++) { auto x = _mm256_loadu_ps(src + i * 8); auto y = _mm256_mul_ps(x, x); y = _mm256_mul_ps(y, x); y = _mm256_fmadd_ps(y, var1, x); y = _mm256_mul_ps(y, var2); + y = _mm256_max_ps(y, clamp_min); + y = _mm256_min_ps(y, clamp_max); // y = tanh(y) { auto y2 = _mm256_mul_ps(y, y); diff --git a/source/backend/cpu/x86_x64/sse/FunctionSummary.hpp b/source/backend/cpu/x86_x64/sse/FunctionSummary.hpp index fed2a0fba..55d2b7c7d 100644 --- a/source/backend/cpu/x86_x64/sse/FunctionSummary.hpp +++ b/source/backend/cpu/x86_x64/sse/FunctionSummary.hpp @@ -39,6 +39,7 @@ void _SSE_MNNAddC4WithStride(const float* source, float* dest, size_t srcStride, void _SSE_MNNReluWithSlopeChannel(float* dst, const float* src, const float* slope, size_t sizeQuad, size_t depthQuad); void _SSE_MNNGelu(float* dst, const float* src, size_t size, float* parameters); +void _SSE_MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params); void _SSE_MNNHardSwish(float* dst, const float* src, size_t size); diff --git a/source/backend/cpu/x86_x64/sse/MathFunctions.cpp b/source/backend/cpu/x86_x64/sse/MathFunctions.cpp index ecfdcaf88..3af6c1a0d 100644 --- a/source/backend/cpu/x86_x64/sse/MathFunctions.cpp +++ b/source/backend/cpu/x86_x64/sse/MathFunctions.cpp @@ -167,6 +167,8 @@ void _SSE_MNNGelu(float* dst, const float* src, size_t size, float* parameters) auto var10 = _mm_set1_ps(0.5); auto varOne = _mm_set1_ps(1.f); auto varNegOne = _mm_set1_ps(-1.f); + auto clamp_min = _mm_set1_ps(-5.0f); + auto clamp_max = _mm_set1_ps(5.0f); for (int i = 0; i < size * 2; i++) { auto x = _mm_loadu_ps(src + i * 4); auto y = _mm_mul_ps(x, x); @@ -174,6 +176,8 @@ void _SSE_MNNGelu(float* dst, const float* src, size_t size, float* parameters) y = _mm_mul_ps(y, var1); y = _mm_add_ps(y, x); y = _mm_mul_ps(y, var2); + y = _mm_max_ps(y, clamp_min); + y = _mm_min_ps(y, clamp_max); // y = tanh(y) { auto y2 = _mm_mul_ps(y, y); @@ -270,7 +274,6 @@ void _SSE_MNNNorm(float *dst, const float *src, const float *gamma, const float } } } - void _SSE_MNNNormInt8(int8_t* dst, const int8_t* src, const float* gamma, const float* beta, float epsilon, size_t size, QuanPrePostParameters* params) { float tmpfloat4[4]; int count = static_cast(size / 4); @@ -341,3 +344,47 @@ void _SSE_MNNNormInt8(int8_t* dst, const int8_t* src, const float* gamma, const // step 4: Float -> Int8 _SSE_MNNFloat2Int8(dstf, dst, size / 4, outScale.data(), params->minValue, params->maxValue, params->outputZeroPoint[0]); } + +void _SSE_MNNReluWithSlopeChannelInt8(int8_t* dst, const int8_t* src, const float* slope, size_t planeNumber, size_t depthQuad, QuanPrePostParameters *params) { + uint8_t* dstO = (uint8_t*)dst; + uint8_t* srcO = (uint8_t*)src; + auto outputZero = _mm_set1_ps(static_cast(params->outputZeroPoint[0])); + __m128 maxValue = _mm_set1_ps(params->maxValue); + __m128 minValue = _mm_set1_ps(params->minValue); + auto offset = _mm_set1_epi32(128); + auto zero = _mm_set1_epi32(0); + __m128 plus = _mm_set1_ps(0.5f); + __m128 minus = _mm_set1_ps(-0.5f); + __m128i zeroPointValue = _mm_set1_epi32(static_cast(params->inputZeroPoint[0]) + 128); + for (int j = 0;j < depthQuad; ++j) { + auto slopeZ = _mm_loadu_ps(slope + 4 * j); + const uint8_t* srcZ = srcO + 4 * j * planeNumber; + uint8_t* dstZ = dstO + 4 * j * planeNumber; + int32_t srcZ_ext[4] = {*(int32_t*)srcZ, 0, 0, 0}; + for (int i = 0; i < planeNumber; ++i) { + // auto srcData8 = _mm_loadu_si32(srcZ); + auto srcData8 = _mm_castps_si128(_mm_loadu_ps((float*)srcZ_ext)); + auto srcData16 = _mm_unpacklo_epi8(srcData8, zero); + auto srcData32 = _mm_unpacklo_epi16(srcData16, zero); + srcData32 = _mm_sub_epi32(srcData32, zeroPointValue); + auto srcDataf = _mm_cvtepi32_ps(srcData32); + auto mask1 = _mm_cmplt_ps(srcDataf, _mm_castsi128_ps(zero)); + auto mask0 = _mm_cmpge_ps(srcDataf, _mm_castsi128_ps(zero)); + auto f = _mm_mul_ps(srcDataf, slopeZ); + f = _mm_add_ps(f, outputZero); + f = _mm_min_ps(f, maxValue); + f = _mm_max_ps(f, minValue); + auto r = _mm_add_ps(_mm_and_ps(srcDataf, mask0), _mm_and_ps(f, mask1)); + auto m0 = _mm_cmplt_ps(r, _mm_castsi128_ps(zero)); + m0 = _mm_blendv_ps(plus, minus, m0); + r = _mm_add_ps(r, m0); + // Round to zero + auto d0 = _mm_cvtps_epi32(_mm_round_ps(r, 3)); + d0 = _mm_add_epi32(d0, offset); + d0 = _mm_packs_epi32(d0, d0); + d0 = _mm_packus_epi16(d0, d0); + *((int*)dst + i) = _mm_cvtsi128_si32(d0); + } + } +} + diff --git a/source/backend/cuda/CMakeLists.txt b/source/backend/cuda/CMakeLists.txt index be9e9ca81..d4ea34b4d 100644 --- a/source/backend/cuda/CMakeLists.txt +++ b/source/backend/cuda/CMakeLists.txt @@ -80,6 +80,7 @@ endif() option(MNN_CUDA_QUANT "Enable MNN CUDA Quant File" OFF) option(MNN_CUDA_BF16 "Enable MNN CUDA Bfloat16 File" OFF) +option(MNN_CUDA_TUNE_PARAM "Enable MNN CUDA Tuning Cutlass Params" OFF) IF (MNN_CUDA_QUANT) add_definitions(-DENABLE_CUDA_QUANT) @@ -89,6 +90,10 @@ IF (MNN_CUDA_BF16) add_definitions(-DENABLE_CUDA_BF16) ENDIF() +IF (MNN_CUDA_TUNE_PARAM) + add_definitions(-DENABLE_CUDA_TUNE_PARAM) +ENDIF() + file(GLOB_RECURSE MNN_CUDA_SRC ${CMAKE_CURRENT_LIST_DIR}/core/* ${CMAKE_CURRENT_SOURCE_DIR}/execution/*) message(STATUS "message ${CUDA_NVCC_FLAGS} !!!!!!!!!!! ${CUDA_INCLUDE_DIRS}") diff --git a/source/backend/cuda/core/CUDABackend.cpp b/source/backend/cuda/core/CUDABackend.cpp index 7ed2e5690..4cb001290 100644 --- a/source/backend/cuda/core/CUDABackend.cpp +++ b/source/backend/cuda/core/CUDABackend.cpp @@ -19,7 +19,6 @@ #include "execution/MNNCUDADefine.hpp" #include "execution/CastExecution.hpp" #include "CUDATools.hpp" - // #define MNN_CUDA_COPY_DEBUG namespace MNN { @@ -70,6 +69,14 @@ float CUDARuntimeWrapper::onGetMemoryInMB() { return staticMemoryInMB; } +std::pair CUDARuntimeWrapper::onGetCache() {//make Cache + return mCUDARuntime->makeCache(); +} + +bool CUDARuntimeWrapper::onSetCache(const void* buffer, size_t size) {//set Cache + return mCUDARuntime->setCache(std::make_pair(buffer, size)); +} + Backend* CUDARuntimeWrapper::onCreate(const BackendConfig* config) const { #ifdef LOG_VERBOSE MNN_PRINT("cudaruntime:%p, create CUDABackend\n", this); diff --git a/source/backend/cuda/core/CUDABackend.hpp b/source/backend/cuda/core/CUDABackend.hpp index f5db02aa8..11d061831 100644 --- a/source/backend/cuda/core/CUDABackend.hpp +++ b/source/backend/cuda/core/CUDABackend.hpp @@ -40,6 +40,8 @@ class MNN_PUBLIC CUDARuntimeWrapper : public Runtime { return Compiler_Loop; } virtual float onGetMemoryInMB() override; + virtual std::pair onGetCache() override; + virtual bool onSetCache(const void* buffer, size_t size) override; private: std::shared_ptr mBufferPool; diff --git a/source/backend/cuda/core/runtime/CUDARuntime.cpp b/source/backend/cuda/core/runtime/CUDARuntime.cpp index eba62e7ba..76749affb 100644 --- a/source/backend/cuda/core/runtime/CUDARuntime.cpp +++ b/source/backend/cuda/core/runtime/CUDARuntime.cpp @@ -15,6 +15,7 @@ #include #include #include "core/Macro.h" +#include "execution/cutlass_common/tune/CudaCache_generated.h" //#define MNN_OPEN_TIME_TRACE #include @@ -164,6 +165,71 @@ void CUDARuntime::memset(void *dst, int value, size_t size_in_bytes) { checkKernelErrors; } +std::pair CUDARuntime::makeCache() { + std::unique_ptr cache(new CudaCache::CacheT); + for (auto& iter : mTunedBlockWarpShape) { + std::unique_ptr tuning(new CudaCache::AutotuningT); + tuning->params = iter.first.first; + tuning->problemSize = iter.first.second; + + tuning->threadBlockSize = iter.second.first; + tuning->timeCost = iter.second.second; + + cache->tunings.emplace_back(std::move(tuning)); + } + + flatbuffers::FlatBufferBuilder builder; + auto lastOffset = CudaCache::Cache::Pack(builder, cache.get()); + builder.Finish(lastOffset); + mBuffer.resize(builder.GetSize()); + ::memcpy(mBuffer.data(), builder.GetBufferPointer(), builder.GetSize()); + return std::make_pair(mBuffer.data(), mBuffer.size()); +} + +bool CUDARuntime::setCache(std::pair cache) { + auto buffer = cache.first; + auto size = cache.second; + if (nullptr == buffer) { + mCacheOutside = nullptr; + mCacheOutsideSize = 0; + mBuffer.clear(); + return false;//actually get nothing + } + mCacheOutsideSize = size; + mCacheOutside = buffer; + auto cacheBuffer = CudaCache::GetCache(buffer); + flatbuffers::Verifier verify((const uint8_t*)buffer, size); + if (false == CudaCache::VerifyCacheBuffer(verify)) { + return false; + } + if (nullptr == cacheBuffer->tunings()) { + return false; + } + + // Load Auto Tuning Info + if (nullptr != cacheBuffer->tunings()) { + auto tuningInfo = cacheBuffer->tunings(); + for (int i=0; isize(); ++i) { + auto tun = tuningInfo->GetAs(i); + if (nullptr == tun->params() || nullptr == tun->problemSize()) { + MNN_ERROR("Error tunning info\n"); + continue; + } + std::vector param(tun->params()->size()); + for (int v=0; vparams()->data()[v]; + } + std::vector problem(tun->problemSize()->size()); + for (int v=0; vproblemSize()->data()[v]; + } + std::string blockShape = tun->threadBlockSize()->str(); + uint32_t cost = tun->timeCost(); + mTunedBlockWarpShape.insert(std::make_pair(std::make_pair(param, problem), std::make_pair(blockShape, cost))); + } + } + return true; +} } // namespace MNN diff --git a/source/backend/cuda/core/runtime/CUDARuntime.hpp b/source/backend/cuda/core/runtime/CUDARuntime.hpp index d82be0ec8..e5c2204e2 100644 --- a/source/backend/cuda/core/runtime/CUDARuntime.hpp +++ b/source/backend/cuda/core/runtime/CUDARuntime.hpp @@ -121,6 +121,11 @@ class CUDARuntime { return mProp.sharedMemPerBlock; } + std::map, std::vector>, std::pair> & getTunedBlockWarpShape() { + return mTunedBlockWarpShape; + }; + std::pair makeCache(); + bool setCache(std::pair cache); int selectDeviceMaxFreeMemory(); @@ -134,6 +139,12 @@ class CUDARuntime { float mFlops = 4.0f; bool mIsCreateError{false}; size_t mThreadPerBlock = 128; + +private: + std::map, std::vector>, std::pair> mTunedBlockWarpShape; + std::vector mBuffer; + const void* mCacheOutside = nullptr; + size_t mCacheOutsideSize = 0; }; } // namespace MNN diff --git a/source/backend/cuda/execution/ConvCutlassExecution.cu b/source/backend/cuda/execution/ConvCutlassExecution.cu index 0c267c18a..d99aed46d 100644 --- a/source/backend/cuda/execution/ConvCutlassExecution.cu +++ b/source/backend/cuda/execution/ConvCutlassExecution.cu @@ -201,7 +201,48 @@ ErrorCode ConvCutlassExecution::onResize(const std::vector &inputs, con return callCutlassGemmTensorCore884(inputs, outputs); } +#ifdef ENABLE_CUDA_TUNE_PARAM + /* + // 0 -> Gemm, 1~N -> BatchGemm + int32_t batchSize = 0; + // [0]->A, [1]->B, [2]->bias, [3]->output + std::pair ptrOffset[4]; + int32_t batchOffset[4]; + // [0]->alpha, [1]->beta, [2]->splitK + int32_t coefs[3]; + // 0 -> RowColumn, 1 -> RowRow + int32_t layout; + bool epilogueVectorize + */ + mInfo.problemSize[0] = mGemmInfo.elh[0]; + mInfo.problemSize[1] = mGemmInfo.elhPad[2]; + mInfo.problemSize[2] = mGemmInfo.elhPad[1]; + + mInfo.coefs[0] = 1; + mInfo.coefs[1] = 1; + mInfo.coefs[2] = 1; + + mInfo.epilogueVectorize = true; + mInfo.epilogueType = mActivationType;// Linear-Relu-Relu6 + mInfo.precisionType = mPrecisonLevel;// + mInfo.backend = mBackendPtr; + + mInfo.batchSize = 0;// For Gemm + mInfo.layout = 0; + void *inputA_ptr = mNeedIm2Col ? (void *)mIm2ColBuffer : (void *)input->deviceId(); + + mInfo.ptrOffset[0] = std::make_pair((void *)inputA_ptr, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[1] = std::make_pair((void *)mFilterAddr, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[2] = std::make_pair((void *)mBiasAddr, 0); + mInfo.ptrOffset[3] = std::make_pair((void *)outputs[0]->deviceId(), mGemmInfo.elhPad[2]); + getGemmTensorCoreFloat16Param(&mInfo); + // set preferd block shape argments + setGemmTensorCoreFloat16Argments(&mInfo); + return NO_ERROR; +#else + return callCutlassGemmTensorCore(inputs, outputs); +#endif } ErrorCode ConvCutlassExecution::onExecute(const std::vector &inputs, const std::vector &outputs) { diff --git a/source/backend/cuda/execution/ConvCutlassExecution.hpp b/source/backend/cuda/execution/ConvCutlassExecution.hpp index 1c1bd8684..1fa7dfac8 100644 --- a/source/backend/cuda/execution/ConvCutlassExecution.hpp +++ b/source/backend/cuda/execution/ConvCutlassExecution.hpp @@ -13,7 +13,7 @@ #include "CutlassGemmParam.hpp" #include "MNNCUDADefine.hpp" #include "MNNCUDAFunction.cuh" -#include "cutlass/CutlassConvCommonExecution.hpp" +#include "cutlass_common/CutlassConvCommonExecution.hpp" namespace MNN { namespace CUDA { diff --git a/source/backend/cuda/execution/ConvWinogradExecution.cu b/source/backend/cuda/execution/ConvWinogradExecution.cu index 8ce9d173d..d74499f16 100644 --- a/source/backend/cuda/execution/ConvWinogradExecution.cu +++ b/source/backend/cuda/execution/ConvWinogradExecution.cu @@ -127,7 +127,14 @@ ConvWinogradExecution::Resource::~Resource() { // Do nothing } -ConvWinogradExecution::ConvWinogradExecution(Backend* backend, const MNN::Op* op, std::shared_ptr res) : Execution(backend), mOp(op) { +ConvWinogradExecution::ConvWinogradExecution(Backend* backend, const MNN::Op* op, std::shared_ptr res) : + #ifdef ENABLE_CUDA_TUNE_PARAM + CutlassGemmTuneCommonExecution(backend), + #else + Execution(backend), + #endif + mOp(op) +{ mResource = res; int precisonLevel = static_cast(backend)->getPrecision(); mFp16Infer = (precisonLevel == 2); @@ -302,6 +309,48 @@ ErrorCode ConvWinogradExecution::onResize(const std::vector &inputs, c } //MNN_PRINT("Winograd BatchGemm batch:%d, MNK:%d-%d-%d\n", mBlock2, mGemmInfo.elh[0], mGemmInfo.elhPad[2], mGemmInfo.elhPad[1]); if(mFp16Infer) { + #ifdef ENABLE_CUDA_TUNE_PARAM + /* + // 0 -> Gemm, 1~N -> BatchGemm + int32_t batchSize = 0; + // [0]->A, [1]->B, [2]->bias, [3]->output + std::pair ptrOffset[4]; + int32_t batchOffset[4]; + // [0]->alpha, [1]->beta, [2]->splitK + int32_t coefs[3]; + // 0 -> RowColumn, 1 -> RowRow + int32_t layout; + bool epilogueVectorize + */ + mInfo.problemSize[0] = mGemmInfo.elh[0]; + mInfo.problemSize[1] = mGemmInfo.elhPad[2]; + mInfo.problemSize[2] = mGemmInfo.elhPad[1]; + + mInfo.coefs[0] = 1; + mInfo.coefs[1] = 0; + mInfo.epilogueVectorize = true; + mInfo.epilogueType = 0;// Linear + mInfo.precisionType = 2;// FP16_FP16 + mInfo.backend = mResource->mBackend; + + mInfo.batchSize = mBlock2; + mInfo.layout = 0; + + mInfo.ptrOffset[0] = std::make_pair((void *)mBtdB_Buffer, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[1] = std::make_pair((void *)mResource->mFilter, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[2] = std::make_pair((void *)mResource->mBias, 0); + mInfo.ptrOffset[3] = std::make_pair((void *)mMatmul_Buffer, mGemmInfo.elhPad[2]); + + mInfo.batchOffset[0] = mGemmInfo.elh[0] * mGemmInfo.elhPad[1]; + mInfo.batchOffset[1] = mGemmInfo.elhPad[1] * mGemmInfo.elhPad[2]; + mInfo.batchOffset[2] = 0; + mInfo.batchOffset[3] = mGemmInfo.elh[0] * mGemmInfo.elhPad[2]; + + getGemmBatchedTensorCoreFloat16Param(&mInfo); + // set preferd block shape argments + setGemmBatchedTensorCoreFloat16Argments(&mInfo); + + #else typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm75::Arguments arguments{problem_size, // <- problem size of matrix multiplication {(ElementInput_F16 *)mBtdB_Buffer, mGemmInfo.elhPad[1]}, // Ptr + ldm (int64_t)(mGemmInfo.elh[0] * mGemmInfo.elhPad[1]), // batch_stride_A @@ -329,6 +378,7 @@ ErrorCode ConvWinogradExecution::onResize(const std::vector &inputs, c // Initialize CUTLASS kernel with arguments and workspace pointer status = mGemmBatchedF16F16LnSm75.initialize(arguments, (uint8_t *)mWorkspace); cutlass_check(status); + #endif } else { typename GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm75::Arguments arguments{problem_size, // <- problem size of matrix multiplication @@ -427,8 +477,12 @@ ErrorCode ConvWinogradExecution::onExecute(const std::vector &inputs, c cutlass::Status status = mGemmBatchedF16F32LnSm75(); cutlass_check(status); } else { + #ifdef ENABLE_CUDA_TUNE_PARAM + runGemmBatchedTensorCoreFloat16Infer(&mInfo); + #else cutlass::Status status = mGemmBatchedF16F16LnSm75(); cutlass_check(status); + #endif } } } diff --git a/source/backend/cuda/execution/ConvWinogradExecution.hpp b/source/backend/cuda/execution/ConvWinogradExecution.hpp index a83a9ee46..e262a966c 100644 --- a/source/backend/cuda/execution/ConvWinogradExecution.hpp +++ b/source/backend/cuda/execution/ConvWinogradExecution.hpp @@ -14,10 +14,19 @@ #include "MNNCUDADefine.hpp" #include "MNNCUDAFunction.cuh" +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp" +#endif namespace MNN { namespace CUDA { -class ConvWinogradExecution : public Execution { +class ConvWinogradExecution : + #ifdef ENABLE_CUDA_TUNE_PARAM + public CutlassGemmTuneCommonExecution + #else + public Execution + #endif +{ public: struct Resource; static bool isValid(const Convolution2D* conv); diff --git a/source/backend/cuda/execution/DeconvSingleInputExecution.hpp b/source/backend/cuda/execution/DeconvSingleInputExecution.hpp index ba5dd51e7..eafd15eb0 100644 --- a/source/backend/cuda/execution/DeconvSingleInputExecution.hpp +++ b/source/backend/cuda/execution/DeconvSingleInputExecution.hpp @@ -13,7 +13,7 @@ #include "MNNCUDADefine.hpp" #include "CutlassGemmParam.hpp" #include "MNNCUDAFunction.cuh" -#include "cutlass/CutlassDeconvCommonExecution.hpp" +#include "cutlass_common/CutlassDeconvCommonExecution.hpp" namespace MNN { namespace CUDA { diff --git a/source/backend/cuda/execution/MatMulExecution.cu b/source/backend/cuda/execution/MatMulExecution.cu index 5eb42d68b..f7bdaa669 100644 --- a/source/backend/cuda/execution/MatMulExecution.cu +++ b/source/backend/cuda/execution/MatMulExecution.cu @@ -123,7 +123,13 @@ __global__ void PackPadFill( } -MatMulExecution::MatMulExecution(bool transposeA, bool transposeB, Backend *backend, int aS, int bS, int cS) : Execution(backend) { +MatMulExecution::MatMulExecution(bool transposeA, bool transposeB, Backend *backend, int aS, int bS, int cS) : + #ifdef ENABLE_CUDA_TUNE_PARAM + CutlassGemmTuneCommonExecution(backend) + #else + Execution(backend) + #endif +{ mTransposeA = transposeA; mTransposeB = transposeB; mBackend = backend; @@ -397,6 +403,82 @@ void MatMulExecution::setArguments(const std::vector &inputs, const st } if(mFp16Infer) { + #ifdef ENABLE_CUDA_TUNE_PARAM + /* + // 0 -> Gemm, 1~N -> BatchGemm + int32_t batchSize = 0; + // [0]->A, [1]->B, [2]->bias, [3]->output + std::pair ptrOffset[4]; + int32_t batchOffset[4]; + // [0]->alpha, [1]->beta, [2]->splitK + int32_t coefs[3]; + // 0 -> RowColumn, 1 -> RowRow + int32_t layout; + bool epilogueVectorize + */ + mInfo.problemSize[0] = mGemmInfo.elh[0]; + mInfo.problemSize[1] = mGemmInfo.elh[2]; + mInfo.problemSize[2] = mGemmInfo.elhPad[1]; + + mInfo.coefs[0] = 1; + mInfo.coefs[1] = 0; + if (inputs.size() > 2) { + mInfo.coefs[1] = 1; + } + mInfo.epilogueVectorize = true; + mInfo.epilogueType = 0;// Linear + mInfo.precisionType = 2;// FP16_FP16 + mInfo.backend = mBackend; + + if(mUseRRLayout) { + mInfo.batchSize = mBatch; + mInfo.layout = 1; + + mInfo.ptrOffset[0] = std::make_pair((void *)mTempMatA, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[1] = std::make_pair((void *)mTempMatB, mGemmInfo.elhPad[2]); + mInfo.ptrOffset[2] = std::make_pair((void *)mBiasPtr, 0); + mInfo.ptrOffset[3] = std::make_pair((void *)C->deviceId(), mGemmInfo.elhPad[2]); + + mInfo.batchOffset[0] = mGemmInfo.elh[0] * mGemmInfo.elhPad[1]* mAs; + mInfo.batchOffset[1] = mGemmInfo.elhPad[1] * mGemmInfo.elhPad[2]* mBs; + mInfo.batchOffset[2] = 0; + mInfo.batchOffset[3] = mGemmInfo.elh[0] * mGemmInfo.elhPad[2]; + } else { + if(hAlignment) { + mInfo.epilogueVectorize = true; + } else { + mInfo.epilogueVectorize = false; + } + + if(hAlignment && mConvertGemmSplitK) { + mInfo.batchSize = 0; + mInfo.layout = 0; + mInfo.coefs[2] = 16; + + mInfo.ptrOffset[0] = std::make_pair((void *)mTempMatA, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[1] = std::make_pair((void *)mTempMatB, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[2] = std::make_pair((void *)mBiasPtr, 0); + mInfo.ptrOffset[3] = std::make_pair((void *)C->deviceId(), mGemmInfo.elh[2]); + } else { + mInfo.batchSize = mBatch; + mInfo.layout = 0; + + mInfo.ptrOffset[0] = std::make_pair((void *)mTempMatA, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[1] = std::make_pair((void *)mTempMatB, mGemmInfo.elhPad[1]); + mInfo.ptrOffset[2] = std::make_pair((void *)mBiasPtr, 0); + mInfo.ptrOffset[3] = std::make_pair((void *)C->deviceId(), mGemmInfo.elh[2]); + + mInfo.batchOffset[0] = mGemmInfo.elh[0] * mGemmInfo.elhPad[1]* mAs; + mInfo.batchOffset[1] = mGemmInfo.elhPad[1] * mGemmInfo.elh[2]* mBs; + mInfo.batchOffset[2] = 0; + mInfo.batchOffset[3] = mGemmInfo.elh[0] * mGemmInfo.elh[2]; + } + } + getGemmBatchedTensorCoreFloat16Param(&mInfo); + + // set preferd block shape argments + setGemmBatchedTensorCoreFloat16Argments(&mInfo); + #else if(mUseRRLayout) { typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm75::Arguments arguments{problem_size, // <- problem size of matrix multiplication {(ElementInput_F16 *)mTempMatA, mGemmInfo.elhPad[1]}, // Ptr + ldm @@ -530,7 +612,7 @@ void MatMulExecution::setArguments(const std::vector &inputs, const st } } } - + #endif } else { if(mUseRRLayout) { if(mNeedConvertMatAB) { @@ -1044,6 +1126,9 @@ ErrorCode MatMulExecution::onExecute(const std::vector &inputs, const } } else { + #ifdef ENABLE_CUDA_TUNE_PARAM + runGemmBatchedTensorCoreFloat16Infer(&mInfo); + #else if(mUseRRLayout) { cutlass::Status status = mGemmBatchedF16F16LnAlign8RRSm75(); cutlass_check(status); @@ -1066,6 +1151,7 @@ ErrorCode MatMulExecution::onExecute(const std::vector &inputs, const } } } + #endif } // printf("normal:%d rrlayout:%d convertab:%d halign:%d\n", mFp16Fp32MixInfer, mUseRRLayout, mNeedConvertMatAB, hAlignment); return NO_ERROR; diff --git a/source/backend/cuda/execution/MatMulExecution.hpp b/source/backend/cuda/execution/MatMulExecution.hpp index ebd3fcb2d..9d48a3eac 100644 --- a/source/backend/cuda/execution/MatMulExecution.hpp +++ b/source/backend/cuda/execution/MatMulExecution.hpp @@ -15,9 +15,19 @@ #include "CutlassGemmParam.hpp" #include "MNNCUDAFunction.cuh" +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp" +#endif + namespace MNN { namespace CUDA { -class MatMulExecution : public Execution { +class MatMulExecution : + #ifdef ENABLE_CUDA_TUNE_PARAM + public CutlassGemmTuneCommonExecution + #else + public Execution + #endif +{ public: MatMulExecution(bool transposeA, bool transposeB, Backend *backend, int aS = 1, int bS = 1, int cS = 1); virtual ~MatMulExecution(); @@ -34,6 +44,7 @@ class MatMulExecution : public Execution { Backend* mBackend = nullptr; std::shared_ptr mBiasTensor; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm75 mGemmBatchedF16F16LnAlign1RCSm75; GemmTensor_F16_F16_Linear_AlignCuda_Sm75 mGemmF16F16LnAlign1Sm75; GemmBatchedTensor_F32_F32_Linear_AlignCuda_Row_Column_Sm75 mGemmBatchedF32F32LnAlign1RCSm75; diff --git a/source/backend/cuda/execution/MultiInputConvExecution.hpp b/source/backend/cuda/execution/MultiInputConvExecution.hpp index f79df96e2..46765ea79 100644 --- a/source/backend/cuda/execution/MultiInputConvExecution.hpp +++ b/source/backend/cuda/execution/MultiInputConvExecution.hpp @@ -13,7 +13,7 @@ #include "CutlassGemmParam.hpp" #include "MNNCUDADefine.hpp" #include "MNNCUDAFunction.cuh" -#include "cutlass/CutlassConvCommonExecution.hpp" +#include "cutlass_common/CutlassConvCommonExecution.hpp" namespace MNN { namespace CUDA { diff --git a/source/backend/cuda/execution/MultiInputDeconvExecution.hpp b/source/backend/cuda/execution/MultiInputDeconvExecution.hpp index b3320af31..88e3853cb 100644 --- a/source/backend/cuda/execution/MultiInputDeconvExecution.hpp +++ b/source/backend/cuda/execution/MultiInputDeconvExecution.hpp @@ -10,7 +10,7 @@ #include "backend/cuda/core/CUDABackend.hpp" #include "core/Execution.hpp" -#include "cutlass/CutlassDeconvCommonExecution.hpp" +#include "cutlass_common/CutlassDeconvCommonExecution.hpp" #include "MNNCUDADefine.hpp" #include "MNNCUDAFunction.cuh" diff --git a/source/backend/cuda/execution/bf16/ConvCutlassBf16Execution.hpp b/source/backend/cuda/execution/bf16/ConvCutlassBf16Execution.hpp index be17b8ab2..a19529665 100644 --- a/source/backend/cuda/execution/bf16/ConvCutlassBf16Execution.hpp +++ b/source/backend/cuda/execution/bf16/ConvCutlassBf16Execution.hpp @@ -15,7 +15,7 @@ #include "CutlassGemmBf16Param.hpp" #include "../MNNCUDADefine.hpp" #include "../MNNCUDAFunction.cuh" -#include "../cutlass/CutlassConvCommonExecution.hpp" +#include "../cutlass_common/CutlassConvCommonExecution.hpp" namespace MNN { namespace CUDA { diff --git a/source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.cu b/source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.cu similarity index 95% rename from source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.cu rename to source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.cu index 57509ffd1..fc0af0aa1 100644 --- a/source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.cu +++ b/source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.cu @@ -11,7 +11,13 @@ namespace MNN { namespace CUDA { -CutlassConvCommonExecution::CutlassConvCommonExecution(Backend *backend) : Execution(backend) { +CutlassConvCommonExecution::CutlassConvCommonExecution(Backend *backend) : +#ifdef ENABLE_CUDA_TUNE_PARAM +CutlassGemmTuneCommonExecution(backend) +#else +Execution(backend) +#endif +{ mBackendPtr = backend; } @@ -103,6 +109,9 @@ ErrorCode CutlassConvCommonExecution::runCutlassGemmFunc() { return NO_ERROR; } +#ifdef ENABLE_CUDA_TUNE_PARAM + runGemmTensorCoreFloat16Infer(&mInfo); +#else if(mActivationType == 1) { if(mFp16Fp32MixInfer) { cutlass::Status status = mGemmF16F32ReluSm75(); @@ -128,7 +137,7 @@ ErrorCode CutlassConvCommonExecution::runCutlassGemmFunc() { cutlass_check(status); } } - +#endif return NO_ERROR; } diff --git a/source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.hpp b/source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.hpp similarity index 93% rename from source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.hpp rename to source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.hpp index c1bd6df98..10c9b3ca6 100644 --- a/source/backend/cuda/execution/cutlass/CutlassConvCommonExecution.hpp +++ b/source/backend/cuda/execution/cutlass_common/CutlassConvCommonExecution.hpp @@ -16,10 +16,19 @@ #include "../MNNCUDADefine.hpp" #include "../MNNCUDAFunction.cuh" +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "tune/CutlassGemmTuneCommonExecution.hpp" +#endif namespace MNN { namespace CUDA { -class CutlassConvCommonExecution : public Execution { +class CutlassConvCommonExecution : + #ifdef ENABLE_CUDA_TUNE_PARAM + public CutlassGemmTuneCommonExecution + #else + public Execution + #endif +{ public: CutlassConvCommonExecution(Backend* backend); virtual ~CutlassConvCommonExecution() = default; diff --git a/source/backend/cuda/execution/cutlass/CutlassDeconvCommonExecution.cu b/source/backend/cuda/execution/cutlass_common/CutlassDeconvCommonExecution.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassDeconvCommonExecution.cu rename to source/backend/cuda/execution/cutlass_common/CutlassDeconvCommonExecution.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassDeconvCommonExecution.hpp b/source/backend/cuda/execution/cutlass_common/CutlassDeconvCommonExecution.hpp similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassDeconvCommonExecution.hpp rename to source/backend/cuda/execution/cutlass_common/CutlassDeconvCommonExecution.hpp diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmBf16TensorCore.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmBf16TensorCore.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmBf16TensorCore.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmBf16TensorCore.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat16.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat16.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat16.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat16.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat16Deconv.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat16Deconv.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat16Deconv.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat16Deconv.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat32.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat32.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat32.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat32.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat32Decov.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat32Decov.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmCUDACoreFloat32Decov.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmCUDACoreFloat32Decov.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmTensorCore.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCore.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmTensorCore.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCore.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmTensorCore884.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCore884.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmTensorCore884.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCore884.cu diff --git a/source/backend/cuda/execution/cutlass/CutlassGemmTensorCoreDeconv.cu b/source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCoreDeconv.cu similarity index 100% rename from source/backend/cuda/execution/cutlass/CutlassGemmTensorCoreDeconv.cu rename to source/backend/cuda/execution/cutlass_common/CutlassGemmTensorCoreDeconv.cu diff --git a/source/backend/cuda/execution/cutlass_common/tune/CudaCache_generated.h b/source/backend/cuda/execution/cutlass_common/tune/CudaCache_generated.h new file mode 100644 index 000000000..2fed08e31 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CudaCache_generated.h @@ -0,0 +1,297 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_CUDACACHE_CUDACACHE_H_ +#define FLATBUFFERS_GENERATED_CUDACACHE_CUDACACHE_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace CudaCache { + +struct Autotuning; +struct AutotuningT; + +struct Cache; +struct CacheT; + +inline const flatbuffers::TypeTable *AutotuningTypeTable(); + +inline const flatbuffers::TypeTable *CacheTypeTable(); + +struct AutotuningT : public flatbuffers::NativeTable { + typedef Autotuning TableType; + std::vector params; + std::vector problemSize; + std::string threadBlockSize; + uint32_t timeCost; + AutotuningT() + : timeCost(0) { + } +}; + +struct Autotuning FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef AutotuningT NativeTableType; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return AutotuningTypeTable(); + } + const flatbuffers::Vector *params() const { + return GetPointer *>(4); + } + const flatbuffers::Vector *problemSize() const { + return GetPointer *>(6); + } + const flatbuffers::String *threadBlockSize() const { + return GetPointer(8); + } + uint32_t timeCost() const { + return GetField(10, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, 4) && + verifier.VerifyVector(params()) && + VerifyOffset(verifier, 6) && + verifier.VerifyVector(problemSize()) && + VerifyOffset(verifier, 8) && + verifier.VerifyString(threadBlockSize()) && + VerifyField(verifier, 10) && + verifier.EndTable(); + } + AutotuningT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(AutotuningT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const AutotuningT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct AutotuningBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_params(flatbuffers::Offset> params) { + fbb_.AddOffset(4, params); + } + void add_problemSize(flatbuffers::Offset> problemSize) { + fbb_.AddOffset(6, problemSize); + } + void add_threadBlockSize(flatbuffers::Offset threadBlockSize) { + fbb_.AddOffset(8, threadBlockSize); + } + void add_timeCost(uint32_t timeCost) { + fbb_.AddElement(10, timeCost, 0); + } + explicit AutotuningBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + AutotuningBuilder &operator=(const AutotuningBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateAutotuning( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> params = 0, + flatbuffers::Offset> problemSize = 0, + flatbuffers::Offset threadBlockSize = 0, + uint32_t timeCost = 0) { + AutotuningBuilder builder_(_fbb); + builder_.add_timeCost(timeCost); + builder_.add_threadBlockSize(threadBlockSize); + builder_.add_problemSize(problemSize); + builder_.add_params(params); + return builder_.Finish(); +} + +flatbuffers::Offset CreateAutotuning(flatbuffers::FlatBufferBuilder &_fbb, const AutotuningT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct CacheT : public flatbuffers::NativeTable { + typedef Cache TableType; + std::vector> tunings; + CacheT() { + } +}; + +struct Cache FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef CacheT NativeTableType; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return CacheTypeTable(); + } + const flatbuffers::Vector> *tunings() const { + return GetPointer> *>(4); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, 4) && + verifier.VerifyVector(tunings()) && + verifier.VerifyVectorOfTables(tunings()) && + verifier.EndTable(); + } + CacheT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(CacheT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const CacheT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct CacheBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_tunings(flatbuffers::Offset>> tunings) { + fbb_.AddOffset(4, tunings); + } + explicit CacheBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + CacheBuilder &operator=(const CacheBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateCache( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> tunings = 0) { + CacheBuilder builder_(_fbb); + builder_.add_tunings(tunings); + return builder_.Finish(); +} + +flatbuffers::Offset CreateCache(flatbuffers::FlatBufferBuilder &_fbb, const CacheT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +inline AutotuningT *Autotuning::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new AutotuningT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void Autotuning::UnPackTo(AutotuningT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = params(); if (_e) { _o->params.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->params[_i] = _e->Get(_i); } } }; + { auto _e = problemSize(); if (_e) { _o->problemSize.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->problemSize[_i] = _e->Get(_i); } } }; + { auto _e = threadBlockSize(); if (_e) _o->threadBlockSize = _e->str(); }; + { auto _e = timeCost(); _o->timeCost = _e; }; +} + +inline flatbuffers::Offset Autotuning::Pack(flatbuffers::FlatBufferBuilder &_fbb, const AutotuningT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateAutotuning(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateAutotuning(flatbuffers::FlatBufferBuilder &_fbb, const AutotuningT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const AutotuningT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _params = _o->params.size() ? _fbb.CreateVector(_o->params) : 0; + auto _problemSize = _o->problemSize.size() ? _fbb.CreateVector(_o->problemSize) : 0; + auto _threadBlockSize = _o->threadBlockSize.empty() ? 0 : _fbb.CreateString(_o->threadBlockSize); + auto _timeCost = _o->timeCost; + return CudaCache::CreateAutotuning( + _fbb, + _params, + _problemSize, + _threadBlockSize, + _timeCost); +} + +inline CacheT *Cache::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = new CacheT(); + UnPackTo(_o, _resolver); + return _o; +} + +inline void Cache::UnPackTo(CacheT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = tunings(); if (_e) { _o->tunings.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->tunings[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } }; +} + +inline flatbuffers::Offset Cache::Pack(flatbuffers::FlatBufferBuilder &_fbb, const CacheT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateCache(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateCache(flatbuffers::FlatBufferBuilder &_fbb, const CacheT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const CacheT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _tunings = _o->tunings.size() ? _fbb.CreateVector> (_o->tunings.size(), [](size_t i, _VectorArgs *__va) { return CreateAutotuning(*__va->__fbb, __va->__o->tunings[i].get(), __va->__rehasher); }, &_va ) : 0; + return CudaCache::CreateCache( + _fbb, + _tunings); +} + +inline const flatbuffers::TypeTable *AutotuningTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 1, -1 }, + { flatbuffers::ET_UINT, 1, -1 }, + { flatbuffers::ET_STRING, 0, -1 }, + { flatbuffers::ET_UINT, 0, -1 } + }; + static const char * const names[] = { + "params", + "problemSize", + "threadBlockSize", + "timeCost" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 4, type_codes, nullptr, nullptr, names + }; + return &tt; +} + +inline const flatbuffers::TypeTable *CacheTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 1, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + AutotuningTypeTable + }; + static const char * const names[] = { + "tunings" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +inline const CudaCache::Cache *GetCache(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const CudaCache::Cache *GetSizePrefixedCache(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyCacheBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedCacheBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishCacheBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedCacheBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +inline std::unique_ptr UnPackCache( + const void *buf, + const flatbuffers::resolver_function_t *res = nullptr) { + return std::unique_ptr(GetCache(buf)->UnPack(res)); +} + +} // namespace CudaCache + +#endif // FLATBUFFERS_GENERATED_CUDACACHE_CUDACACHE_H_ diff --git a/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedParamTune.hpp b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedParamTune.hpp new file mode 100644 index 000000000..b790bdbdd --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedParamTune.hpp @@ -0,0 +1,828 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM + +#include "../../CutlassGemmParam.hpp" +#include "cutlass/gemm/device/gemm_batched.h" + +namespace MNN { +namespace CUDA { +using BatchedSwizzleThreadBlock = cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Row_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Row_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::RowMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 6>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 4>; + +using GemmBatchedTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::GemmBatched< + cutlass::half_t, + cutlass::layout::RowMajor, + cutlass::half_t, + cutlass::layout::ColumnMajor, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + BatchedSwizzleThreadBlock, + 3>; + +} +} +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedTensorFloat16TuneInfer.cu b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedTensorFloat16TuneInfer.cu new file mode 100644 index 000000000..4f957b762 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmBatchedTensorFloat16TuneInfer.cu @@ -0,0 +1,917 @@ +// +// CutlassGemmTuneCommonExecution.cu +// MNN +// +// Created by MNN on 2023/10/09. +// Copyright © 2018, Alibaba Group Holding Limited +// +#ifdef ENABLE_CUDA_TUNE_PARAM + +#include "CutlassGemmTuneCommonExecution.hpp" + +namespace MNN { +namespace CUDA { + +void CutlassGemmTuneCommonExecution::setGemmBatchedTensorCoreFloat16Argments(const GemmParamInfo* params) { + + ElementComputeEpilogue alpha = ElementComputeEpilogue(params->coefs[0]); + ElementComputeEpilogue beta = ElementComputeEpilogue(params->coefs[1]); + + // Split K dimension into 1 partitions + cutlass::gemm::GemmCoord problem_size(params->problemSize[0], params->problemSize[1], params->problemSize[2]);// m n k + void* workspace_ptr = nullptr; + // MNN_PRINT("gemmbatched: batch-%d, problem-%d %d %d, layout:%d vec:%d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2], params->layout, params->epilogueVectorize); + + if(params->batchSize > 0) { // BatchGemm + if(params->layout == 0) { // RowColumn + if(params->epilogueVectorize) { // AlignTensor + // BatchGemm + RowColumn + AlignTensor + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + } else { + // BatchGemm + RowColumn + AlignCuda + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + } + } else if(params->layout == 1) { // RowRow + if(params->epilogueVectorize) { // AlignTensor + // BatchGemm + RowRow + AlignTensor + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign8RR_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + } else { + // BatchGemm + RowColumn + AlignCuda + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmBatchedF16F16TensorAlign1RR_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + return; + } + } + } else { + MNN_PRINT("Not support Gemm Infer now\n"); + } + } else { // Gemm + MNN_PRINT("Not support Gemm Infer now\n"); + } +} + +void CutlassGemmTuneCommonExecution::runGemmBatchedTensorCoreFloat16Infer(const GemmParamInfo* params) { + // MNN_PRINT("Run %d %d %d %s\n", params->batchSize, params->layout, params->epilogueVectorize, params->prefeBlockSize.c_str()); + if(params->batchSize > 0) { // BatchGemm + if(params->layout == 0) { // RowColumn + if(params->epilogueVectorize) { // AlignTensor + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RC_256x64x32(); + cutlass_check(status); + return; + } + } else { + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RC_256x64x32(); + cutlass_check(status); + return; + } + } + } else if(params->layout == 1) { + if(params->epilogueVectorize) { // AlignTensor + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign8RR_256x64x32(); + cutlass_check(status); + return; + } + } else { + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmBatchedF16F16TensorAlign1RR_256x64x32(); + cutlass_check(status); + return; + } + } + } + } + MNN_PRINT("Error Not support Gemm Infer now\n"); + return; +} + +} // namespace CUDA +} // namespace MNN +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmParamTune.hpp b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmParamTune.hpp new file mode 100644 index 000000000..247c7a21d --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmParamTune.hpp @@ -0,0 +1,1305 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM + +#include "../../CutlassGemmParam.hpp" + +namespace MNN { +namespace CUDA { +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Linear, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F16_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + cutlass::half_t, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F16_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignCuda_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueCudaOp_F32_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 64>, + cutlass::gemm::GemmShape<32, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x64 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 64>, + cutlass::gemm::GemmShape<64, 32, 64>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<32, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 6, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<128, 64, 32>, + cutlass::gemm::GemmShape<64, 32, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x128x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<64, 128, 32>, + cutlass::gemm::GemmShape<32, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 4, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +using GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_256x64x32 = cutlass::gemm::device::Gemm< + cutlass::half_t, + LayoutInputA, + cutlass::half_t, + LayoutInputB, + float, + LayoutOutput, + ElementAccumulator, + cutlass::arch::OpClassTensorOp, + cutlass::arch::Sm80, + cutlass::gemm::GemmShape<256, 64, 32>, + cutlass::gemm::GemmShape<64, 64, 32>, + cutlass::gemm::GemmShape<16, 8, 16>, + EpilogueTensorOp_F32_Relu6, + SwizzleThreadBlock, + 3, + 128 / cutlass::sizeof_bits::value, 128 / cutlass::sizeof_bits::value, true>; + +} +} +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTune.hpp b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTune.hpp new file mode 100644 index 000000000..e79858c2a --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTune.hpp @@ -0,0 +1,40 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM + +#include "backend/cuda/core/runtime/CUDARuntime.hpp" +#include "backend/cuda/core/CUDABackend.hpp" +#include "CutlassGemmBatchedParamTune.hpp" +#include "CutlassGemmParamTune.hpp" +#include +// #define MNN_CUDA_TUNE_LOG + +namespace MNN { +namespace CUDA { + +struct GemmParamInfo { + // MxNxK + int32_t problemSize[3] = {1, 1, 1}; + // 0 -> Gemm, 1~N -> BatchGemm + int32_t batchSize = 0; + // [0]->A, [1]->B, [2]->bias, [3]->output + std::pair ptrOffset[4]; + int32_t batchOffset[4]; + // [0]->alpha, [1]->beta, [2]->splitK + int32_t coefs[3]; + // 0 -> RowColumn, 1 -> RowRow + int32_t layout; + bool epilogueVectorize; + // 0 -> Linear, 1 -> Relu, 2 -> Relu6 + int32_t epilogueType; + // In_Out: 0 -> FP16_FP32, 1 -> FP32_FP32, 2 -> FP16_FP16 + int32_t precisionType; + std::string prefeBlockSize; + Backend* backend; +}; + +void getGemmBatchedTensorCoreFloat16Param(GemmParamInfo* params); +void getGemmTensorCoreFloat16Param(GemmParamInfo* params); + +} +} + +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp new file mode 100644 index 000000000..aec63be1a --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/CutlassGemmTuneCommonExecution.hpp @@ -0,0 +1,122 @@ +// +// CutlassGemmTuneCommonExecution.hpp +// MNN +// +// Created by MNN on 2023/10/09. +// Copyright © 2018, Alibaba Group Holding Limited +// + +#ifdef ENABLE_CUDA_TUNE_PARAM + +#ifndef CutlassGemmTuneCommonExecution_hpp +#define CutlassGemmTuneCommonExecution_hpp + +#include "backend/cuda/core/CUDABackend.hpp" +#include "core/Execution.hpp" +#include "execution/cutlass_common/tune/CutlassGemmTune.hpp" + +namespace MNN { +namespace CUDA { + +class CutlassGemmTuneCommonExecution : public Execution { +public: + CutlassGemmTuneCommonExecution(Backend* backend) : Execution(backend) {}; + virtual ~CutlassGemmTuneCommonExecution() = default; + + void setGemmBatchedTensorCoreFloat16Argments(const GemmParamInfo* params); + void runGemmBatchedTensorCoreFloat16Infer(const GemmParamInfo* params); + void setGemmTensorCoreFloat16Argments(const GemmParamInfo* params); + void runGemmTensorCoreFloat16Infer(const GemmParamInfo* params); +protected: + GemmParamInfo mInfo; + + // GemmBatched + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 mGemmBatchedF16F16TensorAlign8RC_64x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 mGemmBatchedF16F16TensorAlign8RC_64x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 mGemmBatchedF16F16TensorAlign8RC_64x128x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 mGemmBatchedF16F16TensorAlign8RC_128x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 mGemmBatchedF16F16TensorAlign8RC_128x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 mGemmBatchedF16F16TensorAlign8RC_256x64x32; + + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32 mGemmBatchedF16F16TensorAlign1RC_64x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64 mGemmBatchedF16F16TensorAlign1RC_64x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32 mGemmBatchedF16F16TensorAlign1RC_64x128x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32 mGemmBatchedF16F16TensorAlign1RC_128x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64 mGemmBatchedF16F16TensorAlign1RC_128x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32 mGemmBatchedF16F16TensorAlign1RC_256x64x32; + + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32 mGemmBatchedF16F16TensorAlign8RR_64x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64 mGemmBatchedF16F16TensorAlign8RR_64x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32 mGemmBatchedF16F16TensorAlign8RR_64x128x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32 mGemmBatchedF16F16TensorAlign8RR_128x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64 mGemmBatchedF16F16TensorAlign8RR_128x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32 mGemmBatchedF16F16TensorAlign8RR_256x64x32; + + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32 mGemmBatchedF16F16TensorAlign1RR_64x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64 mGemmBatchedF16F16TensorAlign1RR_64x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32 mGemmBatchedF16F16TensorAlign1RR_64x128x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32 mGemmBatchedF16F16TensorAlign1RR_128x64x32; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64 mGemmBatchedF16F16TensorAlign1RR_128x64x64; + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32 mGemmBatchedF16F16TensorAlign1RR_256x64x32; + + // // Gemm Linear + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F16TensorLnAlign8RC_64x64x32; + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F16TensorLnAlign8RC_64x64x64; + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F16TensorLnAlign8RC_64x128x32; + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F16TensorLnAlign8RC_128x64x32; + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F16TensorLnAlign8RC_128x64x64; + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F16TensorLnAlign8RC_256x64x32; + + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F32TensorLnAlign8RC_64x64x32; + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F32TensorLnAlign8RC_64x64x64; + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F32TensorLnAlign8RC_64x128x32; + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F32TensorLnAlign8RC_128x64x32; + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F32TensorLnAlign8RC_128x64x64; + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F32TensorLnAlign8RC_256x64x32; + + // // Gemm Relu + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F16TensorReluAlign8RC_64x64x32; + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F16TensorReluAlign8RC_64x64x64; + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F16TensorReluAlign8RC_64x128x32; + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F16TensorReluAlign8RC_128x64x32; + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F16TensorReluAlign8RC_128x64x64; + GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F16TensorReluAlign8RC_256x64x32; + + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F32TensorReluAlign8RC_64x64x32; + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F32TensorReluAlign8RC_64x64x64; + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F32TensorReluAlign8RC_64x128x32; + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F32TensorReluAlign8RC_128x64x32; + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F32TensorReluAlign8RC_128x64x64; + GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F32TensorReluAlign8RC_256x64x32; + + // // Gemm Relu6 + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F16TensorRelu6Align8RC_64x64x32; + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F16TensorRelu6Align8RC_64x64x64; + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F16TensorRelu6Align8RC_64x128x32; + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F16TensorRelu6Align8RC_128x64x32; + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F16TensorRelu6Align8RC_128x64x64; + GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F16TensorRelu6Align8RC_256x64x32; + + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x32 mGemmF16F32TensorRelu6Align8RC_64x64x32; + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x64 mGemmF16F32TensorRelu6Align8RC_64x64x64; + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x128x32 mGemmF16F32TensorRelu6Align8RC_64x128x32; + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x32 mGemmF16F32TensorRelu6Align8RC_128x64x32; + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x64 mGemmF16F32TensorRelu6Align8RC_128x64x64; + GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_256x64x32 mGemmF16F32TensorRelu6Align8RC_256x64x32; + + int mGpuComputeCap = 75; + int mActivationType = 0; + bool mFp16Infer = false; + bool mFp32Infer = false; + bool mFp16Fp32MixInfer = false; + bool mBf16Infer = false; + int mPrecisonLevel; + std::shared_ptr workspaceTensor; + void* mWorkspace; +}; + +} // namespace CUDA +} // namespace MNN + +#endif /* CutlassGemmTuneCommonExecution */ +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/GemmBatchedTensorCoreFloat16Tune.cu b/source/backend/cuda/execution/cutlass_common/tune/GemmBatchedTensorCoreFloat16Tune.cu new file mode 100644 index 000000000..66c06f5e0 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/GemmBatchedTensorCoreFloat16Tune.cu @@ -0,0 +1,1513 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "CutlassGemmTune.hpp" +namespace MNN { +namespace CUDA { + +void getGemmBatchedTensorCoreFloat16Param(GemmParamInfo* params) { + MNN_ASSERT(params->epilogueType == 0);// Linear + MNN_ASSERT(params->precisionType == 2);// FP16_FP16 + auto& tunedBlockWarpShape = static_cast(params->backend)->getCUDARuntime()->getTunedBlockWarpShape(); + std::vector info = {(int32_t)params->layout, (int32_t)params->epilogueVectorize, (int32_t)params->precisionType}; + std::vector problem = {(uint32_t)params->batchSize, (uint32_t)params->problemSize[0], (uint32_t)params->problemSize[1], (uint32_t)params->problemSize[2]}; + auto key = std::make_pair(info, problem); + + if (tunedBlockWarpShape.find(key) != tunedBlockWarpShape.end()) { + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("getGemmBatchedTensorCoreFloat16Param Found! prefer:%s\n", tunedBlockWarpShape[key].first.c_str()); + #endif + params->prefeBlockSize = tunedBlockWarpShape[key].first; + return; + } + + cudaEvent_t events[12]; + cudaError_t result; + for (auto & event : events) { + result = cudaEventCreate(&event); + if (result != cudaSuccess) { + MNN_PRINT("Failed to create CUDA event\n"); + } + } + + ElementComputeEpilogue alpha = ElementComputeEpilogue(params->coefs[0]); + ElementComputeEpilogue beta = ElementComputeEpilogue(params->coefs[1]); + + // Split K dimension into 1 partitions + cutlass::gemm::GemmCoord problem_size(params->problemSize[0], params->problemSize[1], params->problemSize[2]);// m n k + void* workspace_ptr = nullptr; + + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("gemmbatched: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + #endif + const int warmup = 10; + const int loop = 100; + int event_index = 0; + // us + float costTime_64x64x32, costTime_64x64x64, costTime_64x128x32, costTime_128x64x32, costTime_128x64x64, costTime_256x64x32; + + if(params->batchSize > 0) { // BatchGemm + if(params->layout == 0) { // RowColumn + if(params->epilogueVectorize) { // AlignTensor + // BatchGemm + RowColumn + AlignTensor + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + } else { + // BatchGemm + RowColumn + AlignCuda + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Column_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + } + } else if(params->layout == 1) { // RowRow + if(params->epilogueVectorize) { // AlignTensor + // BatchGemm + RowRow + AlignTensor + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignTensor_Row_Row_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + + } else { + // BatchGemm + RowColumn + AlignCuda + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + (int64_t)params->batchOffset[0], // batch_stride_A + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + (int64_t)params->batchOffset[1], // batch_stride_B + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + (int64_t)params->batchOffset[2], // batch_stride_bias + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + (int64_t)params->batchOffset[3], // batch_stride_C + {alpha, beta}, // <- tuple of alpha and beta + params->batchSize}; // batch_count + + size_t workspace_size = GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmBatchedTensor_F16_F16_Linear_AlignCuda_Row_Row_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + } + } + } else { // Gemm + getGemmTensorCoreFloat16Param(params); + } + + std::vector times; + std::string preferBlock; + uint32_t time_64x64x32 = (uint32_t)((1000 * costTime_64x64x32) / loop); + times.push_back(time_64x64x32); + uint32_t time_64x64x64 = (uint32_t)((1000 * costTime_64x64x64) / loop); + times.push_back(time_64x64x64); + uint32_t time_64x128x32 = (uint32_t)((1000 * costTime_64x128x32) / loop); + times.push_back(time_64x128x32); + uint32_t time_128x64x32 = (uint32_t)((1000 * costTime_128x64x32) / loop); + times.push_back(time_128x64x32); + uint32_t time_128x64x64 = (uint32_t)((1000 * costTime_128x64x64) / loop); + times.push_back(time_128x64x64); + uint32_t time_256x64x32 = (uint32_t)((1000 * costTime_256x64x32) / loop); + times.push_back(time_256x64x32); + std::sort(times.begin(), times.end()); + + if(time_64x64x32 == times[0]) { + preferBlock = "_64x64x32_"; + } else if(time_64x64x64 == times[0]) { + preferBlock = "_64x64x64_"; + } else if(time_128x64x32 == times[0]) { + preferBlock = "_128x64x32_"; + } else if(time_128x64x64 == times[0]) { + preferBlock = "_128x64x64_"; + } else if(time_256x64x32 == times[0]) { + preferBlock = "_256x64x32_"; + } else if(time_64x128x32 == times[0]) { + preferBlock = "_64x128x32_"; + } else { + MNN_PRINT("param blockSize assign error, please check\n"); + } + params->prefeBlockSize = preferBlock; + + #ifdef MNN_CUDA_TUNE_LOG + static uint32_t total_64x64x32 = 0, total_64x64x64 = 0, total_64x128x32 = 0, total_128x64x32 = 0, total_128x64x64 = 0, total_256x64x32 = 0, total_min = 0; + total_64x64x32 += time_64x64x32; + total_64x64x64 += time_64x64x64; + total_64x128x32 += time_64x128x32; + total_128x64x32 += time_128x64x32; + total_128x64x64 += time_128x64x64; + total_256x64x32 += time_256x64x32; + total_min += times[0]; + + MNN_PRINT("GemmBatch layer time:%d %d %d %d %d %d, mintime:%d\ntotal time: %d %d %d %d %d %d, mintime:%d\n", time_64x64x32, time_64x64x64, time_64x128x32, time_128x64x32, time_128x64x64, time_256x64x32, times[0], total_64x64x32, total_64x64x64, total_64x128x32, total_128x64x32, total_128x64x64, total_256x64x32, total_min); + #endif + + for (auto & event : events) { + cudaEventDestroy(event); + } + + if(tunedBlockWarpShape.find(key) == tunedBlockWarpShape.end()) { + tunedBlockWarpShape.insert(std::make_pair(key, std::make_pair(preferBlock, times[0]))); + } +} + +} +} + +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16Tune.cu b/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16Tune.cu new file mode 100644 index 000000000..9ebb0dcc1 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16Tune.cu @@ -0,0 +1,755 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "CutlassGemmTune.hpp" +namespace MNN { +namespace CUDA { + +void getGemmTensorCoreFloat16Param(GemmParamInfo* params) { + MNN_ASSERT(params->batchSize == 0); + + auto& tunedBlockWarpShape = static_cast(params->backend)->getCUDARuntime()->getTunedBlockWarpShape(); + std::vector info = {(int32_t)params->layout, (int32_t)params->epilogueVectorize, (int32_t)params->precisionType}; + std::vector problem = {(uint32_t)params->batchSize, (uint32_t)params->problemSize[0], (uint32_t)params->problemSize[1], (uint32_t)params->problemSize[2]}; + auto key = std::make_pair(info, problem); + + if (tunedBlockWarpShape.find(key) != tunedBlockWarpShape.end()) { + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("getGemmTensorCoreFloat16Param Found! prefer:%s\n", tunedBlockWarpShape[key].first.c_str()); + #endif + params->prefeBlockSize = tunedBlockWarpShape[key].first; + return; + } + + cudaEvent_t events[12]; + cudaError_t result; + for (auto & event : events) { + result = cudaEventCreate(&event); + if (result != cudaSuccess) { + MNN_PRINT("Failed to create CUDA event\n"); + } + } + + ElementComputeEpilogue alpha = ElementComputeEpilogue(params->coefs[0]); + ElementComputeEpilogue beta = ElementComputeEpilogue(params->coefs[1]); + + // Split K dimension into 1 partitions + cutlass::gemm::GemmCoord problem_size(params->problemSize[0], params->problemSize[1], params->problemSize[2]);// m n k + void* workspace_ptr = nullptr; + + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("gemm: batch-%d, problem-%d %d %d. %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2], params->epilogueType, params->precisionType); + #endif + const int warmup = 10; + const int loop = 100; + int event_index = 0; + // us + float costTime_64x128x32, costTime_64x64x32, costTime_64x64x64, costTime_128x64x32, costTime_128x64x64, costTime_256x64x32; + + // Different epilogue type(linear/relu/relu6) regard as the same + if(params->precisionType == 2) { // InOut:FP16_FP16 + // BatchGemm + RowColumn + AlignTensor + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + } else if(params->precisionType == 0) { // InOut:FP16_FP32 + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32 gemmBatched_64x64x32; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32 : %f ms\n", costTime_64x64x32); + #endif + } + + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64 gemmBatched_64x64x64; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64 : %f ms\n", costTime_64x64x64); + #endif + } + + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32 gemmBatched_64x128x32; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_64x128x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_64x128x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32 : %f ms\n", costTime_64x128x32); + #endif + } + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32 gemmBatched_128x64x32; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32 : %f ms\n", costTime_128x64x32); + #endif + } + + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64 gemmBatched_128x64x64; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_128x64x64(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_128x64x64, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64 : %f ms\n", costTime_128x64x64); + #endif + } + + { + GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32 gemmBatched_256x64x32; + + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = gemmBatched_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = gemmBatched_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + + for(int i = 0; i < warmup; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + cudaDeviceSynchronize(); + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + for(int i = 0; i < loop; i++) { + cutlass::Status status = gemmBatched_256x64x32(); + cutlass_check(status); + } + + result = cudaEventRecord(events[event_index++]); + if (result != cudaSuccess) { + MNN_PRINT("Failed to record start event.\n"); + } + + cudaDeviceSynchronize(); + cudaEventElapsedTime(&costTime_256x64x32, events[event_index-2], events[event_index-1]); + #ifdef MNN_CUDA_TUNE_LOG + MNN_PRINT("GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32 : %f ms\n", costTime_256x64x32); + #endif + } + } + + std::vector times; + std::string preferBlock; + uint32_t time_64x64x32 = (uint32_t)((1000 * costTime_64x64x32) / loop); + times.push_back(time_64x64x32); + uint32_t time_64x64x64 = (uint32_t)((1000 * costTime_64x64x64) / loop); + times.push_back(time_64x64x64); + uint32_t time_64x128x32 = (uint32_t)((1000 * costTime_64x128x32) / loop); + times.push_back(time_64x128x32); + uint32_t time_128x64x32 = (uint32_t)((1000 * costTime_128x64x32) / loop); + times.push_back(time_128x64x32); + uint32_t time_128x64x64 = (uint32_t)((1000 * costTime_128x64x64) / loop); + times.push_back(time_128x64x64); + uint32_t time_256x64x32 = (uint32_t)((1000 * costTime_256x64x32) / loop); + times.push_back(time_256x64x32); + std::sort(times.begin(), times.end()); + + if(time_64x64x32 == times[0]) { + preferBlock = "_64x64x32_"; + } else if(time_64x64x64 == times[0]) { + preferBlock = "_64x64x64_"; + } else if(time_128x64x32 == times[0]) { + preferBlock = "_128x64x32_"; + } else if(time_128x64x64 == times[0]) { + preferBlock = "_128x64x64_"; + } else if(time_256x64x32 == times[0]) { + preferBlock = "_256x64x32_"; + } else if(time_64x128x32 == times[0]) { + preferBlock = "_64x128x32_"; + } else { + MNN_PRINT("param blockSize assign error, please check\n"); + } + params->prefeBlockSize = preferBlock; + + #ifdef MNN_CUDA_TUNE_LOG + static uint32_t total_64x128x32 = 0, total_64x64x32 = 0, total_64x64x64 = 0, total_128x64x32 = 0, total_128x64x64 = 0, total_256x64x32 = 0, total_min = 0; + total_64x64x32 += time_64x64x32; + total_64x64x64 += time_64x64x64; + total_64x128x32 += time_64x128x32; + total_128x64x32 += time_128x64x32; + total_128x64x64 += time_128x64x64; + total_256x64x32 += time_256x64x32; + total_min += times[0]; + + MNN_PRINT("Gemm layer time:%d %d %d %d %d %d, mintime:%d\ntotal time: %d %d %d %d %d %d, mintime:%d\n", time_64x128x32, time_64x64x32, time_64x64x64, time_128x64x32, time_128x64x64, time_256x64x32, times[0], total_64x128x32, total_64x64x32, total_64x64x64, total_128x64x32, total_128x64x64, total_256x64x32, total_min); + #endif + + for (auto & event : events) { + cudaEventDestroy(event); + } + if(tunedBlockWarpShape.find(key) == tunedBlockWarpShape.end()) { + tunedBlockWarpShape.insert(std::make_pair(key, std::make_pair(preferBlock, times[0]))); + } +} + +} +} + +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16TuneInfer.cu b/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16TuneInfer.cu new file mode 100644 index 000000000..bd5005a41 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/GemmTensorCoreFloat16TuneInfer.cu @@ -0,0 +1,1147 @@ +#ifdef ENABLE_CUDA_TUNE_PARAM +#include "CutlassGemmTuneCommonExecution.hpp" + +namespace MNN { +namespace CUDA { + +void CutlassGemmTuneCommonExecution::setGemmTensorCoreFloat16Argments(const GemmParamInfo* params) { + MNN_ASSERT(params->batchSize == 0); + + ElementComputeEpilogue alpha = ElementComputeEpilogue(params->coefs[0]); + ElementComputeEpilogue beta = ElementComputeEpilogue(params->coefs[1]); + + // Split K dimension into 1 partitions + cutlass::gemm::GemmCoord problem_size(params->problemSize[0], params->problemSize[1], params->problemSize[2]);// m n k + void* workspace_ptr = nullptr; + // MNN_PRINT("gemm: batch-%d, problem-%d %d %d, %d %d, %s\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2], params->epilogueType, params->precisionType, params->prefeBlockSize.c_str()); + + if(params->epilogueType == 0) { // epilogueLinear + if(params->precisionType == 2) { // InOut:FP16_FP16 + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorLnAlign8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } else if(params->precisionType == 0) { // InOut:FP16_FP32 + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Linear_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorLnAlign8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } + } else if(params->epilogueType == 1) { // epilogueRelu + if(params->precisionType == 2) { // InOut:FP16_FP16 + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorReluAlign8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } else if(params->precisionType == 0) { // InOut:FP16_FP32 + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorReluAlign8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } + } else if(params->epilogueType == 2) { // epilogueRelu6 + if(params->precisionType == 2) { // InOut:FP16_FP16 + // MNN_PRINT("gemmbatched0: batch-%d, problem-%d %d %d\n", params->batchSize, params->problemSize[0], params->problemSize[1], params->problemSize[2]); + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F16 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F16 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F16_Relu6_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F16TensorRelu6Align8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } else if(params->precisionType == 0) { // InOut:FP16_FP32 + if (params->prefeBlockSize == "_64x64x32_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_64x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x64x64_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_64x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_64x128x32_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x128x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_64x128x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x128x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_64x128x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x32_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_128x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_128x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_128x64x64_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x64::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_128x64x64::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_128x64x64.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_128x64x64.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + if (params->prefeBlockSize == "_256x64x32_") + { + typename GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_256x64x32::Arguments arguments{problem_size, // <- problem size of matrix multiplication + {(ElementInput_F16 *)params->ptrOffset[0].first, params->ptrOffset[0].second}, // Ptr + ldm + {(ElementInput_F16 *)params->ptrOffset[1].first, params->ptrOffset[1].second}, // Ptr + ldm + {(ElementOutput_F32 *)params->ptrOffset[2].first, params->ptrOffset[2].second}, // Ptr + ldm if ldm = 0, vector, + {(ElementOutput_F32 *)params->ptrOffset[3].first, params->ptrOffset[3].second}, // Ptr + ldm + {alpha, beta}, // <- tuple of alpha and beta + params->coefs[2]}; // splitK + + size_t workspace_size = GemmTensor_F16_F32_Relu6_AlignTensor_Row_Column_Sm80_256x64x32::get_workspace_size(arguments); + if(workspace_size != 0 && workspace_ptr == nullptr) { + std::shared_ptr workspaceTensor; + workspaceTensor.reset(Tensor::createDevice({(int)workspace_size})); + static_cast(params->backend)->onAcquireBuffer(workspaceTensor.get(), Backend::STATIC); + workspace_ptr = (void *)workspaceTensor.get()->buffer().device; + } + // Check the problem size is supported or not + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_256x64x32.can_implement(arguments); + cutlass_check(status); + + // Initialize CUTLASS kernel with arguments and workspace pointer + status = mGemmF16F32TensorRelu6Align8RC_256x64x32.initialize(arguments, (uint8_t *)workspace_ptr); + cutlass_check(status); + } + } + } + MNN_ASSERT(false); + return; +} + +void CutlassGemmTuneCommonExecution::runGemmTensorCoreFloat16Infer(const GemmParamInfo* params) { + // MNN_PRINT("Run %d %d %d %s\n", params->epilogueType, params->precisionType, params->epilogueVectorize, params->prefeBlockSize.c_str()); + if(params->epilogueType == 0) { // epilogueLinear + if(params->precisionType == 2) { // InOut:FP16_FP16 + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F16TensorLnAlign8RC_256x64x32(); + cutlass_check(status); + return; + } + } else if(params->precisionType == 0) { + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F32TensorLnAlign8RC_256x64x32(); + cutlass_check(status); + return; + } + } + } + + if(params->epilogueType == 1) { // epilogueRelu + if(params->precisionType == 2) { // InOut:FP16_FP16 + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F16TensorReluAlign8RC_256x64x32(); + cutlass_check(status); + return; + } + } else if(params->precisionType == 0) { + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F32TensorReluAlign8RC_256x64x32(); + cutlass_check(status); + return; + } + } + } + + if(params->epilogueType == 2) { // epilogueRelu6 + if(params->precisionType == 2) { // InOut:FP16_FP16 + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F16TensorRelu6Align8RC_256x64x32(); + cutlass_check(status); + return; + } + } else if(params->precisionType == 0) { + if (params->prefeBlockSize == "_64x64x32_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x64x64_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_64x128x32_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_64x128x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x32_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_128x64x32(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_128x64x64_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_128x64x64(); + cutlass_check(status); + return; + } + if (params->prefeBlockSize == "_256x64x32_") { + cutlass::Status status = mGemmF16F32TensorRelu6Align8RC_256x64x32(); + cutlass_check(status); + return; + } + } + } + MNN_PRINT("Error Not support Gemm Infer now\n"); + MNN_ASSERT(false); + return; +} + +} +} + +#endif \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/make_cutlass_tune_param.py b/source/backend/cuda/execution/cutlass_common/tune/make_cutlass_tune_param.py new file mode 100644 index 000000000..935ac1eb6 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/make_cutlass_tune_param.py @@ -0,0 +1,208 @@ +#!/usr/bin/python +import sys +import os +import re +gGemmParamTuneHeadFile = "CutlassGemmParamTune.hpp" +gGemmBatchedParamTuneHeadFile = "CutlassGemmBatchedParamTune.hpp" + +def generateGemmTuneFile(headfile): + hpp = "#ifdef ENABLE_CUDA_TUNE_PARAM\n\n" + hpp += "#include \"../../CutlassGemmParam.hpp\"\n\n" + hpp += "namespace MNN {\n" + hpp += "namespace CUDA {\n" + + data_precision = ["cutlass::half_t", "float"] + processor_type = ["cutlass::arch::OpClassTensorOp"] + sm_arch_type = ["cutlass::arch::Sm80"] + mma16816_shape = "cutlass::gemm::GemmShape<16, 8, 16>" + epilogue_type = ["LinearCombination", "LinearCombinationRelu", "LinearCombinationRelu6"] + layout_a_name = "_Row" + layout_b_name = "_Column" + thread_block_shapes = ["cutlass::gemm::GemmShape<64, 64, 64>", "cutlass::gemm::GemmShape<128, 64, 64>", "cutlass::gemm::GemmShape<64, 64, 32>", "cutlass::gemm::GemmShape<128, 64, 32>", "cutlass::gemm::GemmShape<64, 128, 32>", "cutlass::gemm::GemmShape<256, 64, 32>"] + warp_shapes = ["cutlass::gemm::GemmShape<32, 32, 64>", "cutlass::gemm::GemmShape<64, 32, 64>", "cutlass::gemm::GemmShape<32, 32, 32>", "cutlass::gemm::GemmShape<64, 32, 32>", "cutlass::gemm::GemmShape<32, 64, 32>", "cutlass::gemm::GemmShape<64, 64, 32>"] + NumStages = "3" + + for epilogue in epilogue_type: + epilogue_name = "" + if epilogue == "LinearCombination": + epilogue_name = "Linear" + elif epilogue == "LinearCombinationRelu": + epilogue_name = "Relu" + elif epilogue == "LinearCombinationRelu6": + epilogue_name = "Relu6" + + for precision in data_precision: + inp_precision_name = "" + if precision == "cutlass::half_t": + inp_precision_name = "_F16" + elif precision == "float": + inp_precision_name = "_F32" + + for processor in processor_type: + gemm_name = "GemmTensor" + processor_name = ["EpilogueCudaOp", "EpilogueTensorOp"] + + for smarch in sm_arch_type: + sm_name = "_Sm80" + element_input_precision = precision + for element_output_precision in data_precision: + if element_input_precision == "float":# and element_output_precision == "cutlass::half_t": + continue + out_precision_name = "" + if element_output_precision == "cutlass::half_t": + out_precision_name = "_F16_" + elif element_output_precision == "float": + out_precision_name = "_F32_" + + for out_align in processor_name: + out_align_name = "" + if out_align == "EpilogueTensorOp": + out_align_name = "_AlignTensor" + elif out_align == "EpilogueCudaOp": + out_align_name = "_AlignCuda" + + for thread_block_shape in thread_block_shapes: + warp_shape = "cutlass::gemm::GemmShape<32, 32, 64>" + block_size = "_64x64x64" + if thread_block_shape == "cutlass::gemm::GemmShape<64, 64, 64>": + warp_shape = "cutlass::gemm::GemmShape<32, 32, 64>" + block_size = "_64x64x64" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<128, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<64, 32, 32>" + block_size = "_128x64x32" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<64, 128, 32>": + warp_shape = "cutlass::gemm::GemmShape<32, 64, 32>" + block_size = "_64x128x32" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<256, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<64, 64, 32>" + block_size = "_256x64x32" + NumStages = "3" + elif thread_block_shape == "cutlass::gemm::GemmShape<64, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<32, 32, 32>" + block_size = "_64x64x32" + NumStages = "6" + elif thread_block_shape == "cutlass::gemm::GemmShape<128, 64, 64>": + warp_shape = "cutlass::gemm::GemmShape<64, 32, 64>" + block_size = "_128x64x64" + NumStages = "3" + + hpp += "using " + gemm_name + inp_precision_name + out_precision_name + epilogue_name + out_align_name + layout_a_name + layout_b_name + sm_name + block_size + " = cutlass::gemm::device::Gemm<\n " + hpp += element_input_precision + ",\n LayoutInputA,\n " + hpp += element_input_precision + ",\n LayoutInputB,\n " + hpp += element_output_precision + ",\n LayoutOutput,\n ElementAccumulator,\n " + hpp += processor + ",\n " + smarch + ",\n " + hpp += thread_block_shape + ",\n " + warp_shape + ",\n " + hpp += mma16816_shape + ",\n " + + hpp += out_align + out_precision_name + epilogue_name + ",\n " + hpp += "SwizzleThreadBlock,\n " + hpp += NumStages + if sm_name == "_Sm80": + hpp += ",\n 128 / cutlass::sizeof_bits<" + element_input_precision + ">::value, 128 / cutlass::sizeof_bits<" + element_input_precision + ">::value, true>;\n\n" + else : + hpp += ">;\n\n" + + hpp += "}\n}\n#endif" + with open(headfile, "w") as f: + f.write(hpp) + +def generateGemmBatchedTuneFile(headfile): + hpp = "#ifdef ENABLE_CUDA_TUNE_PARAM\n\n" + hpp += "#include \"../../CutlassGemmParam.hpp\"\n" + hpp += "#include \"cutlass/gemm/device/gemm_batched.h\"\n\n" + hpp += "namespace MNN {\n" + hpp += "namespace CUDA {\n" + + hpp += "using BatchedSwizzleThreadBlock = cutlass::gemm::threadblock::GemmBatchedIdentityThreadblockSwizzle;\n\n" + + thread_block_shapes = ["cutlass::gemm::GemmShape<64, 64, 64>", "cutlass::gemm::GemmShape<128, 64, 64>", "cutlass::gemm::GemmShape<64, 64, 32>", "cutlass::gemm::GemmShape<128, 64, 32>", "cutlass::gemm::GemmShape<64, 128, 32>", "cutlass::gemm::GemmShape<256, 64, 32>"] + warp_shapes = ["cutlass::gemm::GemmShape<32, 32, 64>", "cutlass::gemm::GemmShape<64, 32, 64>", "cutlass::gemm::GemmShape<32, 32, 32>", "cutlass::gemm::GemmShape<64, 32, 32>", "cutlass::gemm::GemmShape<32, 64, 32>", "cutlass::gemm::GemmShape<64, 64, 32>"] + NumStages = "3" + + data_precision = ["cutlass::half_t", "float"] + processor_type = ["cutlass::arch::OpClassTensorOp"] + sm_arch_type = ["cutlass::arch::Sm80"] + mma16816_shape = "cutlass::gemm::GemmShape<16, 8, 16>" + layout_type = ["cutlass::layout::RowMajor", "cutlass::layout::ColumnMajor"] + for element_input_precision in ["cutlass::half_t"]: + inp_precision_name = "_F16" + for processor in processor_type: + gemm_name = "GemmBatchedTensor" + processor_name = ["EpilogueCudaOp", "EpilogueTensorOp"] + + for smarch in sm_arch_type: + sm_name = "_Sm80" + for element_output_precision in ["cutlass::half_t", "float"]: + out_precision_name = "" + if element_output_precision == "cutlass::half_t": + out_precision_name = "_F16_" + elif element_output_precision == "float": + out_precision_name = "_F32_" + + for out_align in processor_name: + out_align_name = "" + if out_align == "EpilogueTensorOp": + out_align_name = "_AlignTensor" + elif out_align == "EpilogueCudaOp": + out_align_name = "_AlignCuda" + + layout_a = "cutlass::layout::RowMajor" + layout_a_name = "_Row" + for layout_b in layout_type: + layout_b_name = "" + if layout_b == "cutlass::layout::RowMajor": + layout_b_name = "_Row" + elif layout_b == "cutlass::layout::ColumnMajor": + layout_b_name = "_Column" + + for thread_block_shape in thread_block_shapes: + warp_shape = "cutlass::gemm::GemmShape<32, 32, 64>" + block_size = "_64x64x64" + if thread_block_shape == "cutlass::gemm::GemmShape<64, 64, 64>": + warp_shape = "cutlass::gemm::GemmShape<32, 32, 64>" + block_size = "_64x64x64" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<128, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<64, 32, 32>" + block_size = "_128x64x32" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<64, 128, 32>": + warp_shape = "cutlass::gemm::GemmShape<32, 64, 32>" + block_size = "_64x128x32" + NumStages = "4" + elif thread_block_shape == "cutlass::gemm::GemmShape<256, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<64, 64, 32>" + block_size = "_256x64x32" + NumStages = "3" + elif thread_block_shape == "cutlass::gemm::GemmShape<64, 64, 32>": + warp_shape = "cutlass::gemm::GemmShape<32, 32, 32>" + block_size = "_64x64x32" + NumStages = "6" + elif thread_block_shape == "cutlass::gemm::GemmShape<128, 64, 64>": + warp_shape = "cutlass::gemm::GemmShape<64, 32, 64>" + block_size = "_128x64x64" + NumStages = "3" + + hpp += "using " + gemm_name + inp_precision_name + out_precision_name + "Linear" + out_align_name + layout_a_name + layout_b_name + sm_name + block_size + " = cutlass::gemm::device::GemmBatched<\n " + hpp += element_input_precision + ",\n " + layout_a + ",\n " + hpp += element_input_precision + ",\n " + layout_b + ",\n " + hpp += element_output_precision + ",\n LayoutOutput,\n ElementAccumulator,\n " + hpp += processor + ",\n " + smarch + ",\n " + hpp += thread_block_shape + ",\n " + warp_shape + ",\n " + hpp += mma16816_shape + ",\n " + + hpp += out_align + out_precision_name + "Linear,\n " + hpp += "BatchedSwizzleThreadBlock,\n " + hpp += NumStages + ">;\n\n" + + hpp += "}\n}\n#endif" + with open(headfile, "w") as f: + f.write(hpp) + +if __name__ == '__main__': + generateGemmTuneFile(gGemmParamTuneHeadFile); + generateGemmBatchedTuneFile(gGemmBatchedParamTuneHeadFile); + diff --git a/source/backend/cuda/execution/cutlass_common/tune/schema/CudaCache.fbs b/source/backend/cuda/execution/cutlass_common/tune/schema/CudaCache.fbs new file mode 100644 index 000000000..ba7a27244 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/schema/CudaCache.fbs @@ -0,0 +1,18 @@ +namespace CudaCache; +attribute "priority"; + +table Autotuning { + // layout, alignment, precisionType + params:[int]; + // B, M, N, K + problemSize:[uint]; + + threadBlockSize:string; + timeCost:uint; +} + +table Cache { + tunings:[Autotuning]; +} + +root_type Cache; diff --git a/source/backend/cuda/execution/cutlass_common/tune/schema/README.md b/source/backend/cuda/execution/cutlass_common/tune/schema/README.md new file mode 100644 index 000000000..5a36fb670 --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/schema/README.md @@ -0,0 +1,2 @@ +# Generate scheme +- sh generate.sh \ No newline at end of file diff --git a/source/backend/cuda/execution/cutlass_common/tune/schema/generate.sh b/source/backend/cuda/execution/cutlass_common/tune/schema/generate.sh new file mode 100644 index 000000000..05a35d2aa --- /dev/null +++ b/source/backend/cuda/execution/cutlass_common/tune/schema/generate.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# check is flatbuffer installed or not +FLATC=../../../../../../../../3rd_party/flatbuffers/tmp/flatc + +# clean up +echo "*** cleaning up ***" +rm -f current/*.h +[ ! -d current ] && mkdir current + +# flatc all fbs +pushd current > /dev/null +echo "*** generating fbs under $DIR ***" +find ../*.fbs | xargs ${FLATC} -c -b --gen-object-api --reflect-names + +popd > /dev/null + +cp current/*.h ../ && rm -rf current +# finish +echo "*** done ***" diff --git a/source/backend/metal/AllShader.cpp b/source/backend/metal/AllShader.cpp index 2638f66b5..602184146 100644 --- a/source/backend/metal/AllShader.cpp +++ b/source/backend/metal/AllShader.cpp @@ -2669,11 +2669,19 @@ const char* shader_MetalUnary_metal = " int height;\n" " int size;\n" "};\n" +"static inline float4 MNNEXP(float4 tmp) {\n" +" tmp=clamp(tmp,(float4)-87.0,(float4)87.0);\n" +" return exp(tmp);\n" +"}\n" +"static inline float4 MNNTANH(float4 V) {\n" +" float4 tmp=MNNEXP((float4)(2.0)*V);\n" +" return (tmp-(float4)1.0)/(tmp+(float4)1.0);\n" +"}\n" "static inline float4 neg(float4 V) { return -V; }\n" "static inline float4 square(float4 V) { return V*V; }\n" -"static inline float4 expm1(float4 V) {return exp(V)-1;}\n" +"static inline float4 expm1(float4 V) {return MNNEXP(V)-1;}\n" "static inline float4 reciprocal(float4 V) {return 1.0/(V);}\n" -"static inline float4 sigmoid(float4 V) {return 1.f/(1.f+exp(-V));}\n" +"static inline float4 sigmoid(float4 V) {return 1.f/(1.f+MNNEXP(-V));}\n" "static inline float4 log1p(float4 V) {return log(1.f+V);}\n" "static inline float4 hardswish(float4 V) {\n" " return (float4)(1.0/6.0)*(V*min(max(V+(float4)3,0),(float4)6));\n" @@ -2682,7 +2690,7 @@ const char* shader_MetalUnary_metal = " float4 temp=(float4)0.044715*V*V*V;\n" " temp=(float4)0.79788458*(temp+V);\n" " temp=clamp(temp,(float4)-5.0,(float4)5.0);\n" -" float4 result=((float4)1.0+tanh(temp))*V*(float4)0.5;\n" +" float4 result=((float4)1.0+MNNTANH(temp))*V*(float4)0.5;\n" " return result;\n" "}\n" "#define define_op(op) " @@ -2702,7 +2710,7 @@ const char* shader_MetalUnary_metal = "define_op(square);\n" "define_op(sqrt);\n" "define_op(rsqrt);\n" -"define_op(exp);\n" +"define_op(MNNEXP);\n" "define_op(log);\n" "define_op(sin);\n" "define_op(cos);\n" @@ -2712,7 +2720,7 @@ const char* shader_MetalUnary_metal = "define_op(atan);\n" "define_op(neg);\n" "define_op(reciprocal)\n" -"define_op(tanh);\n" +"define_op(MNNTANH);\n" "define_op(sigmoid);\n" "define_op(sign);\n" "define_op(log1p);\n" diff --git a/source/backend/metal/MetalUnary.mm b/source/backend/metal/MetalUnary.mm index aaf2d0933..c722114b1 100755 --- a/source/backend/metal/MetalUnary.mm +++ b/source/backend/metal/MetalUnary.mm @@ -27,13 +27,13 @@ op_case(SQUARE, square); op_case(SQRT, sqrt); op_case(RSQRT, rsqrt); - op_case(EXP, exp); + op_case(EXP, MNNEXP); op_case(EXPM1, expm1); op_case(LOG, log); op_case(SIN, sin); op_case(COS, cos); op_case(TAN, tan); - op_case(TANH, tanh); + op_case(TANH, MNNTANH); op_case(SIGMOID, sigmoid); op_case(ASIN, asin); op_case(ACOS, acos); diff --git a/source/backend/metal/shader/MetalUnary.metal b/source/backend/metal/shader/MetalUnary.metal index c36a82ef9..0e5297683 100644 --- a/source/backend/metal/shader/MetalUnary.metal +++ b/source/backend/metal/shader/MetalUnary.metal @@ -4,11 +4,20 @@ struct unary_shape { int size; }; +static inline float4 MNNEXP(float4 tmp) { + tmp = clamp(tmp, (float4)-87.0, (float4)87.0); + return exp(tmp); +} + +static inline float4 MNNTANH(float4 value) { + float4 tmp = MNNEXP((float4)(2.0)*value); + return (tmp-(float4)1.0)/(tmp+(float4)1.0); +} static inline float4 neg(float4 value) { return -value; } static inline float4 square(float4 value) { return value * value; } -static inline float4 expm1(float4 value) {return exp(value) - 1;} +static inline float4 expm1(float4 value) {return MNNEXP(value) - 1;} static inline float4 reciprocal(float4 value) {return 1.0/(value);} -static inline float4 sigmoid(float4 value) {return 1.f / (1.f + exp(-value));} +static inline float4 sigmoid(float4 value) {return 1.f / (1.f + MNNEXP(-value));} static inline float4 log1p(float4 value) {return log(1.f + value);} static inline float4 hardswish(float4 value) { return (float4)(1.0/6.0) * (value * min(max(value+(float4)3, 0), (float4)6)); @@ -17,7 +26,7 @@ static inline float4 gelu(float4 value) { float4 temp = (float4)0.044715 * value * value * value; temp = (float4)0.79788458 * (temp + value); temp = clamp(temp, (float4)-5.0, (float4)5.0); - float4 result = ((float4)1.0 + tanh(temp)) * value * (float4)0.5; + float4 result = ((float4)1.0 + MNNTANH(temp)) * value * (float4)0.5; return result; } @@ -39,7 +48,7 @@ define_op(expm1); define_op(square); define_op(sqrt); define_op(rsqrt); -define_op(exp); +define_op(MNNEXP); define_op(log); define_op(sin); define_op(cos); @@ -49,7 +58,7 @@ define_op(acos); define_op(atan); define_op(neg); define_op(reciprocal) -define_op(tanh); +define_op(MNNTANH); define_op(sigmoid); define_op(sign); define_op(log1p); diff --git a/source/backend/opencl/core/OpenCLBackend.cpp b/source/backend/opencl/core/OpenCLBackend.cpp index cc2200e33..b3cf2b4d9 100644 --- a/source/backend/opencl/core/OpenCLBackend.cpp +++ b/source/backend/opencl/core/OpenCLBackend.cpp @@ -428,7 +428,12 @@ Execution* OpenCLBackend::onCreate(const std::vector& inputs, const std #endif auto creators = gCreator(); auto iter = creators->find(std::make_pair(op->type(), mOpenCLRuntime->getGpuMemType())); - + if (0 != inputs.size() && (getDataType(inputs[0]) == DataType_DT_INT8 || inputs[0]->getType().bytes() == 1)) { + #if 0//close log + MNN_PRINT("Don't support type %s for int8 input\n", EnumNameOpType(op->type())); + #endif + return NULL; + } if (iter == creators->end()) { mOpenCLRuntime->setDevideOpRecord(); #if 0//close log @@ -906,11 +911,13 @@ void OpenCLBackend::onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTenso #ifdef LOG_VERBOSE MNN_PRINT("Start onCopyBuffer !\n"); #endif - if (srcTensor->deviceId() == 0 && dstTensor->deviceId() != 0) { + auto srcDevice = (srcTensor->deviceId() != 0 && srcTensor->deviceId() != 1); + auto dstDevice = (dstTensor->deviceId() != 0 && dstTensor->deviceId() != 1); + if (!srcDevice && dstDevice) { copyToDevice(srcTensor, dstTensor); - }else if(srcTensor->deviceId() != 0 && dstTensor->deviceId() == 0){ + }else if(srcDevice && !dstDevice){ copyFromDevice(srcTensor, dstTensor); - }else if(srcTensor->deviceId() != 0 && dstTensor->deviceId() != 0){ + }else if(srcDevice && dstDevice){ mCLRuntime->copyBetweenDevice(srcTensor, dstTensor); }else{ MNN_PRINT("onCopyBuffer float error !!! \n"); @@ -1120,5 +1127,13 @@ bool placeholder = []() { return true; }(); +DataType OpenCLBackend::getDataType(const Tensor* tensor) { + auto des = TensorUtils::getDescribe(tensor); + if (nullptr == des->quantAttr.get()) { + return DataType_DT_FLOAT; + } + return des->type; +} + } // namespace OpenCL } // namespace MNN diff --git a/source/backend/opencl/core/OpenCLBackend.hpp b/source/backend/opencl/core/OpenCLBackend.hpp index a22c5f2a5..c20795b8d 100644 --- a/source/backend/opencl/core/OpenCLBackend.hpp +++ b/source/backend/opencl/core/OpenCLBackend.hpp @@ -127,6 +127,8 @@ class OpenCLBackend : public Backend { BackendConfig::PrecisionMode getPrecision() const { return mPrecision; } + + DataType getDataType(const Tensor* tensor); bool isCreateError() const; virtual void* onMapTensor(Tensor::MapType mtype, Tensor::DimensionType dtype, const Tensor* srcTensor) override; diff --git a/source/backend/opencl/core/runtime/OpenCLRuntime.cpp b/source/backend/opencl/core/runtime/OpenCLRuntime.cpp index 0be70bf32..6a1869c12 100644 --- a/source/backend/opencl/core/runtime/OpenCLRuntime.cpp +++ b/source/backend/opencl/core/runtime/OpenCLRuntime.cpp @@ -237,7 +237,7 @@ OpenCLRuntime::OpenCLRuntime(const BackendConfig::PrecisionMode precision, const if((false == OpenCLSymbolsOperator::getOpenclSymbolsPtr()->isQcomError()) && getDeviceSupportsExtension(*(mFirstGPUDevicePtr.get()), "cl_qcom_recordable_queues")){ uint32_t MaxRecordableQueueSize = mFirstGPUDevicePtr->getInfo(); cl_int err; - if(MaxRecordableQueueSize > 0){ + if(MaxRecordableQueueSize > 0 && IMAGE == mMemType){ // TODO: Use setSessionHint to set the number of mUseRecordableQueueSize mUseRecordableQueueSize = 10; mUseRecordableQueueSize = MaxRecordableQueueSize < mUseRecordableQueueSize ? MaxRecordableQueueSize : mUseRecordableQueueSize; diff --git a/source/backend/opencl/execution/buffer/BinaryBufExecution.cpp b/source/backend/opencl/execution/buffer/BinaryBufExecution.cpp index 61cdce622..73610c99f 100644 --- a/source/backend/opencl/execution/buffer/BinaryBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/BinaryBufExecution.cpp @@ -18,6 +18,10 @@ namespace OpenCL { BinaryBufExecution::BinaryBufExecution(const std::vector &inputs, const std::string &compute, const MNN::Op *op, Backend *backend) : CommonExecution(backend, op), mCompute(compute) { mBuildOptions.emplace("-DOPERATOR=" + compute); + auto dataType = inputs[0]->getType(); + if (dataType.code == halide_type_int){ + mBuildOptions.emplace("-DOPENCL_INPUT_INT"); + } } uint32_t BinaryBufExecution::realSize(const Tensor* tensor) { diff --git a/source/backend/opencl/execution/buffer/ConvBufExecution.cpp b/source/backend/opencl/execution/buffer/ConvBufExecution.cpp index e771bca16..9d738ae38 100644 --- a/source/backend/opencl/execution/buffer/ConvBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/ConvBufExecution.cpp @@ -700,6 +700,11 @@ class ConvolutionBufCreator : public OpenCLBackend::Creator { } } + if(op->main_as_Convolution2D()->common()->group() > 1){ + // Don't support group > 1 now + return nullptr; + } + if (inputs.size() > 1) { // Multi inputs for (int i = 0; i < inputs.size(); ++i) { diff --git a/source/backend/opencl/execution/buffer/DepthwiseConvBufExecution.cpp b/source/backend/opencl/execution/buffer/DepthwiseConvBufExecution.cpp index 0ac37171e..791dc0428 100644 --- a/source/backend/opencl/execution/buffer/DepthwiseConvBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/DepthwiseConvBufExecution.cpp @@ -327,7 +327,7 @@ class DepthwiseConvolutionBufCreator : public OpenCLBackend::Creator { MNN_ASSERT(inputs.size() <= 3); if (inputs.size() > 1) { - MNN_PRINT("multi input depthwise conv for opencl buffer not supoort!\n"); + //MNN_PRINT("multi input depthwise conv for opencl buffer not supoort!\n"); return nullptr; } diff --git a/source/backend/opencl/execution/buffer/MatmulBufExecution.cpp b/source/backend/opencl/execution/buffer/MatmulBufExecution.cpp index db1693237..96cbfb70d 100644 --- a/source/backend/opencl/execution/buffer/MatmulBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/MatmulBufExecution.cpp @@ -70,6 +70,7 @@ ErrorCode MatMulBufExecution::onResize(const std::vector &inputs, cons ret |= mKernel.setArg(idx++, static_cast(height)); ret |= mKernel.setArg(idx++, static_cast(heightblocks)); ret |= mKernel.setArg(idx++, static_cast(widthblocks)); + ret |= mKernel.setArg(idx++, static_cast(width)); MNN_CHECK_CL_SUCCESS(ret, "setArg MatMulBufExecution mTransposeA"); mLocalWorkSize = localWS2DDefault(mGlobalWorkSize, mMaxWorkGroupSize, mOpenCLBackend->getOpenCLRuntime(), mKernelName, mKernel).first; @@ -94,6 +95,7 @@ ErrorCode MatMulBufExecution::onResize(const std::vector &inputs, cons ret |= mKernel.setArg(idx++, static_cast(outputChannel)); ret |= mKernel.setArg(idx++, static_cast(outputChannelBlocks)); ret |= mKernel.setArg(idx++, static_cast(widthblocks)); + ret |= mKernel.setArg(idx++, static_cast(width)); MNN_CHECK_CL_SUCCESS(ret, "setArg MatMulBufExecution"); mLocalWorkSize = localWS2DDefault(mGlobalWorkSize, mMaxWorkGroupSize, mOpenCLBackend->getOpenCLRuntime(), mKernelName, mKernel).first; } diff --git a/source/backend/opencl/execution/buffer/PoolBufExecution.cpp b/source/backend/opencl/execution/buffer/PoolBufExecution.cpp index b7279d3a7..ba7560d5b 100644 --- a/source/backend/opencl/execution/buffer/PoolBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/PoolBufExecution.cpp @@ -30,10 +30,6 @@ PoolBufExecution::PoolBufExecution(const std::vector &inputs, const MN mPaddings[0] = mPoolParams->padY() * 2; mPaddings[1] = mPoolParams->padX() * 2; mPadType = mPoolParams->padType(); - if (mPadType == PoolPadType_VALID) { - mPaddings[0] = 0; - mPaddings[1] = 0; - } if (inputs[0]->channel() >= 16) { TensorUtils::setTensorChannelPack(inputs[0], 16); } @@ -45,6 +41,8 @@ ErrorCode PoolBufExecution::onResize(const std::vector &inputs, const #endif auto input = inputs[0]; auto output = outputs[0]; + bool returnRedice = outputs.size() == 2; + auto redice = returnRedice ? outputs[1] : outputs[0]; auto runtime = mOpenCLBackend->getOpenCLRuntime(); #ifdef MNN_SUPPORT_INTEL_SUBGROUP @@ -65,6 +63,21 @@ ErrorCode PoolBufExecution::onResize(const std::vector &inputs, const mPaddings[0] = padNeededHeight; mPaddings[1] = padNeededWidth; + }else if (mPoolParams->padType() == PoolPadType_VALID) { + mPaddings[0] = mPaddings[1] = 0; + } + + auto countType = mPoolParams->countType(); + if (mPoolParams->pads() != nullptr && mPadType == PoolPadType_CAFFE) { + mPadType = PoolPadType_VALID; + } + + if (countType == MNN::AvgPoolCountType_DEFAULT) { + if (mPadType == MNN::PoolPadType_CAFFE) { + countType = MNN::AvgPoolCountType_INCLUDE_PADDING; + } else { + countType = MNN::AvgPoolCountType_EXCLUDE_PADDING; + } } MNN_ASSERT(mDilations[0] == 1 && mDilations[1] == 1); @@ -86,6 +99,12 @@ ErrorCode PoolBufExecution::onResize(const std::vector &inputs, const if (mPoolType == PoolType_AVEPOOL) { buildOptions.emplace("-DPOOL_AVG"); + if(countType == MNN::AvgPoolCountType_INCLUDE_PADDING){ + buildOptions.emplace("-DCOUNT_INCLUDE_PADDING"); + } + } + if(returnRedice){ + buildOptions.emplace("-DRETURN_REDICE"); } mKernel = runtime->buildKernel("pooling_buf", kernelName, buildOptions); @@ -115,6 +134,7 @@ ErrorCode PoolBufExecution::onResize(const std::vector &inputs, const ret |= mKernel.setArg(idx++, sizeof(strideShape), strideShape); ret |= mKernel.setArg(idx++, sizeof(kernelShape), kernelShape); ret |= mKernel.setArg(idx++, openCLBuffer(output)); + ret |= mKernel.setArg(idx++, openCLBuffer(redice)); ret |= mKernel.setArg(idx++, channelBlocks); MNN_CHECK_CL_SUCCESS(ret, "setArg PoolBufExecution"); @@ -135,6 +155,8 @@ ErrorCode PoolBufExecution::SubgrouponResize(const std::vector &inputs #endif auto input = inputs[0]; auto output = outputs[0]; + bool returnRedice = outputs.size() == 2; + auto redice = returnRedice ? outputs[1] : outputs[0]; if (mPoolParams->isGlobal()) { std::vector inputShape = tensorShapeFormat(inputs[0]); @@ -185,6 +207,9 @@ ErrorCode PoolBufExecution::SubgrouponResize(const std::vector &inputs if (mPoolType == PoolType_AVEPOOL) { buildOptions.emplace("-DPOOL_AVG"); } + if(returnRedice){ + buildOptions.emplace("-DRETURN_REDICE"); + } int input_line_size = mStrides[1] * (8 - 1) + mKernels[1]; buildOptions.emplace("-DINPUT_LINE_SIZE=" + std::to_string(input_line_size)); if (channels % 16 != 0) { @@ -221,6 +246,7 @@ ErrorCode PoolBufExecution::SubgrouponResize(const std::vector &inputs ret |= mKernel.setArg(idx++, sizeof(outputImageShape), outputImageShape); ret |= mKernel.setArg(idx++, sizeof(paddingShape), paddingShape); ret |= mKernel.setArg(idx++, openCLBuffer(output)); + ret |= mKernel.setArg(idx++, openCLBuffer(redice)); ret |= mKernel.setArg(idx++, channels); ret |= mKernel.setArg(idx++, in_channel_block); ret |= mKernel.setArg(idx++, out_channel_block); diff --git a/source/backend/opencl/execution/buffer/UnaryBufExecution.cpp b/source/backend/opencl/execution/buffer/UnaryBufExecution.cpp index 62fbc9ebd..9d113470f 100644 --- a/source/backend/opencl/execution/buffer/UnaryBufExecution.cpp +++ b/source/backend/opencl/execution/buffer/UnaryBufExecution.cpp @@ -23,6 +23,11 @@ ErrorCode UnaryBufExecution::onResize(const std::vector& inputs, const Tensor* output = outputs[0]; auto openCLBackend = static_cast(backend()); auto runtime = openCLBackend->getOpenCLRuntime(); + + auto dataType = inputs[0]->getType(); + if (dataType.code == halide_type_int){ + mBuildOptions.emplace("-DOPENCL_INPUT_INT"); + } #ifdef MNN_SUPPORT_INTEL_SUBGROUP if (runtime->isSupportedIntelSubgroup()) { return SubgrouponResize(inputs, outputs); diff --git a/source/backend/opencl/execution/cl/binary.cl b/source/backend/opencl/execution/cl/binary.cl index 79a6d119f..bee2f9d0b 100644 --- a/source/backend/opencl/execution/cl/binary.cl +++ b/source/backend/opencl/execution/cl/binary.cl @@ -15,6 +15,22 @@ __kernel void binary(__private int global_dim0, __private int global_dim1, FLOAT4 in0, in1; if (pos.x < global_dim0 && pos.y < global_dim1) { +#ifdef OPENCL_INPUT_INT + if(isFull.x == 0) { + in0 = CONVERT_FLOAT4(convert_int4(RI_F(input0, SAMPLER, (int2)(0, 0)))); + in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); + } else { + in0 = CONVERT_FLOAT4(convert_int4(RI_F(input0, SAMPLER, pos))); + } + if(isFull.y == 0) { + in1 = CONVERT_FLOAT4(convert_int4(RI_F(input1, SAMPLER, (int2)(0, 0)))); + in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); + } else { + in1 = CONVERT_FLOAT4(convert_int4(RI_F(input1, SAMPLER, pos))); + } + + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else if(isFull.x == 0) { in0 = RI_F(input0, SAMPLER, (int2)(0, 0)); in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); @@ -29,6 +45,7 @@ __kernel void binary(__private int global_dim0, __private int global_dim1, } FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -43,9 +60,16 @@ __kernel void binary_prelu(__read_only image2d_t input0, __read_only image2d_t i if (nhwc.x < shape.x && nhwc.w < shape.w) { int4 nhwc1 = nhwc * input1NHWCStep; int2 pos1 = (int2)(nhwc1.w*whInput1.x+nhwc1.z, nhwc1.x*whInput1.y+nhwc1.y); + +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(RI_F(input0, SAMPLER, pos))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(RI_F(input1, SAMPLER, pos1))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in0 = RI_F(input0, SAMPLER, pos); FLOAT4 in1 = RI_F(input1, SAMPLER, pos1); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif WI_F(output, pos, out); } } diff --git a/source/backend/opencl/execution/cl/binary_buf.cl b/source/backend/opencl/execution/cl/binary_buf.cl index 033bd4ca3..271564881 100644 --- a/source/backend/opencl/execution/cl/binary_buf.cl +++ b/source/backend/opencl/execution/cl/binary_buf.cl @@ -12,15 +12,24 @@ __kernel void binary_buf(__private int global_dim0, __private int global_dim1, if (pos.x < global_dim0 && pos.y < global_dim1) { int offset = pos.x * (shape.y*shape.z) + pos.y; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(offset*isFull.x, input0))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(offset*isFull.y, input1))); +#else FLOAT4 in0 = vload4(offset*isFull.x, input0); FLOAT4 in1 = vload4(offset*isFull.y, input1); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -37,9 +46,15 @@ __kernel void prelu_buf(__private int global_dim0, __private int global_dim1, if (pos.x < global_dim0 && pos.y < global_dim1) { int offset = pos.x * (shape.y*shape.z) + pos.y; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(offset, input0))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(pos.x % shape.w, input1))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in0 = vload4(offset, input0); FLOAT4 in1 = vload4(pos.x % shape.w, input1); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, offset, output); } } diff --git a/source/backend/opencl/execution/cl/binary_subgroup_buf.cl b/source/backend/opencl/execution/cl/binary_subgroup_buf.cl index cffc286a8..d1cc04ae6 100644 --- a/source/backend/opencl/execution/cl/binary_subgroup_buf.cl +++ b/source/backend/opencl/execution/cl/binary_subgroup_buf.cl @@ -20,15 +20,24 @@ __kernel void binary_buf_c4_c4_c4(__private int global_dim0, __private int globa const int offset = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -57,15 +66,24 @@ __kernel void binary_buf_c4_c4_c16(__private int global_dim0, __private int glob const int offset = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; const int dst_offset = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16 + (channel_idx % 4) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -103,16 +121,25 @@ __kernel void binary_buf_c4_c16_c4(__private int global_dim0, __private int glob const int offset0 = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; const int offset1 = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*src_width+w_idx+input1_pad_left) * 16 + (channel_idx % 4) * 4; - + +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset0*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset1*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -141,15 +168,24 @@ __kernel void binary_buf_c16_c4_c4(__private int global_dim0, __private int glob const int offset1 = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; const int offset0 = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*src_width+w_idx+input0_pad_left) * 16 + (channel_idx % 4) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset0*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset1*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -180,15 +216,24 @@ __kernel void binary_buf_c4_c16_c16(__private int global_dim0, __private int glo const int offset1 = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*src_width+w_idx+input1_pad_left) * 16 + (channel_idx % 4) * 4; const int dst_offset = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16 + (channel_idx % 4) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset0*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset1*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -228,16 +273,25 @@ __kernel void binary_buf_c16_c4_c16(__private int global_dim0, __private int glo const int offset1 = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; const int offset0 = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*src_width+w_idx+input0_pad_left) * 16 + (channel_idx % 4) * 4; const int dst_offset = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16 + (channel_idx % 4) * 4; - + +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0*isFull.x))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1*isFull.y))); +#else FLOAT4 in0 = vload4(0, input0 + offset0*isFull.x); FLOAT4 in1 = vload4(0, input1 + offset1*isFull.y); +#endif if(isFull.x == 0) { in0 = (FLOAT4)(in0.x, in0.x, in0.x, in0.x); } if(isFull.y == 0) { in1 = (FLOAT4)(in1.x, in1.x, in1.x, in1.x); } +#ifdef OPENCL_INPUT_INT + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -272,10 +326,15 @@ __kernel void prelu_buf_c4_c4(__private int global_dim0, __private int global_di const int offset0 = (((batch_idx*channel4+channel_idx)*shape.y+h_idx)*shape.z+w_idx) * 4; const int offset1 = channel_idx * 4; - +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in0 = vload4(0, input0 + offset0); FLOAT4 in1 = vload4(0, input1 + offset1); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, 0, output + offset0); } @@ -300,9 +359,15 @@ __kernel void prelu_buf_c4_c16(__private int global_dim0, __private int global_d const int offset1 = channel_idx * 4; const int offset = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16 + (channel_idx % 4) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(vload4(0, input0 + offset0))); + FLOAT4 in1 = CONVERT_FLOAT4(convert_int4(vload4(0, input1 + offset1))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in0 = vload4(0, input0 + offset0); FLOAT4 in1 = vload4(0, input1 + offset1); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, 0, output + offset); if(w_idx == 0){ int pad_offset = (((batch_idx*channel16+channe_out_idx)*shape.y+h_idx)*dst_width) * 16 + (channel_idx % 4) * 4; @@ -336,6 +401,16 @@ __kernel void prelu_buf_c16_c16(__private int global_dim0, __private int global_ const int offset1 = channel_idx * 16; const int offset = (((batch_idx*channel16+channel_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16; +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))))); + FLOAT4 in1 = (FLOAT4)(convert_int(as_half(intel_sub_group_block_read_us((__global ushort*)(input1 + offset1))))); +#else + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input0 + offset0))))); + FLOAT4 in1 = (FLOAT4)(convert_int(as_float(intel_sub_group_block_read((__global uint*)(input1 + offset1))))); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in0 = as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))); FLOAT4 in1 = (FLOAT4)(as_half(intel_sub_group_block_read_us((__global ushort*)(input1 + offset1)))); @@ -345,6 +420,7 @@ __kernel void prelu_buf_c16_c16(__private int global_dim0, __private int global_ #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif { if (w_idx + 4 > shape.z) { for (int i = 0; i < shape.z % 4; i++) { @@ -391,6 +467,16 @@ __kernel void prelu_buf_c16_c4(__private int global_dim0, __private int global_d const int offset1 = channel_idx * 16; const int offset = (((batch_idx*channel4+(channel_idx<<2))*shape.y+h_idx)*shape.z+w_idx) * 4; +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))))); + FLOAT4 in1 = (FLOAT4)(convert_int(as_half(intel_sub_group_block_read_us((__global ushort*)(input1 + offset1))))); +#else + FLOAT4 in0 = CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input0 + offset0))))); + FLOAT4 in1 = (FLOAT4)(convert_int(as_float(intel_sub_group_block_read((__global uint*)(input1 + offset1))))); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in0 = as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))); FLOAT4 in1 = (FLOAT4)(as_half(intel_sub_group_block_read_us((__global ushort*)(input1 + offset1)))); @@ -400,6 +486,7 @@ __kernel void prelu_buf_c16_c4(__private int global_dim0, __private int global_d #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif const int lid_x = sglid % 4; const int lid_y = sglid / 4; @@ -435,6 +522,16 @@ __kernel void binary_buf_c16_c16_c16(__private int global_dim0, __private int gl const int offset1 = (((batch_idx*channel16+channel_idx)*shape.y+h_idx)*src1_width+w_idx+input1_pad_left) * 16; const int offset = (((batch_idx*channel16+channel_idx)*shape.y+h_idx)*dst_width+w_idx+output_pad_left) * 16; +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in0 = isFull.x ? CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))))) : (FLOAT4)(convert_int(input0[0])); + FLOAT4 in1 = isFull.y ? CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input1 + offset1))))) : (FLOAT4)(convert_int(input1[0])); +#else + FLOAT4 in0 = isFull.x ? CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input0 + offset0))))) : (FLOAT4)(convert_int(input0[0])); + FLOAT4 in1 = isFull.y ? CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input1 + offset1))))) : (FLOAT4)(convert_int(input1[0])); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in0 = isFull.x ? as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))) : (FLOAT4)(input0[0]); FLOAT4 in1 = isFull.y ? as_half4(intel_sub_group_block_read_us4((__global ushort*)(input1 + offset1))) : (FLOAT4)(input1[0]); @@ -444,6 +541,7 @@ __kernel void binary_buf_c16_c16_c16(__private int global_dim0, __private int gl #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } @@ -498,6 +596,16 @@ __kernel void binary_buf_c16_c16_c4(__private int global_dim0, __private int glo const int offset1 = (((batch_idx*channel16+channel_idx)*shape.y+h_idx)*src1_width+w_idx+input1_pad_left) * 16; const int offset = (((batch_idx*channel4+(channel_idx << 2))*shape.y+h_idx)*shape.z+w_idx) * 4; +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in0 = isFull.x ? CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))))) : (FLOAT4)(convert_int(input0[0])); + FLOAT4 in1 = isFull.y ? CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input1 + offset1))))) : (FLOAT4)(convert_int(input1[0])); +#else + FLOAT4 in0 = isFull.x ? CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input0 + offset0))))) : (FLOAT4)(convert_int(input0[0])); + FLOAT4 in1 = isFull.y ? CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input1 + offset1))))) : (FLOAT4)(convert_int(input1[0])); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in0 = isFull.x ? as_half4(intel_sub_group_block_read_us4((__global ushort*)(input0 + offset0))) : (FLOAT4)(input0[0]); FLOAT4 in1 = isFull.y ? as_half4(intel_sub_group_block_read_us4((__global ushort*)(input1 + offset1))) : (FLOAT4)(input1[0]); @@ -507,6 +615,7 @@ __kernel void binary_buf_c16_c16_c4(__private int global_dim0, __private int glo #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if(activationType == 1) { out = fmax(out, (FLOAT4)0); } diff --git a/source/backend/opencl/execution/cl/depthwise_deconv2d.cl b/source/backend/opencl/execution/cl/depthwise_deconv2d.cl index f01857332..42abb8687 100644 --- a/source/backend/opencl/execution/cl/depthwise_deconv2d.cl +++ b/source/backend/opencl/execution/cl/depthwise_deconv2d.cl @@ -45,15 +45,12 @@ __kernel void depthwise_deconv2d(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, const int out_batch_idx = out_batch_height_idx / output_shape.x; const int out_height_idx = out_batch_height_idx % output_shape.x; - - const int out_width_fill_idx = out_width_idx - (stride_shape.y - 1); - const int out_height_fill_idx = out_height_idx - (stride_shape.x - 1); - int kernel_start_x = (out_width_fill_idx + align_shape.y) / stride_shape.y; - int kernel_start_y = (out_height_fill_idx + align_shape.x) / stride_shape.x; + int kernel_start_x = (out_width_idx + align_shape.y) / stride_shape.y; + int kernel_start_y = (out_height_idx + align_shape.x) / stride_shape.x; - int deal_kernel_width = kernel_shape.y - mad24(kernel_start_x, stride_shape.y, padding_shape.y) + out_width_fill_idx - 1; - int deal_kernel_height = kernel_shape.x - mad24(kernel_start_y, stride_shape.x, padding_shape.x) + out_height_fill_idx - 1; + int deal_kernel_width = kernel_shape.y - mad24(kernel_start_x, stride_shape.y, padding_shape.y) + out_width_idx - 1; + int deal_kernel_height = kernel_shape.x - mad24(kernel_start_y, stride_shape.x, padding_shape.x) + out_height_idx - 1; int kernel_image_x; float4 in0; diff --git a/source/backend/opencl/execution/cl/matmul_buf.cl b/source/backend/opencl/execution/cl/matmul_buf.cl index 46d0fe1e5..a0e9413fd 100644 --- a/source/backend/opencl/execution/cl/matmul_buf.cl +++ b/source/backend/opencl/execution/cl/matmul_buf.cl @@ -18,13 +18,15 @@ __kernel void matmul_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a, __global FLOAT* output_c, __private const int channels, __private const int channel_blocks, - __private const int width_blocks) { + __private const int width_blocks, + __private const int width) { const int width_blocks_idx = get_global_id(0);// output W const int height_idx = get_global_id(1);// output H DEAL_NON_UNIFORM_DIM2(width_blocks_idx, height_idx); FLOAT4 a; FLOAT4 b0 = 0, b1 = 0, b2 = 0, b3 = 0; + FLOAT4 v_zero = (FLOAT4)((FLOAT)0.0); #ifdef BIAS FLOAT4 temp = vload4(width_blocks_idx, input_c); @@ -40,26 +42,48 @@ __kernel void matmul_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a, FLOAT result3 = 0; #endif - for (short pos = 0; pos < channel_blocks; pos += 1) { + const int remain = channel_blocks*4 - channels; + for (short pos = 0; pos < channel_blocks - 1; pos += 1) { const int inpa_offset = height_idx * channel_blocks + pos; a = vload4(inpa_offset, input_a); - short remain = (pos + 1) * 4 - channels; const int inpb_offset = (pos*4) * width_blocks + width_blocks_idx; b0 = vload4(inpb_offset, input_b); b1 = vload4(inpb_offset + width_blocks, input_b); b2 = vload4(inpb_offset + width_blocks*2, input_b); b3 = vload4(inpb_offset + width_blocks*3, input_b); + + FLOAT4 btmp0 = (FLOAT4)(b0.s0, b1.s0, b2.s0, b3.s0); + FLOAT4 btmp1 = (FLOAT4)(b0.s1, b1.s1, b2.s1, b3.s1); + FLOAT4 btmp2 = (FLOAT4)(b0.s2, b1.s2, b2.s2, b3.s2); + FLOAT4 btmp3 = (FLOAT4)(b0.s3, b1.s3, b2.s3, b3.s3); + + result0 += dot(a, btmp0); + result1 += dot(a, btmp1); + result2 += dot(a, btmp2); + result3 += dot(a, btmp3); + } + + { + const int inpa_offset = height_idx * channel_blocks + channel_blocks - 1; + a = vload4(inpa_offset, input_a); + + const int inpb_offset = ((channel_blocks - 1)*4) * width_blocks + width_blocks_idx; + + b0 = vload4(inpb_offset, input_b); + b1 = ((remain >= 3) ? v_zero : vload4(inpb_offset + width_blocks, input_b)); + b2 = ((remain >= 2) ? v_zero : vload4(inpb_offset + width_blocks*2, input_b)); + b3 = ((remain >= 1) ? v_zero : vload4(inpb_offset + width_blocks*3, input_b)); if (remain == 3) { - b1 = 0; - b2 = 0; - b3 = 0; + a.y = 0; + a.z = 0; + a.w = 0; } else if (remain == 2) { - b2 = 0; - b3 = 0; + a.z = 0; + a.w = 0; } else if (remain == 1) { - b3 = 0; + a.w = 0;; } FLOAT4 btmp0 = (FLOAT4)(b0.s0, b1.s0, b2.s0, b3.s0); @@ -85,13 +109,15 @@ __kernel void matmul_transB_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a __global FLOAT* output_c, __private const int channels, __private const int channel_blocks, - __private const int width_blocks) { + __private const int width_blocks, + __private const int width) { const int width_blocks_idx = get_global_id(0); const int height_idx = get_global_id(1); DEAL_NON_UNIFORM_DIM2(width_blocks_idx, height_idx); FLOAT4 a; FLOAT4 b0 = 0, b1 = 0, b2 = 0, b3 = 0; + FLOAT4 v_zero = (FLOAT4)((FLOAT)0.0); #ifdef BIAS FLOAT4 temp = vload4(width_blocks_idx, input_c); @@ -106,26 +132,44 @@ __kernel void matmul_transB_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a FLOAT result3 = 0; #endif - for (short pos = 0; pos < channel_blocks; pos += 1) { + const int remaina = channel_blocks*4 - channels; + const int remainb = (width_blocks_idx+1)*4 - width; + for (short pos = 0; pos < channel_blocks - 1; pos += 1) { const int inpa_offset = height_idx * channel_blocks + pos; a = vload4(inpa_offset, input_a); - short remain = (pos + 1) * 4 - channels; const int inpb_offset = (width_blocks_idx*4) * channel_blocks + pos; b0 = vload4(inpb_offset, input_b); - b1 = vload4(inpb_offset + channel_blocks, input_b); - b2 = vload4(inpb_offset + channel_blocks*2, input_b); - b3 = vload4(inpb_offset + channel_blocks*3, input_b); + b1 = ((remainb >= 3) ? v_zero : vload4(inpb_offset + channel_blocks, input_b)); + b2 = ((remainb >= 2) ? v_zero : vload4(inpb_offset + channel_blocks*2, input_b)); + b3 = ((remainb >= 1) ? v_zero : vload4(inpb_offset + channel_blocks*3, input_b)); - if (remain == 3) { + result0 += dot(a, b0); + result1 += dot(a, b1); + result2 += dot(a, b2); + result3 += dot(a, b3); + } + + { + const int inpa_offset = height_idx * channel_blocks + channel_blocks - 1; + a = vload4(inpa_offset, input_a); + + const int inpb_offset = (width_blocks_idx*4) * channel_blocks + channel_blocks - 1; + + b0 = vload4(inpb_offset, input_b); + b1 = ((remainb >= 3) ? v_zero : vload4(inpb_offset + channel_blocks, input_b)); + b2 = ((remainb >= 2) ? v_zero : vload4(inpb_offset + channel_blocks*2, input_b)); + b3 = ((remainb >= 1) ? v_zero : vload4(inpb_offset + channel_blocks*3, input_b)); + + if (remaina == 3) { a.y = 0; a.z = 0; a.w = 0; - } else if (remain == 2) { + } else if (remaina == 2) { a.z = 0; a.w = 0; - } else if (remain == 1) { + } else if (remaina == 1) { a.w = 0; } @@ -149,7 +193,8 @@ __kernel void matmul_transA_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a __private const int channel_blocks, __private const int height, __private const int height_blocks, - __private const int width_blocks) { + __private const int width_blocks, + __private const int width) { const int width_blocks_idx = get_global_id(0); const int height_blocks_idx = get_global_id(1); @@ -168,14 +213,14 @@ __kernel void matmul_transA_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a FLOAT4 result3 = 0; #endif - for (short pos = 0; pos < channel_blocks; pos += 1) { + const int remain = channel_blocks*4 - channels; + for (short pos = 0; pos < channel_blocks - 1; pos += 1) { const int inpa_offset = (4*pos) * height_blocks + height_blocks_idx; - short remain = (pos + 1) * 4 - channels; FLOAT4 a0 = vload4(inpa_offset, input_a); - FLOAT4 a1 = ((remain >= 3) ? v_zero : vload4(inpa_offset + height_blocks, input_a)); - FLOAT4 a2 = ((remain >= 2) ? v_zero : vload4(inpa_offset + height_blocks*2, input_a)); - FLOAT4 a3 = ((remain >= 1) ? v_zero : vload4(inpa_offset + height_blocks*3, input_a)); + FLOAT4 a1 = vload4(inpa_offset + height_blocks, input_a); + FLOAT4 a2 = vload4(inpa_offset + height_blocks*2, input_a); + FLOAT4 a3 = vload4(inpa_offset + height_blocks*3, input_a); const int inpb_offset = (4*pos) * width_blocks + width_blocks_idx; FLOAT4 b0 = vload4(inpb_offset, input_b); @@ -214,6 +259,52 @@ __kernel void matmul_transA_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* input_a result3.z += dot(a3_trans, b2_trans); result3.w += dot(a3_trans, b3_trans); } + + { + const int inpa_offset = (4*(channel_blocks - 1)) * height_blocks + height_blocks_idx; + FLOAT4 a0 = vload4(inpa_offset, input_a); + FLOAT4 a1 = ((remain >= 3) ? v_zero : vload4(inpa_offset + height_blocks, input_a)); + FLOAT4 a2 = ((remain >= 2) ? v_zero : vload4(inpa_offset + height_blocks*2, input_a)); + FLOAT4 a3 = ((remain >= 1) ? v_zero : vload4(inpa_offset + height_blocks*3, input_a)); + + const int inpb_offset = (4*(channel_blocks - 1)) * width_blocks + width_blocks_idx; + FLOAT4 b0 = vload4(inpb_offset, input_b); + FLOAT4 b1 = ((remain >= 3) ? v_zero : vload4(inpb_offset + width_blocks, input_b)); + FLOAT4 b2 = ((remain >= 3) ? v_zero : vload4(inpb_offset + width_blocks*2, input_b)); + FLOAT4 b3 = ((remain >= 3) ? v_zero : vload4(inpb_offset + width_blocks*3, input_b)); + + FLOAT4 a0_trans = (FLOAT4)(a0.x, a1.x, a2.x, a3.x); + FLOAT4 a1_trans = (FLOAT4)(a0.y, a1.y, a2.y, a3.y); + FLOAT4 a2_trans = (FLOAT4)(a0.z, a1.z, a2.z, a3.z); + FLOAT4 a3_trans = (FLOAT4)(a0.w, a1.w, a2.w, a3.w); + + FLOAT4 b0_trans = (FLOAT4)(b0.x, b1.x, b2.x, b3.x); + FLOAT4 b1_trans = (FLOAT4)(b0.y, b1.y, b2.y, b3.y); + FLOAT4 b2_trans = (FLOAT4)(b0.z, b1.z, b2.z, b3.z); + FLOAT4 b3_trans = (FLOAT4)(b0.w, b1.w, b2.w, b3.w); + + //matmul + result0.x += dot(a0_trans, b0_trans); + result0.y += dot(a0_trans, b1_trans); + result0.z += dot(a0_trans, b2_trans); + result0.w += dot(a0_trans, b3_trans); + + result1.x += dot(a1_trans, b0_trans); + result1.y += dot(a1_trans, b1_trans); + result1.z += dot(a1_trans, b2_trans); + result1.w += dot(a1_trans, b3_trans); + + result2.x += dot(a2_trans, b0_trans); + result2.y += dot(a2_trans, b1_trans); + result2.z += dot(a2_trans, b2_trans); + result2.w += dot(a2_trans, b3_trans); + + result3.x += dot(a3_trans, b0_trans); + result3.y += dot(a3_trans, b1_trans); + result3.z += dot(a3_trans, b2_trans); + result3.w += dot(a3_trans, b3_trans); + } + const int out_offset = (4*height_blocks_idx) * width_blocks + width_blocks_idx; vstore4(result0, out_offset, output_c); @@ -235,7 +326,8 @@ __kernel void matmul_transA_transB_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* __private const int channel_blocks, __private const int height, __private const int height_blocks, - __private const int width_blocks) { + __private const int width_blocks, + __private const int width) { const int width_blocks_idx = get_global_id(0); const int height_blocks_idx = get_global_id(1); @@ -254,20 +346,61 @@ __kernel void matmul_transA_transB_buf(GLOBAL_SIZE_2_DIMS __global const FLOAT* FLOAT4 result2 = 0; FLOAT4 result3 = 0; #endif - - for (short pos = 0; pos < channel_blocks; pos += 1) { + + const int remaina = channel_blocks * 4 - channels; + const int remainb = (width_blocks_idx + 1) * 4 - width; + for (short pos = 0; pos < channel_blocks - 1; pos += 1) { const int inpa_offset = (4*pos) * height_blocks + height_blocks_idx; - short remain = (pos + 1) * 4 - channels; FLOAT4 a0 = vload4(inpa_offset, input_a); - FLOAT4 a1 = ((remain >= 3) ? v_zero : vload4(inpa_offset + height_blocks, input_a)); - FLOAT4 a2 = ((remain >= 2) ? v_zero : vload4(inpa_offset + height_blocks*2, input_a)); - FLOAT4 a3 = ((remain >= 1) ? v_zero : vload4(inpa_offset + height_blocks*3, input_a)); + FLOAT4 a1 = vload4(inpa_offset + height_blocks, input_a); + FLOAT4 a2 = vload4(inpa_offset + height_blocks*2, input_a); + FLOAT4 a3 = vload4(inpa_offset + height_blocks*3, input_a); const int inpb_offset = (4*width_blocks_idx) * channel_blocks + pos; FLOAT4 b0 = vload4(inpb_offset, input_b); - FLOAT4 b1 = vload4(inpb_offset + channel_blocks, input_b); - FLOAT4 b2 = vload4(inpb_offset + channel_blocks*2, input_b); - FLOAT4 b3 = vload4(inpb_offset + channel_blocks*3, input_b); + FLOAT4 b1 = ((remainb >= 3) ? v_zero : vload4(inpb_offset + channel_blocks, input_b)); + FLOAT4 b2 = ((remainb >= 2) ? v_zero : vload4(inpb_offset + channel_blocks*2, input_b)); + FLOAT4 b3 = ((remainb >= 1) ? v_zero : vload4(inpb_offset + channel_blocks*3, input_b)); + + FLOAT4 a0_trans = (FLOAT4)(a0.x, a1.x, a2.x, a3.x); + FLOAT4 a1_trans = (FLOAT4)(a0.y, a1.y, a2.y, a3.y); + FLOAT4 a2_trans = (FLOAT4)(a0.z, a1.z, a2.z, a3.z); + FLOAT4 a3_trans = (FLOAT4)(a0.w, a1.w, a2.w, a3.w); + + //matmul + result0.x += dot(a0_trans, b0); + result0.y += dot(a0_trans, b1); + result0.z += dot(a0_trans, b2); + result0.w += dot(a0_trans, b3); + + result1.x += dot(a1_trans, b0); + result1.y += dot(a1_trans, b1); + result1.z += dot(a1_trans, b2); + result1.w += dot(a1_trans, b3); + + result2.x += dot(a2_trans, b0); + result2.y += dot(a2_trans, b1); + result2.z += dot(a2_trans, b2); + result2.w += dot(a2_trans, b3); + + result3.x += dot(a3_trans, b0); + result3.y += dot(a3_trans, b1); + result3.z += dot(a3_trans, b2); + result3.w += dot(a3_trans, b3); + } + + { + const int inpa_offset = (4*(channel_blocks-1)) * height_blocks + height_blocks_idx; + FLOAT4 a0 = vload4(inpa_offset, input_a); + FLOAT4 a1 = ((remaina >= 3) ? v_zero : vload4(inpa_offset + height_blocks, input_a)); + FLOAT4 a2 = ((remaina >= 2) ? v_zero : vload4(inpa_offset + height_blocks*2, input_a)); + FLOAT4 a3 = ((remaina >= 1) ? v_zero : vload4(inpa_offset + height_blocks*3, input_a)); + + const int inpb_offset = (4*width_blocks_idx) * channel_blocks + channel_blocks-1; + FLOAT4 b0 = vload4(inpb_offset, input_b); + FLOAT4 b1 = ((remainb >= 3) ? v_zero : vload4(inpb_offset + channel_blocks, input_b)); + FLOAT4 b2 = ((remainb >= 2) ? v_zero : vload4(inpb_offset + channel_blocks*2, input_b)); + FLOAT4 b3 = ((remainb >= 1) ? v_zero : vload4(inpb_offset + channel_blocks*3, input_b)); FLOAT4 a0_trans = (FLOAT4)(a0.x, a1.x, a2.x, a3.x); FLOAT4 a1_trans = (FLOAT4)(a0.y, a1.y, a2.y, a3.y); diff --git a/source/backend/opencl/execution/cl/opencl_program.cc b/source/backend/opencl/execution/cl/opencl_program.cc index 4d0e0a698..19a7c3632 100644 --- a/source/backend/opencl/execution/cl/opencl_program.cc +++ b/source/backend/opencl/execution/cl/opencl_program.cc @@ -16,7 +16,7 @@ extern const std::map> OpenCLProgramMap }, { "unary", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x70,0x6f,0x73,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x77,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x70,0x6f,0x73,0x2c,0x20,0x68,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x70,0x6f,0x73,0x2c,0x20,0x68,0x62,0x29,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x70,0x6f,0x73,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x77,0x29,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x70,0x6f,0x73,0x2c,0x20,0x68,0x62,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x70,0x6f,0x73,0x2c,0x20,0x68,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x70,0x6f,0x73,0x2c,0x20,0x68,0x62,0x29,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x7d,0xa, } }, #ifndef MNN_OPENCL_BUFFER_CLOSED { @@ -71,7 +71,7 @@ extern const std::map> OpenCLProgramMap #ifndef MNN_OPENCL_BUFFER_CLOSED { "binary_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x50,0x49,0x20,0x33,0x2e,0x31,0x34,0x31,0x35,0x39,0x32,0x36,0x35,0x33,0x35,0x38,0x39,0x66,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x4e,0x43,0x34,0x2c,0x20,0x48,0x57,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x70,0x6f,0x73,0x2e,0x78,0x20,0x2a,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x2b,0x20,0x70,0x6f,0x73,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x4e,0x43,0x34,0x2c,0x20,0x48,0x57,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x70,0x6f,0x73,0x2e,0x78,0x20,0x2a,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x2b,0x20,0x70,0x6f,0x73,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x50,0x49,0x20,0x33,0x2e,0x31,0x34,0x31,0x35,0x39,0x32,0x36,0x35,0x33,0x35,0x38,0x39,0x66,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x4e,0x43,0x34,0x2c,0x20,0x48,0x57,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x70,0x6f,0x73,0x2e,0x78,0x20,0x2a,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x2b,0x20,0x70,0x6f,0x73,0x2e,0x79,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x4e,0x43,0x34,0x2c,0x20,0x48,0x57,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x70,0x6f,0x73,0x2e,0x78,0x20,0x2a,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x2b,0x20,0x70,0x6f,0x73,0x2e,0x79,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } }, #endif #ifndef MNN_OPENCL_BUFFER_CLOSED @@ -84,7 +84,7 @@ extern const std::map> OpenCLProgramMap #ifdef MNN_SUPPORT_INTEL_SUBGROUP { "binary_subgroup_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2a,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x3c,0x3c,0x32,0x29,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x3f,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2a,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3c,0x3c,0x20,0x32,0x29,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x3f,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x2a,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0xa,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,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x72,0x65,0x6c,0x75,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x5d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2a,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x3c,0x3c,0x32,0x29,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x3f,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,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,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x20,0x3d,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x25,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x20,0x2f,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x70,0x61,0x63,0x6b,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x2a,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x30,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x30,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x31,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x5f,0x69,0x64,0x78,0x2b,0x69,0x6e,0x70,0x75,0x74,0x31,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3c,0x3c,0x20,0x32,0x29,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2b,0x77,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x30,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3f,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x2b,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x31,0x29,0x29,0x29,0x20,0x3a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x5b,0x30,0x5d,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x3f,0x20,0x28,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } }, #endif #endif @@ -104,14 +104,14 @@ extern const std::map> OpenCLProgramMap #ifdef MNN_SUPPORT_INTEL_SUBGROUP { "pooling_subgroup_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x20,0x3d,0x20,0x28,0x63,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x2f,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x2f,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x30,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x33,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x32,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x33,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x34,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x35,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x36,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x37,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x28,0x77,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x62,0x5f,0x69,0x64,0x78,0x2c,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0x63,0x5f,0x69,0x64,0x78,0x29,0x2c,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2c,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x29,0x2a,0x31,0x36,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x73,0x72,0x63,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x6f,0x70,0x65,0x6e,0x63,0x6c,0x5f,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x5f,0x68,0x69,0x6e,0x74,0x28,0x38,0x29,0x29,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x5d,0x3b,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x5f,0x79,0x29,0x7b,0xa,0x2f,0x2f,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x2a,0x31,0x36,0x29,0x29,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6c,0x73,0x65,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x2a,0x31,0x36,0x29,0x29,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x2f,0x2f,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6c,0x73,0x65,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x73,0x72,0x63,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x73,0x72,0x63,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x30,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x69,0x2a,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x69,0x2a,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x31,0x36,0x3b,0xa,0x23,0x69,0x66,0x20,0x4f,0x55,0x54,0x50,0x55,0x54,0x5f,0x4c,0x45,0x46,0x54,0x4f,0x56,0x45,0x52,0x53,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x31,0x36,0x20,0x3e,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2a,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x38,0x20,0x3c,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x38,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x38,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x38,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x38,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x25,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x33,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x32,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x33,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x34,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x35,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x36,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x37,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x28,0x77,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x62,0x5f,0x69,0x64,0x78,0x2c,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0x63,0x5f,0x69,0x64,0x78,0x29,0x2c,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2c,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x29,0x2a,0x31,0x36,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x73,0x72,0x63,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x6f,0x70,0x65,0x6e,0x63,0x6c,0x5f,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x5f,0x68,0x69,0x6e,0x74,0x28,0x38,0x29,0x29,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x5d,0x3b,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x5f,0x79,0x29,0x7b,0xa,0x2f,0x2f,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x2a,0x31,0x36,0x29,0x29,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6c,0x73,0x65,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x28,0x6b,0x68,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x29,0x2a,0x31,0x36,0x29,0x29,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x2f,0x2f,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6c,0x73,0x65,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x2f,0x2f,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x2f,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x73,0x72,0x63,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x73,0x72,0x63,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x20,0x4f,0x55,0x54,0x50,0x55,0x54,0x5f,0x4c,0x45,0x46,0x54,0x4f,0x56,0x45,0x52,0x53,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x31,0x36,0x20,0x3e,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2a,0x31,0x36,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3e,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x72,0x65,0x64,0x69,0x63,0x65,0x29,0x2c,0x20,0x20,0x30,0x2c,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2b,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3e,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x20,0x3d,0x20,0x28,0x63,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x2f,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x72,0x65,0x64,0x69,0x63,0x65,0x29,0x2c,0x20,0x20,0x30,0x2c,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2b,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x2f,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x30,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x63,0x5f,0x6c,0x65,0x66,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x33,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x32,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x33,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x34,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x35,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x36,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x37,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x28,0x77,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x38,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x38,0x29,0x30,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x62,0x5f,0x69,0x64,0x78,0x2c,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0x63,0x5f,0x69,0x64,0x78,0x29,0x2c,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2c,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x73,0x72,0x63,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x6f,0x70,0x65,0x6e,0x63,0x6c,0x5f,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x5f,0x68,0x69,0x6e,0x74,0x28,0x38,0x29,0x29,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x73,0x72,0x63,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x73,0x72,0x63,0x20,0x3e,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x38,0x29,0x28,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x73,0x72,0x63,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x30,0x29,0x2a,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x69,0x2a,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x69,0x2a,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x31,0x36,0x3b,0xa,0x23,0x69,0x66,0x20,0x4f,0x55,0x54,0x50,0x55,0x54,0x5f,0x4c,0x45,0x46,0x54,0x4f,0x56,0x45,0x52,0x53,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x31,0x36,0x20,0x3e,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2a,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x38,0x20,0x3c,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x38,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x38,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x38,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x38,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x65,0x6c,0x73,0x65,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x25,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x3b,0xa,0x23,0x69,0x66,0x20,0x4f,0x55,0x54,0x50,0x55,0x54,0x5f,0x4c,0x45,0x46,0x54,0x4f,0x56,0x45,0x52,0x53,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x31,0x36,0x20,0x3e,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2a,0x31,0x36,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x5b,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x5b,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x33,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x59,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x32,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x33,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x34,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x35,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x36,0x2c,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x20,0x2a,0x20,0x37,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x77,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x2c,0x20,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x68,0x5f,0x65,0x6e,0x64,0x20,0x3d,0x20,0x66,0x6d,0x69,0x6e,0x28,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x29,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x28,0x77,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x38,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x38,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x38,0x29,0x30,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x62,0x5f,0x69,0x64,0x78,0x2c,0x69,0x6e,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2c,0x63,0x5f,0x69,0x64,0x78,0x29,0x2c,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2c,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x59,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x49,0x4e,0x50,0x55,0x54,0x5f,0x4c,0x49,0x4e,0x45,0x5f,0x53,0x49,0x5a,0x45,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3e,0x3d,0x20,0x30,0x20,0x26,0x26,0x20,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x68,0x2c,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x69,0x29,0x2c,0x31,0x36,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x4b,0x45,0x52,0x4e,0x45,0x4c,0x5f,0x58,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x38,0x20,0x73,0x72,0x63,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x6f,0x70,0x65,0x6e,0x63,0x6c,0x5f,0x75,0x6e,0x72,0x6f,0x6c,0x6c,0x5f,0x68,0x69,0x6e,0x74,0x28,0x38,0x29,0x29,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x72,0x63,0x5b,0x69,0x5d,0x20,0x3d,0x20,0x6c,0x69,0x6e,0x65,0x5f,0x63,0x61,0x63,0x68,0x65,0x5b,0x6b,0x77,0x20,0x2b,0x20,0x53,0x54,0x52,0x49,0x44,0x45,0x5f,0x58,0x2a,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x73,0x72,0x63,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x73,0x72,0x63,0x20,0x3e,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x38,0x29,0x28,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x73,0x72,0x63,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x75,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x34,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x69,0x66,0x20,0x4f,0x55,0x54,0x50,0x55,0x54,0x5f,0x4c,0x45,0x46,0x54,0x4f,0x56,0x45,0x52,0x53,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x31,0x36,0x20,0x3e,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x63,0x5f,0x69,0x64,0x78,0x2a,0x31,0x36,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x29,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x5b,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5b,0x69,0x5d,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x38,0x20,0x26,0x26,0x20,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x29,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x5b,0x72,0x65,0x64,0x69,0x63,0x65,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x5b,0x69,0x5d,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } }, #endif #endif #ifndef MNN_OPENCL_BUFFER_CLOSED { "pooling_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x29,0x20,0x7b,0xa,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,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x62,0x5f,0x6f,0x68,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x77,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x43,0x4f,0x55,0x4e,0x54,0x5f,0x49,0x4e,0x43,0x4c,0x55,0x44,0x45,0x5f,0x50,0x41,0x44,0x44,0x49,0x4e,0x47,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x28,0x6d,0x69,0x6e,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x6d,0x69,0x6e,0x28,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x43,0x4f,0x55,0x4e,0x54,0x5f,0x49,0x4e,0x43,0x4c,0x55,0x44,0x45,0x5f,0x50,0x41,0x44,0x44,0x49,0x4e,0x47,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x2b,0x2b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x31,0x2e,0x30,0x2a,0x74,0x6f,0x74,0x61,0x6c,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x2b,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2b,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x68,0x3d,0x30,0x3b,0x20,0x6b,0x68,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x6b,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x68,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x6b,0x77,0x3d,0x30,0x3b,0x20,0x6b,0x77,0x3c,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x6b,0x77,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3d,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x77,0x5f,0x63,0x75,0x72,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x69,0x6e,0x70,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2b,0x28,0x6b,0x68,0x2a,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2b,0x6b,0x77,0x29,0x2a,0x34,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x20,0x3e,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x28,0x69,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x68,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x77,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x77,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x20,0x2b,0x20,0x63,0x5f,0x69,0x64,0x78,0x29,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x6f,0x68,0x5f,0x69,0x64,0x78,0x29,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x6f,0x77,0x5f,0x69,0x64,0x78,0x29,0x2a,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x72,0x65,0x64,0x69,0x63,0x65,0x29,0x2c,0x20,0x20,0x30,0x2c,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x7d,0xa, } }, #endif { @@ -121,7 +121,7 @@ extern const std::map> OpenCLProgramMap #ifndef MNN_OPENCL_BUFFER_CLOSED { "unary_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa, } }, #endif #ifndef MNN_OPENCL_BUFFER_CLOSED @@ -186,7 +186,7 @@ extern const std::map> OpenCLProgramMap #ifdef MNN_SUPPORT_INTEL_SUBGROUP { "unary_subgroup_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2b,0x77,0x69,0x64,0x74,0x68,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x3c,0x3c,0x32,0x29,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2a,0x20,0x34,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3f,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x65,0x6c,0x75,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x30,0x2e,0x37,0x39,0x37,0x38,0x38,0x34,0x35,0x38,0x66,0x20,0x2a,0x20,0x28,0x30,0x2e,0x30,0x34,0x34,0x37,0x31,0x35,0x66,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2b,0x20,0x69,0x6e,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x78,0x32,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x76,0x61,0x6c,0x75,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x64,0x73,0x74,0x20,0x3d,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3e,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x3c,0x3d,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x35,0x2e,0x30,0x66,0x20,0x3f,0x20,0x2d,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x31,0x2e,0x30,0x66,0x20,0x3a,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x76,0x61,0x6c,0x75,0x65,0x20,0x2a,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x31,0x37,0x33,0x32,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x37,0x38,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x29,0x29,0x29,0x29,0x20,0x2f,0x20,0x28,0x31,0x33,0x35,0x31,0x33,0x35,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x36,0x32,0x33,0x37,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x28,0x33,0x31,0x35,0x30,0x2e,0x30,0x66,0x20,0x2b,0x20,0x78,0x32,0x20,0x2a,0x20,0x32,0x38,0x2e,0x30,0x66,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2b,0x20,0x64,0x73,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x20,0x2a,0x20,0x30,0x2e,0x35,0x66,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x34,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x2c,0x20,0x77,0x2c,0x20,0x68,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2b,0x77,0x69,0x64,0x74,0x68,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3e,0x20,0x32,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x2b,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x6f,0x75,0x74,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x5f,0x6f,0x75,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x34,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x2c,0x20,0x30,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x31,0x36,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x77,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x25,0x20,0x34,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x7b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x73,0x68,0x6f,0x72,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x77,0x72,0x69,0x74,0x65,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x2b,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x2c,0x20,0x61,0x73,0x5f,0x75,0x69,0x6e,0x74,0x34,0x28,0x6f,0x75,0x74,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x77,0x20,0x3d,0x3d,0x20,0x30,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x64,0x73,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x2a,0x20,0x31,0x36,0x20,0x2b,0x20,0x73,0x67,0x6c,0x69,0x64,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0x20,0x2b,0x2b,0x69,0x29,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x70,0x61,0x64,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x31,0x36,0x5d,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x72,0x65,0x71,0x64,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x73,0x69,0x7a,0x65,0x28,0x31,0x36,0x29,0x29,0x29,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x75,0x6e,0x61,0x72,0x79,0x5f,0x62,0x75,0x66,0x5f,0x63,0x31,0x36,0x5f,0x63,0x34,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x2a,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x20,0x3c,0x3c,0x20,0x32,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x62,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x6c,0x6f,0x63,0x61,0x6c,0x5f,0x69,0x64,0x28,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x2f,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x68,0x62,0x20,0x25,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x72,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x33,0x29,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x20,0x3d,0x20,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x20,0x2b,0x20,0x31,0x35,0x29,0x20,0x2f,0x20,0x31,0x36,0x3b,0xa,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x31,0x36,0x2b,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x73,0x72,0x63,0x5f,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x2b,0x69,0x6e,0x70,0x75,0x74,0x5f,0x70,0x61,0x64,0x5f,0x6c,0x65,0x66,0x74,0x29,0x20,0x2a,0x20,0x31,0x36,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x28,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2a,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x2b,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x3c,0x3c,0x32,0x29,0x29,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2a,0x77,0x69,0x64,0x74,0x68,0x2b,0x77,0x29,0x20,0x2a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x2a,0x20,0x34,0x3b,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x68,0x61,0x6c,0x66,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x5f,0x75,0x73,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x73,0x68,0x6f,0x72,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x20,0x3d,0x20,0x61,0x73,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x74,0x65,0x6c,0x5f,0x73,0x75,0x62,0x5f,0x67,0x72,0x6f,0x75,0x70,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x72,0x65,0x61,0x64,0x34,0x28,0x28,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x75,0x69,0x6e,0x74,0x2a,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x20,0x2b,0x20,0x73,0x72,0x63,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x29,0x29,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x78,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x25,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x3d,0x20,0x73,0x67,0x6c,0x69,0x64,0x20,0x2f,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x77,0x20,0x2b,0x20,0x34,0x20,0x3e,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3f,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x20,0x25,0x20,0x34,0x29,0x20,0x3a,0x20,0x34,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x69,0x20,0x3d,0x20,0x30,0x3b,0x20,0x69,0x20,0x3c,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x20,0x69,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5b,0x64,0x73,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x69,0x20,0x2a,0x20,0x34,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x79,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x2b,0x20,0x6c,0x69,0x64,0x5f,0x78,0x5d,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5b,0x69,0x5d,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa, } }, #endif #endif @@ -196,7 +196,7 @@ extern const std::map> OpenCLProgramMap }, { "depthwise_deconv2d", - { 0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x52,0x45,0x41,0x44,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4d,0x41,0x47,0x45,0x28,0x69,0x2c,0x20,0x62,0x61,0x73,0x65,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3d,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x23,0x23,0x69,0x20,0x2b,0x20,0x62,0x61,0x73,0x65,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3d,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x23,0x23,0x69,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x2c,0x20,0x69,0x6e,0x5f,0x68,0x62,0x5f,0x76,0x61,0x6c,0x75,0x65,0x29,0x29,0x3b,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x43,0x41,0x4c,0x43,0x55,0x4c,0x41,0x54,0x45,0x5f,0x4f,0x55,0x54,0x50,0x55,0x54,0x28,0x69,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x78,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x30,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x79,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x31,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x7a,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x32,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x77,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x33,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x64,0x65,0x70,0x74,0x68,0x77,0x69,0x73,0x65,0x5f,0x64,0x65,0x63,0x6f,0x6e,0x76,0x32,0x64,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x4e,0x4f,0x5f,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x62,0x69,0x61,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x4e,0x4f,0x5f,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x62,0x69,0x61,0x73,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x28,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x28,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2d,0x20,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x28,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2d,0x20,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x20,0x3d,0x20,0x28,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2f,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x20,0x3d,0x20,0x28,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2f,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x6c,0x6c,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x5f,0x69,0x64,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x6b,0x5f,0x79,0x20,0x3d,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x3b,0x20,0x6b,0x5f,0x79,0x20,0x3e,0x3d,0x20,0x30,0x3b,0x20,0x6b,0x5f,0x79,0x20,0x2d,0x3d,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x69,0x64,0x79,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x68,0x62,0x5f,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x5f,0x69,0x64,0x79,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x6b,0x5f,0x78,0x20,0x3d,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x3b,0x20,0x6b,0x5f,0x78,0x20,0x3e,0x3d,0x20,0x30,0x3b,0x20,0x6b,0x5f,0x78,0x20,0x2d,0x3d,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x30,0x20,0x3d,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x52,0x45,0x41,0x44,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4d,0x41,0x47,0x45,0x28,0x30,0x2c,0x20,0x30,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x5f,0x79,0x2c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x6b,0x5f,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x30,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x6f,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x52,0x45,0x4c,0x55,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x30,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x52,0x45,0x4c,0x55,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x63,0x6c,0x61,0x6d,0x70,0x28,0x6f,0x75,0x74,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x36,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x72,0x69,0x74,0x65,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2c,0x20,0x6f,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } + { 0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x52,0x45,0x41,0x44,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4d,0x41,0x47,0x45,0x28,0x69,0x2c,0x20,0x62,0x61,0x73,0x65,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3d,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x23,0x23,0x69,0x20,0x2b,0x20,0x62,0x61,0x73,0x65,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3d,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x23,0x23,0x69,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x76,0x61,0x6c,0x75,0x65,0x23,0x23,0x69,0x2c,0x20,0x69,0x6e,0x5f,0x68,0x62,0x5f,0x76,0x61,0x6c,0x75,0x65,0x29,0x29,0x3b,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x43,0x41,0x4c,0x43,0x55,0x4c,0x41,0x54,0x45,0x5f,0x4f,0x55,0x54,0x50,0x55,0x54,0x28,0x69,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x78,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x30,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x79,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x31,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x7a,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x32,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x23,0x23,0x69,0x2e,0x77,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x33,0x2c,0x20,0x6f,0x75,0x74,0x23,0x23,0x69,0x29,0x3b,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x64,0x65,0x70,0x74,0x68,0x77,0x69,0x73,0x65,0x5f,0x64,0x65,0x63,0x6f,0x6e,0x76,0x32,0x64,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x4e,0x4f,0x5f,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x62,0x69,0x61,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x6e,0x64,0x65,0x66,0x20,0x4e,0x4f,0x5f,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x62,0x69,0x61,0x73,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x28,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x25,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x20,0x3d,0x20,0x28,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2f,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x20,0x3d,0x20,0x28,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2f,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2b,0x20,0x6f,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x69,0x6e,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x5f,0x69,0x64,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x6b,0x5f,0x79,0x20,0x3d,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x79,0x3b,0x20,0x6b,0x5f,0x79,0x20,0x3e,0x3d,0x20,0x30,0x3b,0x20,0x6b,0x5f,0x79,0x20,0x2d,0x3d,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x69,0x64,0x79,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x5f,0x68,0x62,0x5f,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x5f,0x69,0x64,0x79,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x64,0x78,0x5f,0x68,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x6b,0x5f,0x78,0x20,0x3d,0x20,0x64,0x65,0x61,0x6c,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x5f,0x78,0x3b,0x20,0x6b,0x5f,0x78,0x20,0x3e,0x3d,0x20,0x30,0x3b,0x20,0x6b,0x5f,0x78,0x20,0x2d,0x3d,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x30,0x20,0x3d,0x20,0x69,0x6e,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x52,0x45,0x41,0x44,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4d,0x41,0x47,0x45,0x28,0x30,0x2c,0x20,0x30,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6b,0x5f,0x79,0x2c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x6b,0x5f,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x72,0x65,0x61,0x64,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x77,0x65,0x69,0x67,0x68,0x74,0x73,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x28,0x69,0x6e,0x30,0x2c,0x20,0x77,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x6f,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x52,0x45,0x4c,0x55,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x30,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x52,0x45,0x4c,0x55,0x36,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x30,0x20,0x3d,0x20,0x63,0x6c,0x61,0x6d,0x70,0x28,0x6f,0x75,0x74,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x30,0x2c,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x29,0x36,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x77,0x72,0x69,0x74,0x65,0x5f,0x69,0x6d,0x61,0x67,0x65,0x66,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x78,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2c,0x20,0x6f,0x75,0x74,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa, } }, #ifndef MNN_OPENCL_BUFFER_CLOSED { @@ -207,12 +207,12 @@ extern const std::map> OpenCLProgramMap #ifndef MNN_OPENCL_BUFFER_CLOSED { "matmul_buf", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0x20,0x5c,0xa,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x2f,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x57,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0x2f,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x48,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x31,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x74,0x65,0x6d,0x70,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x68,0x6f,0x72,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x20,0x2b,0x20,0x31,0x29,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x2a,0x34,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x33,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x30,0x2c,0x20,0x62,0x31,0x2e,0x73,0x30,0x2c,0x20,0x62,0x32,0x2e,0x73,0x30,0x2c,0x20,0x62,0x33,0x2e,0x73,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x31,0x2c,0x20,0x62,0x31,0x2e,0x73,0x31,0x2c,0x20,0x62,0x32,0x2e,0x73,0x31,0x2c,0x20,0x62,0x33,0x2e,0x73,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x32,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x32,0x2c,0x20,0x62,0x31,0x2e,0x73,0x32,0x2c,0x20,0x62,0x32,0x2e,0x73,0x32,0x2c,0x20,0x62,0x33,0x2e,0x73,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x33,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x33,0x2c,0x20,0x62,0x31,0x2e,0x73,0x33,0x2c,0x20,0x62,0x32,0x2e,0x73,0x33,0x2c,0x20,0x62,0x33,0x2e,0x73,0x33,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x29,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x42,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x31,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x74,0x65,0x6d,0x70,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x68,0x6f,0x72,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x20,0x2b,0x20,0x31,0x29,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2a,0x34,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x33,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x79,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x29,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x41,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x68,0x6f,0x72,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x20,0x2b,0x20,0x31,0x29,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x78,0x2c,0x20,0x62,0x31,0x2e,0x78,0x2c,0x20,0x62,0x32,0x2e,0x78,0x2c,0x20,0x62,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x79,0x2c,0x20,0x62,0x31,0x2e,0x79,0x2c,0x20,0x62,0x32,0x2e,0x79,0x2c,0x20,0x62,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x7a,0x2c,0x20,0x62,0x31,0x2e,0x7a,0x2c,0x20,0x62,0x32,0x2e,0x7a,0x2c,0x20,0x62,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x77,0x2c,0x20,0x62,0x31,0x2e,0x77,0x2c,0x20,0x62,0x32,0x2e,0x77,0x2c,0x20,0x62,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x31,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x32,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x33,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x41,0x5f,0x74,0x72,0x61,0x6e,0x73,0x42,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x68,0x6f,0x72,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x20,0x2b,0x20,0x31,0x29,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x31,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x32,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x33,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0x20,0x5c,0xa,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0x2f,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x57,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0x2f,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x20,0x48,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x31,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x74,0x65,0x6d,0x70,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x70,0x6f,0x73,0x2a,0x34,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x30,0x2c,0x20,0x62,0x31,0x2e,0x73,0x30,0x2c,0x20,0x62,0x32,0x2e,0x73,0x30,0x2c,0x20,0x62,0x33,0x2e,0x73,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x31,0x2c,0x20,0x62,0x31,0x2e,0x73,0x31,0x2c,0x20,0x62,0x32,0x2e,0x73,0x31,0x2c,0x20,0x62,0x33,0x2e,0x73,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x32,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x32,0x2c,0x20,0x62,0x31,0x2e,0x73,0x32,0x2c,0x20,0x62,0x32,0x2e,0x73,0x32,0x2c,0x20,0x62,0x33,0x2e,0x73,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x33,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x33,0x2c,0x20,0x62,0x31,0x2e,0x73,0x33,0x2c,0x20,0x62,0x32,0x2e,0x73,0x33,0x2c,0x20,0x62,0x33,0x2e,0x73,0x33,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x29,0x2a,0x34,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x33,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x79,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x30,0x2c,0x20,0x62,0x31,0x2e,0x73,0x30,0x2c,0x20,0x62,0x32,0x2e,0x73,0x30,0x2c,0x20,0x62,0x33,0x2e,0x73,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x31,0x2c,0x20,0x62,0x31,0x2e,0x73,0x31,0x2c,0x20,0x62,0x32,0x2e,0x73,0x31,0x2c,0x20,0x62,0x33,0x2e,0x73,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x32,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x32,0x2c,0x20,0x62,0x31,0x2e,0x73,0x32,0x2c,0x20,0x62,0x32,0x2e,0x73,0x32,0x2c,0x20,0x62,0x33,0x2e,0x73,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x74,0x6d,0x70,0x33,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x73,0x33,0x2c,0x20,0x62,0x31,0x2e,0x73,0x33,0x2c,0x20,0x62,0x32,0x2e,0x73,0x33,0x2c,0x20,0x62,0x33,0x2e,0x73,0x33,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x74,0x6d,0x70,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x29,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x42,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x31,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x32,0x20,0x3d,0x20,0x30,0x2c,0x20,0x62,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x74,0x65,0x6d,0x70,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x79,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x7a,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x74,0x65,0x6d,0x70,0x2e,0x77,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x31,0x29,0x2a,0x34,0x20,0x2d,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2a,0x34,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2a,0x34,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3d,0x3d,0x20,0x33,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x79,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3d,0x3d,0x20,0x32,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x7a,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x69,0x66,0x20,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x2e,0x77,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x29,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x41,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x78,0x2c,0x20,0x62,0x31,0x2e,0x78,0x2c,0x20,0x62,0x32,0x2e,0x78,0x2c,0x20,0x62,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x79,0x2c,0x20,0x62,0x31,0x2e,0x79,0x2c,0x20,0x62,0x32,0x2e,0x79,0x2c,0x20,0x62,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x7a,0x2c,0x20,0x62,0x31,0x2e,0x7a,0x2c,0x20,0x62,0x32,0x2e,0x7a,0x2c,0x20,0x62,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x77,0x2c,0x20,0x62,0x31,0x2e,0x77,0x2c,0x20,0x62,0x32,0x2e,0x77,0x2c,0x20,0x62,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x29,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x29,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x78,0x2c,0x20,0x62,0x31,0x2e,0x78,0x2c,0x20,0x62,0x32,0x2e,0x78,0x2c,0x20,0x62,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x79,0x2c,0x20,0x62,0x31,0x2e,0x79,0x2c,0x20,0x62,0x32,0x2e,0x79,0x2c,0x20,0x62,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x7a,0x2c,0x20,0x62,0x31,0x2e,0x7a,0x2c,0x20,0x62,0x32,0x2e,0x7a,0x2c,0x20,0x62,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x62,0x30,0x2e,0x77,0x2c,0x20,0x62,0x31,0x2e,0x77,0x2c,0x20,0x62,0x32,0x2e,0x77,0x2c,0x20,0x62,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x31,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x32,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x33,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x74,0x6d,0x75,0x6c,0x5f,0x74,0x72,0x61,0x6e,0x73,0x41,0x5f,0x74,0x72,0x61,0x6e,0x73,0x42,0x5f,0x62,0x75,0x66,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x32,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x20,0x46,0x4c,0x4f,0x41,0x54,0x2a,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x32,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x30,0x2e,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x42,0x49,0x41,0x53,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3d,0x20,0x28,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x20,0x2b,0x20,0x31,0x29,0x20,0x2a,0x20,0x34,0x20,0x2d,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x73,0x68,0x6f,0x72,0x74,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x30,0x3b,0x20,0x70,0x6f,0x73,0x20,0x3c,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2d,0x20,0x31,0x3b,0x20,0x70,0x6f,0x73,0x20,0x2b,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x70,0x6f,0x73,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x70,0x6f,0x73,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x28,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2d,0x31,0x29,0x29,0x20,0x2a,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x61,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x61,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x61,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2d,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x30,0x20,0x3d,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x31,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x33,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x32,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x32,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x62,0x33,0x20,0x3d,0x20,0x28,0x28,0x72,0x65,0x6d,0x61,0x69,0x6e,0x62,0x20,0x3e,0x3d,0x20,0x31,0x29,0x20,0x3f,0x20,0x76,0x5f,0x7a,0x65,0x72,0x6f,0x20,0x3a,0x20,0x76,0x6c,0x6f,0x61,0x64,0x34,0x28,0x69,0x6e,0x70,0x62,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x62,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x78,0x2c,0x20,0x61,0x31,0x2e,0x78,0x2c,0x20,0x61,0x32,0x2e,0x78,0x2c,0x20,0x61,0x33,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x79,0x2c,0x20,0x61,0x31,0x2e,0x79,0x2c,0x20,0x61,0x32,0x2e,0x79,0x2c,0x20,0x61,0x33,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x7a,0x2c,0x20,0x61,0x31,0x2e,0x7a,0x2c,0x20,0x61,0x32,0x2e,0x7a,0x2c,0x20,0x61,0x33,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x61,0x30,0x2e,0x77,0x2c,0x20,0x61,0x31,0x2e,0x77,0x2c,0x20,0x61,0x32,0x2e,0x77,0x2c,0x20,0x61,0x33,0x2e,0x77,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2f,0x2f,0x6d,0x61,0x74,0x6d,0x75,0x6c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x30,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x31,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x32,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x78,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x79,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x7a,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x32,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2e,0x77,0x20,0x2b,0x3d,0x20,0x64,0x6f,0x74,0x28,0x61,0x33,0x5f,0x74,0x72,0x61,0x6e,0x73,0x2c,0x20,0x62,0x33,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x3d,0x20,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x29,0x20,0x2a,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x30,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x31,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x31,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x32,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x32,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x32,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x34,0x2a,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x5f,0x69,0x64,0x78,0x2b,0x33,0x20,0x3e,0x3d,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x76,0x73,0x74,0x6f,0x72,0x65,0x34,0x28,0x72,0x65,0x73,0x75,0x6c,0x74,0x33,0x2c,0x20,0x6f,0x75,0x74,0x5f,0x6f,0x66,0x66,0x73,0x65,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x5f,0x62,0x6c,0x6f,0x63,0x6b,0x73,0x2a,0x33,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x29,0x3b,0xa,0x7d,0xa, } }, #endif { "pooling", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x30,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x30,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x78,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x3d,0x20,0x6d,0x61,0x78,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x65,0x6e,0x64,0x20,0x20,0x20,0x3d,0x20,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x65,0x6e,0x64,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x20,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x5f,0x72,0x65,0x71,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2a,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x5f,0x72,0x65,0x71,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x30,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x21,0x3d,0x20,0x2d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x30,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x21,0x3d,0x20,0x2d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x3b,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x2c,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x29,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x32,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x33,0x20,0x3e,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x32,0x29,0x20,0x7b,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5c,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x70,0x6f,0x6f,0x6c,0x69,0x6e,0x67,0x28,0x47,0x4c,0x4f,0x42,0x41,0x4c,0x5f,0x53,0x49,0x5a,0x45,0x5f,0x33,0x5f,0x44,0x49,0x4d,0x53,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x32,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x44,0x45,0x41,0x4c,0x5f,0x4e,0x4f,0x4e,0x5f,0x55,0x4e,0x49,0x46,0x4f,0x52,0x4d,0x5f,0x44,0x49,0x4d,0x33,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x64,0x69,0x6d,0x31,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2f,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x2d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x73,0x74,0x72,0x69,0x64,0x65,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x2d,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x50,0x4f,0x4f,0x4c,0x5f,0x41,0x56,0x47,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x30,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x30,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x3d,0x20,0x6d,0x61,0x78,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x20,0x3d,0x20,0x6d,0x61,0x78,0x28,0x30,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x65,0x6e,0x64,0x20,0x20,0x20,0x3d,0x20,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x65,0x6e,0x64,0x20,0x20,0x20,0x20,0x3d,0x20,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x43,0x4f,0x55,0x4e,0x54,0x5f,0x49,0x4e,0x43,0x4c,0x55,0x44,0x45,0x5f,0x50,0x41,0x44,0x44,0x49,0x4e,0x47,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x28,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x20,0x2d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x20,0x2a,0x20,0x28,0x6d,0x69,0x6e,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x70,0x61,0x64,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x20,0x2d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x6d,0x75,0x6c,0x32,0x34,0x28,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x2c,0x20,0x28,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x65,0x6e,0x64,0x20,0x2d,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x46,0x4c,0x4f,0x41,0x54,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x5f,0x72,0x65,0x71,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x29,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x73,0x69,0x7a,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x2a,0x20,0x62,0x6c,0x6f,0x63,0x6b,0x5f,0x66,0x6c,0x6f,0x61,0x74,0x5f,0x72,0x65,0x71,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x2d,0x46,0x4c,0x54,0x5f,0x4d,0x41,0x58,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x30,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3d,0x20,0x30,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x3b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x20,0x21,0x3d,0x20,0x2d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3d,0x20,0x30,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x20,0x3c,0x20,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x3b,0x20,0x77,0x69,0x64,0x74,0x68,0x2b,0x2b,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x73,0x65,0x6c,0x65,0x63,0x74,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x2d,0x31,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3c,0x20,0x30,0x20,0x7c,0x7c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3e,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x29,0x29,0x3b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x21,0x3d,0x20,0x2d,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x20,0x3d,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x20,0x3e,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x3f,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x28,0x69,0x6e,0x70,0x75,0x74,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x29,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x20,0x2b,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x73,0x74,0x61,0x72,0x74,0x20,0x2b,0x20,0x77,0x69,0x64,0x74,0x68,0x29,0x20,0x3a,0x20,0x72,0x65,0x64,0x69,0x63,0x65,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x2c,0x20,0x69,0x6e,0x70,0x75,0x74,0x5f,0x64,0x61,0x74,0x61,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x20,0x3d,0x20,0x6d,0x61,0x64,0x32,0x34,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x72,0x65,0x73,0x75,0x6c,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x69,0x66,0x20,0x52,0x45,0x54,0x55,0x52,0x4e,0x5f,0x52,0x45,0x44,0x49,0x43,0x45,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x72,0x65,0x64,0x69,0x63,0x65,0x4f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x63,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x5f,0x77,0x69,0x64,0x74,0x68,0x5f,0x69,0x64,0x78,0x2c,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x5f,0x62,0x61,0x74,0x63,0x68,0x5f,0x68,0x65,0x69,0x67,0x68,0x74,0x5f,0x69,0x64,0x78,0x29,0x2c,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x72,0x65,0x64,0x69,0x63,0x65,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x7d,0xa, } }, #ifndef MNN_OPENCL_BUFFER_CLOSED { @@ -272,7 +272,7 @@ extern const std::map> OpenCLProgramMap }, { "binary", - { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x50,0x49,0x20,0x33,0x2e,0x31,0x34,0x31,0x35,0x39,0x32,0x36,0x35,0x33,0x35,0x38,0x39,0x66,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0xa,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,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x57,0x43,0x34,0x2c,0x20,0x4e,0x48,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x2c,0x20,0x69,0x6e,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x70,0x72,0x65,0x6c,0x75,0x28,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0x69,0x6e,0x74,0x32,0x20,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x74,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x4e,0x48,0x57,0x43,0x53,0x74,0x65,0x70,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x6e,0x68,0x77,0x63,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x70,0x6f,0x73,0x2e,0x79,0x2f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x79,0x25,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x78,0x25,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x78,0x2f,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x6e,0x68,0x77,0x63,0x2e,0x78,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x26,0x26,0x20,0x6e,0x68,0x77,0x63,0x2e,0x77,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x6e,0x68,0x77,0x63,0x31,0x20,0x3d,0x20,0x6e,0x68,0x77,0x63,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x4e,0x48,0x57,0x43,0x53,0x74,0x65,0x70,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x31,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6e,0x68,0x77,0x63,0x31,0x2e,0x77,0x2a,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2e,0x78,0x2b,0x6e,0x68,0x77,0x63,0x31,0x2e,0x7a,0x2c,0x20,0x6e,0x68,0x77,0x63,0x31,0x2e,0x78,0x2a,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2e,0x79,0x2b,0x6e,0x68,0x77,0x63,0x31,0x2e,0x79,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x69,0x6d,0x61,0x67,0x65,0x43,0x6f,0x70,0x79,0x28,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x64,0x69,0x6d,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x64,0x69,0x6d,0x28,0x69,0x6e,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x64,0x69,0x6d,0x2e,0x78,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3e,0x3d,0x20,0x64,0x69,0x6d,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x29,0x3b,0xa,0x7d,0xa, } + { 0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4d,0x4e,0x4e,0x5f,0x53,0x55,0x50,0x50,0x4f,0x52,0x54,0x5f,0x46,0x50,0x31,0x36,0xa,0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x20,0x45,0x58,0x54,0x45,0x4e,0x53,0x49,0x4f,0x4e,0x20,0x63,0x6c,0x5f,0x6b,0x68,0x72,0x5f,0x66,0x70,0x31,0x36,0x20,0x3a,0x20,0x65,0x6e,0x61,0x62,0x6c,0x65,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x23,0x64,0x65,0x66,0x69,0x6e,0x65,0x20,0x50,0x49,0x20,0x33,0x2e,0x31,0x34,0x31,0x35,0x39,0x32,0x36,0x35,0x33,0x35,0x38,0x39,0x66,0xa,0x5f,0x5f,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x5f,0x74,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x20,0x3d,0x20,0x43,0x4c,0x4b,0x5f,0x4e,0x4f,0x52,0x4d,0x41,0x4c,0x49,0x5a,0x45,0x44,0x5f,0x43,0x4f,0x4f,0x52,0x44,0x53,0x5f,0x46,0x41,0x4c,0x53,0x45,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x41,0x44,0x44,0x52,0x45,0x53,0x53,0x5f,0x43,0x4c,0x41,0x4d,0x50,0x20,0x7c,0x20,0x43,0x4c,0x4b,0x5f,0x46,0x49,0x4c,0x54,0x45,0x52,0x5f,0x4e,0x45,0x41,0x52,0x45,0x53,0x54,0x3b,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x28,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x2c,0x20,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x69,0x6e,0x74,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x2c,0xa,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,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0xa,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,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x2f,0x2f,0x5b,0x4e,0x2c,0x48,0x2c,0x57,0x2c,0x43,0x34,0x5d,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2c,0xa,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,0x5f,0x5f,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x20,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0x2f,0x2f,0x57,0x43,0x34,0x2c,0x20,0x4e,0x48,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x2c,0x20,0x69,0x6e,0x31,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x30,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3c,0x20,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x64,0x69,0x6d,0x31,0x29,0x20,0x7b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x30,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x69,0x73,0x46,0x75,0x6c,0x6c,0x2e,0x79,0x20,0x3d,0x3d,0x20,0x30,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x30,0x2c,0x20,0x30,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x28,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x2c,0x20,0x69,0x6e,0x31,0x2e,0x78,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x65,0x6c,0x73,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x28,0x61,0x63,0x74,0x69,0x76,0x61,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x66,0x6d,0x61,0x78,0x28,0x6f,0x75,0x74,0x2c,0x20,0x28,0x46,0x4c,0x4f,0x41,0x54,0x34,0x29,0x30,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x62,0x69,0x6e,0x61,0x72,0x79,0x5f,0x70,0x72,0x65,0x6c,0x75,0x28,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x73,0x68,0x61,0x70,0x65,0x2c,0x20,0x69,0x6e,0x74,0x32,0x20,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x69,0x6e,0x74,0x34,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x4e,0x48,0x57,0x43,0x53,0x74,0x65,0x70,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x6e,0x68,0x77,0x63,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x34,0x29,0x28,0x70,0x6f,0x73,0x2e,0x79,0x2f,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x79,0x25,0x73,0x68,0x61,0x70,0x65,0x2e,0x79,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x78,0x25,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x2c,0x20,0x70,0x6f,0x73,0x2e,0x78,0x2f,0x73,0x68,0x61,0x70,0x65,0x2e,0x7a,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x6e,0x68,0x77,0x63,0x2e,0x78,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x78,0x20,0x26,0x26,0x20,0x6e,0x68,0x77,0x63,0x2e,0x77,0x20,0x3c,0x20,0x73,0x68,0x61,0x70,0x65,0x2e,0x77,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x34,0x20,0x6e,0x68,0x77,0x63,0x31,0x20,0x3d,0x20,0x6e,0x68,0x77,0x63,0x20,0x2a,0x20,0x69,0x6e,0x70,0x75,0x74,0x31,0x4e,0x48,0x57,0x43,0x53,0x74,0x65,0x70,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x31,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x6e,0x68,0x77,0x63,0x31,0x2e,0x77,0x2a,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2e,0x78,0x2b,0x6e,0x68,0x77,0x63,0x31,0x2e,0x7a,0x2c,0x20,0x6e,0x68,0x77,0x63,0x31,0x2e,0x78,0x2a,0x77,0x68,0x49,0x6e,0x70,0x75,0x74,0x31,0x2e,0x79,0x2b,0x6e,0x68,0x77,0x63,0x31,0x2e,0x79,0x29,0x3b,0xa,0xa,0x23,0x69,0x66,0x64,0x65,0x66,0x20,0x4f,0x50,0x45,0x4e,0x43,0x4c,0x5f,0x49,0x4e,0x50,0x55,0x54,0x5f,0x49,0x4e,0x54,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x31,0x29,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x63,0x6f,0x6e,0x76,0x65,0x72,0x74,0x5f,0x69,0x6e,0x74,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x29,0x3b,0xa,0x23,0x65,0x6c,0x73,0x65,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x30,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x30,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x69,0x6e,0x31,0x20,0x3d,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x31,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x31,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x46,0x4c,0x4f,0x41,0x54,0x34,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x43,0x4f,0x4e,0x56,0x45,0x52,0x54,0x5f,0x46,0x4c,0x4f,0x41,0x54,0x34,0x28,0x4f,0x50,0x45,0x52,0x41,0x54,0x4f,0x52,0x29,0x3b,0xa,0x23,0x65,0x6e,0x64,0x69,0x66,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x6f,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,0xa,0xa,0x5f,0x5f,0x6b,0x65,0x72,0x6e,0x65,0x6c,0x20,0x76,0x6f,0x69,0x64,0x20,0x69,0x6d,0x61,0x67,0x65,0x43,0x6f,0x70,0x79,0x28,0x5f,0x5f,0x72,0x65,0x61,0x64,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x5f,0x5f,0x77,0x72,0x69,0x74,0x65,0x5f,0x6f,0x6e,0x6c,0x79,0x20,0x69,0x6d,0x61,0x67,0x65,0x32,0x64,0x5f,0x74,0x20,0x6f,0x75,0x74,0x70,0x75,0x74,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x70,0x6f,0x73,0x20,0x3d,0x20,0x28,0x69,0x6e,0x74,0x32,0x29,0x28,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x30,0x29,0x2c,0x20,0x67,0x65,0x74,0x5f,0x67,0x6c,0x6f,0x62,0x61,0x6c,0x5f,0x69,0x64,0x28,0x31,0x29,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x69,0x6e,0x74,0x32,0x20,0x64,0x69,0x6d,0x20,0x3d,0x20,0x67,0x65,0x74,0x5f,0x69,0x6d,0x61,0x67,0x65,0x5f,0x64,0x69,0x6d,0x28,0x69,0x6e,0x70,0x75,0x74,0x29,0x3b,0xa,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x70,0x6f,0x73,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x64,0x69,0x6d,0x2e,0x78,0x20,0x26,0x26,0x20,0x70,0x6f,0x73,0x2e,0x79,0x20,0x3e,0x3d,0x20,0x64,0x69,0x6d,0x2e,0x79,0x29,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x57,0x49,0x5f,0x46,0x28,0x6f,0x75,0x74,0x70,0x75,0x74,0x2c,0x20,0x70,0x6f,0x73,0x2c,0x20,0x52,0x49,0x5f,0x46,0x28,0x69,0x6e,0x70,0x75,0x74,0x2c,0x20,0x53,0x41,0x4d,0x50,0x4c,0x45,0x52,0x2c,0x20,0x70,0x6f,0x73,0x29,0x29,0x3b,0xa,0x7d,0xa, } }, #ifndef MNN_OPENCL_BUFFER_CLOSED { diff --git a/source/backend/opencl/execution/cl/pooling.cl b/source/backend/opencl/execution/cl/pooling.cl index 3f306f74a..67eef9667 100644 --- a/source/backend/opencl/execution/cl/pooling.cl +++ b/source/backend/opencl/execution/cl/pooling.cl @@ -13,7 +13,8 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, __private const int2 input_shape, __private const int output_height, __private const int2 pad_shape, __private const int2 stride_shape, __private const int2 kernel_shape, - __write_only image2d_t output) { + __write_only image2d_t output, + __write_only image2d_t rediceOutput) { const int output_channel_idx = get_global_id(0); const int output_width_idx = get_global_id(1); const int output_batch_height_idx = get_global_id(2); @@ -48,11 +49,18 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, const int kernel_width_start = max(0, input_width_start); const int kernel_height_end = min(input_height_start + kernel_shape.x, input_shape.x); const int kernel_width_end = min(input_width_start + kernel_shape.y, input_shape.y); + #ifdef COUNT_INCLUDE_PADDING + const int block_size = (min(input_height_start + kernel_shape.x, input_shape.x + pad_shape.x) - input_height_start) * (min(input_width_start + kernel_shape.y, input_shape.y + pad_shape.y) - input_width_start); + #else const int block_size = mul24((kernel_height_end - kernel_height_start), (kernel_width_end - kernel_width_start)); + #endif const FLOAT block_float_req = (FLOAT)1.0f / (FLOAT)block_size; output_result = output_result * block_float_req; #else FLOAT4 output_result = (FLOAT4)(-FLT_MAX); + #if RETURN_REDICE + int4 redice = (int4)0; + #endif for (int height = 0; height < kernel_shape.x; height++) { int input_height_idx = input_height_start + height; input_height_idx = @@ -65,6 +73,9 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, if (input_width_idx != -1) { FLOAT4 input_data = RI_F(input, SAMPLER, (int2)(input_width_idx, input_height_idx)); + #if RETURN_REDICE + redice = input_data > output_result ? (int4)((input_height_start + height) * input_shape.y + input_width_start + width) : redice; + #endif output_result = fmax(output_result, input_data); } } @@ -74,4 +85,7 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, const int output_channel_width_idx = mad24(output_channel_idx, output_width, output_width_idx); WI_F(output, (int2)(output_channel_width_idx, output_batch_height_idx), output_result); + #if RETURN_REDICE + WI_F(rediceOutput, (int2)(output_channel_width_idx, output_batch_height_idx), CONVERT_FLOAT4(redice)); + #endif } diff --git a/source/backend/opencl/execution/cl/pooling_buf.cl b/source/backend/opencl/execution/cl/pooling_buf.cl index 3e28c2040..d933bfe97 100644 --- a/source/backend/opencl/execution/cl/pooling_buf.cl +++ b/source/backend/opencl/execution/cl/pooling_buf.cl @@ -15,6 +15,7 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, __private const int2 stride_shape, __private const int2 kernel_shape, __global FLOAT *output, + __global FLOAT *rediceOutput, __private const int channel_block) { const int ow_idx = get_global_id(0); @@ -31,7 +32,11 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, #ifdef POOL_AVG FLOAT4 result = (FLOAT4)(0); const int inp_offset = (((b_idx*channel_block+c_idx)*input_shape.x+ih_start)*input_shape.y+iw_start)*4; + #ifdef COUNT_INCLUDE_PADDING + int total_count = (min(ih_start + kernel_shape.x, input_shape.x + pad_shape.x) - ih_start) * (min(iw_start + kernel_shape.y, input_shape.y + pad_shape.y) - iw_start); + #else int total_count = 0; + #endif for(int kh=0; kh= input_shape.x) { @@ -44,12 +49,17 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, } FLOAT4 inp_data = vload4(0, input+inp_offset+(kh*input_shape.y+kw)*4); result += inp_data; + #ifndef COUNT_INCLUDE_PADDING total_count++; + #endif } } result = result / (FLOAT4)(1.0*total_count); #else FLOAT4 result = (FLOAT4)(-FLT_MAX); + #if RETURN_REDICE + int4 redice = (int4)0; + #endif const int inp_offset = (((b_idx*channel_block+c_idx)*input_shape.x+ih_start)*input_shape.y+iw_start)*4; for(int kh=0; kh result ? (int4)((ih_start + kh) * input_shape.y + iw_start + kw) : redice; + #endif result = fmax(result, inp_data); } } @@ -69,4 +82,7 @@ __kernel void pooling(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, const int out_offset = (((b_idx*channel_block + c_idx)*output_shape.x + oh_idx)* output_shape.y + ow_idx)*4; vstore4(result, 0, output+out_offset); + #if RETURN_REDICE + vstore4(CONVERT_FLOAT4(redice), 0, rediceOutput+out_offset); + #endif } diff --git a/source/backend/opencl/execution/cl/pooling_subgroup_buf.cl b/source/backend/opencl/execution/cl/pooling_subgroup_buf.cl index 71ba96ded..365ac6ce8 100644 --- a/source/backend/opencl/execution/cl/pooling_subgroup_buf.cl +++ b/source/backend/opencl/execution/cl/pooling_subgroup_buf.cl @@ -13,6 +13,7 @@ __kernel void pooling_c4_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, __private const int2 output_shape, __private const int2 pad_shape, __global FLOAT *output, + __global FLOAT *rediceOutput, __private const int channel, __private const int in_channel_block, __private const int out_channel_block, @@ -54,6 +55,9 @@ __kernel void pooling_c4_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, result = result / (FLOAT4)(1.0*total_count); #else FLOAT4 result = (FLOAT4)(-FLT_MAX); + #if RETURN_REDICE + int4 redice = (int4)0; + #endif const int inp_offset = (((b_idx*in_channel_block+c_idx)*input_shape.x+ih_start)*input_shape.y+iw_start+input_pad_left)*4; for(int kh=0; kh result ? (int4)((ih_start + kh) * input_shape.y + iw_start + kw) : redice; + #endif result = fmax(result, inp_data); } } @@ -73,6 +80,9 @@ __kernel void pooling_c4_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, const int out_offset = (((b_idx*in_channel_block + c_idx)*output_shape.x + oh_idx)* output_shape.y + ow_idx + output_pad_left)*4; vstore4(result, 0, output+out_offset); + #if RETURN_REDICE + vstore4(CONVERT_FLOAT4(redice), 0, rediceOutput+(((b_idx*in_channel_block + c_idx)*output_shape.x + oh_idx)* output_shape.y + ow_idx)*4); + #endif } __kernel void pooling_c4_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, @@ -80,6 +90,7 @@ __kernel void pooling_c4_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, __private const int2 output_shape, __private const int2 pad_shape, __global FLOAT *output, + __global FLOAT *rediceOutput, __private const int channel, __private const int in_channel_block, __private const int out_channel_block, @@ -122,6 +133,9 @@ __kernel void pooling_c4_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, result = result / (FLOAT4)(1.0*total_count); #else FLOAT4 result = (FLOAT4)(-FLT_MAX); + #if RETURN_REDICE + int4 redice = (int4)0; + #endif const int inp_offset = (((b_idx*in_channel_block+c_idx)*input_shape.x+ih_start)*input_shape.y+iw_start+input_pad_left)*4; for(int kh=0; kh result ? (int4)((ih_start + kh) * input_shape.y + iw_start + kw) : redice; + #endif result = fmax(result, inp_data); } } @@ -142,6 +159,9 @@ __kernel void pooling_c4_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, const int c_left = (c_idx % 4) * 4; const int out_offset = (((b_idx*out_channel_block + c_idx/4)*output_shape.x + oh_idx)* dst_width + ow_idx + output_pad_left)*16 + c_left; vstore4(result, 0, output+out_offset); + #if RETURN_REDICE + vstore4(CONVERT_FLOAT4(redice), 0, rediceOutput+(((b_idx*out_channel_block + c_idx)*output_shape.x + oh_idx)* output_shape.y + ow_idx)*4); + #endif if(ow_idx == 0){ int pad_offset = (((b_idx*out_channel_block + c_idx/4)*output_shape.x + oh_idx)* dst_width + 0)*16 + c_left; for(int i = 0; i < output_pad_left; ++i){ @@ -160,6 +180,7 @@ __kernel void pooling_c16_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, __private const int2 output_shape, __private const int2 pad_shape, __global FLOAT *output, + __global FLOAT *rediceOutput, __private const int channel, __private const int in_channel_block, __private const int out_channel_block, @@ -190,6 +211,9 @@ __kernel void pooling_c16_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, FLOAT8 total_count = (w_end - w_start) * (FLOAT8)(h_end - h_start); #else FLOAT8 result = (FLOAT8)(-FLT_MAX); +#if RETURN_REDICE + int8 redice = (int8)0; +#endif #endif const int inp_offset = mul24(mad24(mad24(mad24(b_idx,in_channel_block,c_idx),input_shape.x,ih_start),src_width,iw_start+input_pad_left),16); for(int kh=0; kh= 0 && (iw_start + kw + STRIDE_X*i) < input_shape_y){ -//#ifdef MNN_SUPPORT_FP16 -// src[i] = as_half(intel_sub_group_block_read_us((__global ushort*)(input + inp_offset + (kh*src_width + kw + STRIDE_X*i)*16))); -//#else -// src[i] = as_float(intel_sub_group_block_read((__global uint*)(input + inp_offset + (kh*src_width + kw + STRIDE_X*i)*16))); -//#endif -// }else{ -//#ifdef POOL_AVG -// src[i] = 0; -//#else -// src[i] = (FLOAT)(-FLT_MAX); -//#endif -// } } #ifdef POOL_AVG result += src; #else +#if RETURN_REDICE + redice = src > result ? (int8)((ih_start + kh) * input_shape.y + iw_start + kw) : redice; +#endif result = fmax(result, src); #endif } @@ -282,6 +295,27 @@ __kernel void pooling_c16_c16(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, } } } +#ifdef RETURN_REDICE + const uint lid_x = sglid % 4; + const uint lid_y = sglid / 4; + + const int width_height = output_shape.y * output_shape.x * 4; + const int redice_offset = (((b_idx*out_channel_block + c_idx * 4)*output_shape.x + oh_idx)* output_shape.y + ow_idx)*4; +#if OUTPUT_LEFTOVERS + if ((c_idx+1)*16 >= channel) { + for (int i = 0; i < 8; i++) { + if ((c_idx*16 + lid_y * 4 + lid_x < channel) && (ow_idx + i) < output_shape.y) + rediceOutput[redice_offset + lid_y * width_height + i * 4 + lid_x] = redice[i]; + } + } + else +#endif + { + for (int i = 0; i < 8 && (ow_idx + i) < output_shape.y; i++) { + rediceOutput[redice_offset + lid_y * width_height + i * 4 + lid_x] = redice[i]; + } + } +#endif } __attribute__((intel_reqd_sub_group_size(16))) @@ -290,6 +324,7 @@ __kernel void pooling_c16_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, __private const int2 output_shape, __private const int2 pad_shape, __global FLOAT *output, + __global FLOAT *rediceOutput, __private const int channel, __private const int in_channel_block, __private const int out_channel_block, @@ -319,6 +354,9 @@ __kernel void pooling_c16_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, FLOAT8 total_count = (w_end - w_start) * (FLOAT8)(h_end - h_start); #else FLOAT8 result = (FLOAT8)(-FLT_MAX); +#if RETURN_REDICE + int8 redice = (int8)0; +#endif #endif const int inp_offset = mul24(mad24(mad24(mad24(b_idx,in_channel_block,c_idx),input_shape.x,ih_start),src_width,iw_start+input_pad_left),16); for(int kh=0; kh= 0 && (iw_start + kw + STRIDE_X*i) < input_shape_y){ -//#ifdef MNN_SUPPORT_FP16 -// src[i] = as_half(intel_sub_group_block_read_us((__global ushort*)(input + inp_offset + (kh*src_width + kw + STRIDE_X*i)*16))); -//#else -// src[i] = as_float(intel_sub_group_block_read((__global uint*)(input + inp_offset + (kh*src_width + kw + STRIDE_X*i)*16))); -//#endif -// }else{ -//#ifdef POOL_AVG -// src[i] = 0; -//#else -// src[i] = (FLOAT)(-FLT_MAX); -//#endif -// } } #ifdef POOL_AVG result += src; #else +#if RETURN_REDICE + redice = src > result ? (int8)((ih_start + kh) * input_shape.y + iw_start + kw) : redice; +#endif result = fmax(result, src); #endif } @@ -382,11 +409,17 @@ __kernel void pooling_c16_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, const int out_offset = (((b_idx*out_channel_block + c_idx * 4)*output_shape.x + oh_idx)* output_shape.y + ow_idx + output_pad_left)*4; const int width_height = output_shape.y * output_shape.x * 4; +#if RETURN_REDICE + const int redice_offset = (((b_idx*out_channel_block + c_idx * 4)*output_shape.x + oh_idx)* output_shape.y + ow_idx)*4; +#endif #if OUTPUT_LEFTOVERS if ((c_idx+1)*16 >= channel) { for (int i = 0; i < 8; i++) { if ((c_idx*16 + lid_y * 4 + lid_x < channel) && (ow_idx + i) < output_shape.y) output[out_offset + lid_y * width_height + i * 4 + lid_x] = result[i]; +#if RETURN_REDICE + rediceOutput[redice_offset + lid_y * width_height + i * 4 + lid_x] = redice[i]; +#endif } } else @@ -394,6 +427,9 @@ __kernel void pooling_c16_c4(GLOBAL_SIZE_3_DIMS __global const FLOAT *input, { for (int i = 0; i < 8 && (ow_idx + i) < output_shape.y; i++) { output[out_offset + lid_y * width_height + i * 4 + lid_x] = result[i]; +#if RETURN_REDICE + rediceOutput[redice_offset + lid_y * width_height + i * 4 + lid_x] = redice[i]; +#endif } } -} \ No newline at end of file +} diff --git a/source/backend/opencl/execution/cl/unary.cl b/source/backend/opencl/execution/cl/unary.cl index 5a158e94f..49f7ecb7b 100644 --- a/source/backend/opencl/execution/cl/unary.cl +++ b/source/backend/opencl/execution/cl/unary.cl @@ -28,7 +28,12 @@ __kernel void unary(GLOBAL_SIZE_3_DIMS __read_only image2d_t input, __write_only const int width = global_size_dim1; const int pos = mad24(channel_block_idx, width, w); +#ifdef OPENCL_INPUT_INT + FLOAT4 in = CONVERT_FLOAT4(convert_int4(RI_F(input, SAMPLER, (int2)(pos, hb)))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in = RI_F(input, SAMPLER, (int2)(pos, hb)); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif WI_F(output, (int2)(pos, hb), out); } diff --git a/source/backend/opencl/execution/cl/unary_buf.cl b/source/backend/opencl/execution/cl/unary_buf.cl index 0b2e2a177..26b9e6252 100644 --- a/source/backend/opencl/execution/cl/unary_buf.cl +++ b/source/backend/opencl/execution/cl/unary_buf.cl @@ -31,8 +31,13 @@ __kernel void unary_buf(GLOBAL_SIZE_3_DIMS const int height_idx = hb % height; const int offset = (((batch_idx*global_size_dim0+channel_block_idx)*height+height_idx)*global_size_dim1+w) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in = CONVERT_FLOAT4(convert_int4(vload4(0, input+offset))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in = vload4(0, input+offset); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, 0, output+offset); } diff --git a/source/backend/opencl/execution/cl/unary_subgroup_buf.cl b/source/backend/opencl/execution/cl/unary_subgroup_buf.cl index b80a83ac8..41299849d 100644 --- a/source/backend/opencl/execution/cl/unary_subgroup_buf.cl +++ b/source/backend/opencl/execution/cl/unary_subgroup_buf.cl @@ -36,8 +36,13 @@ __kernel void unary_buf_c4_c4(GLOBAL_SIZE_3_DIMS const int channel4 = (channel + 3) / 4; const int offset = (((batch_idx*channel4+channel_block_idx)*height+height_idx)*width+w) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in = CONVERT_FLOAT4(convert_int4(vload4(0, input+offset))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in = vload4(0, input+offset); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, 0, output+offset); } @@ -64,8 +69,13 @@ __kernel void unary_buf_c4_c16(GLOBAL_SIZE_3_DIMS const int offset = (((batch_idx*channel4+channel_block_idx)*height+height_idx)*width+w) * 4; const int dst_offset = (((batch_idx*channel16+channe_out_idx)*height+height_idx)*dst_width+w+output_pad_left) * 16 + (channel_block_idx % 4) * 4; +#ifdef OPENCL_INPUT_INT + FLOAT4 in = CONVERT_FLOAT4(convert_int4(vload4(0, input+offset))); + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else FLOAT4 in = vload4(0, input+offset); FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif vstore4(out, 0, output+dst_offset); if(w == 0){ int pad_offset = (((batch_idx*channel16+channe_out_idx)*height+height_idx)*dst_width) * 16 + (channel_block_idx % 4) * 4; @@ -102,13 +112,21 @@ __kernel void unary_buf_c16_c16(GLOBAL_SIZE_3_DIMS const int src_offset = (((batch_idx*channel16+channel_idx)*height+height_idx)*src_width+w+input_pad_left) * 16; const int dst_offset = (((batch_idx*channel16+channel_idx)*height+height_idx)*dst_width+w+output_pad_left) * 16; - +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in = CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input + src_offset))))); +#else + FLOAT4 in = CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input + src_offset))))); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in = as_half4(intel_sub_group_block_read_us4((__global ushort*)(input + src_offset))); #else FLOAT4 in = as_float4(intel_sub_group_block_read4((__global uint*)(input + src_offset))); #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif if (w + 4 > width) { for (int i = 0; i < width % 4; i++) { @@ -157,13 +175,21 @@ __kernel void unary_buf_c16_c4(GLOBAL_SIZE_3_DIMS const int src_offset = (((batch_idx*channel16+channel_idx)*height+height_idx)*src_width+w+input_pad_left) * 16; const int dst_offset = (((batch_idx*channel4+(channel_idx<<2))*height+height_idx)*width+w) * 4; const int height_width = height * width * 4; - +#ifdef OPENCL_INPUT_INT +#ifdef MNN_SUPPORT_FP16 + FLOAT4 in = CONVERT_FLOAT4(convert_int4(as_half4(intel_sub_group_block_read_us4((__global ushort*)(input + src_offset))))); +#else + FLOAT4 in = CONVERT_FLOAT4(convert_int4(as_float4(intel_sub_group_block_read4((__global uint*)(input + src_offset))))); +#endif + FLOAT4 out = CONVERT_FLOAT4(convert_int4(OPERATOR)); +#else #ifdef MNN_SUPPORT_FP16 FLOAT4 in = as_half4(intel_sub_group_block_read_us4((__global ushort*)(input + src_offset))); #else FLOAT4 in = as_float4(intel_sub_group_block_read4((__global uint*)(input + src_offset))); #endif FLOAT4 out = CONVERT_FLOAT4(OPERATOR); +#endif const int lid_x = sglid % 4; const int lid_y = sglid / 4; diff --git a/source/backend/opencl/execution/image/ConvExecution.cpp b/source/backend/opencl/execution/image/ConvExecution.cpp index 731ab0953..dc86fa8ff 100644 --- a/source/backend/opencl/execution/image/ConvExecution.cpp +++ b/source/backend/opencl/execution/image/ConvExecution.cpp @@ -78,7 +78,9 @@ ConvExecution::ConvExecution(const std::vector &inputs, const std::vec int kernelHeight = conv2dCommonParams->kernelY(); int outputChannel = conv2dCommonParams->outputCount(); auto gpuType = mOpenCLBackend->getOpenCLRuntime()->getGpuType(); +#ifndef MNN_OPENCL_BUFFER_CLOSED mWeightUseBuffer = gpuType == GpuType::MALI; +#endif int weightSize = 0; const float *filterDataPtr = nullptr; @@ -253,7 +255,7 @@ ConvExecution::ConvExecution(const std::vector &inputs, const std::vec MNN_ERROR("Map error ptrCL == nullptr \n"); } mOpenCLBackend->getOpenCLRuntime()->commandQueue().enqueueUnmapMemObject(filterBufferCL, ptrCL); - +#ifndef MNN_OPENCL_BUFFER_CLOSED if(mWeightUseBuffer){ mFilter.reset(Tensor::createDevice({UP_DIV(inputChannel, 4)*4, UP_DIV(outputChannel, 4), kernelWidth * kernelHeight, 4})); int kernel_buffer_size = UP_DIV(outputChannel, 4)*4* UP_DIV(inputChannel, 4)*4* kernelWidth* kernelHeight; @@ -271,7 +273,9 @@ ConvExecution::ConvExecution(const std::vector &inputs, const std::vec needTrans = true; } bufferConvertor.convertToNC4HW4Buffer(filterBuffer.get(), MNN::OpenCL::CONV2D_FILTER, mFilter.get(), needTrans); - } else{ + } else +#endif + { mFilter.reset(Tensor::createDevice({1, filterImageShape[1], 1, 4 * filterImageShape[0]})); mOpenCLBackend->onAcquireBuffer(mFilter.get(), Backend::STATIC); MNN::OpenCL::ImageBufferConvertor imageBufferConvertor{mOpenCLBackend->getOpenCLRuntime()}; @@ -607,6 +611,11 @@ class ConvolutionCreator : public OpenCLBackend::Creator { virtual ~ConvolutionCreator() = default; virtual Execution *onCreate(const std::vector &inputs, const std::vector &outputs, const MNN::Op *op, Backend *backend) const override { + if(op->main_as_Convolution2D()->common()->group() > 1){ + // Don't support group > 1 now + return nullptr; + } + if (inputs.size() > 1) { return nullptr; } diff --git a/source/backend/opencl/execution/image/EltwiseExecution.cpp b/source/backend/opencl/execution/image/EltwiseExecution.cpp index 61b651d53..382399305 100644 --- a/source/backend/opencl/execution/image/EltwiseExecution.cpp +++ b/source/backend/opencl/execution/image/EltwiseExecution.cpp @@ -30,6 +30,10 @@ static string swapComputeIn0In1(const string& computeOrigin) { EltwiseExecution::EltwiseExecution(const std::vector &inputs, const std::string &compute, const MNN::Op *op, Backend *backend) : CommonExecution(backend, op), mCompute(compute) { mBuildOptions.emplace("-DOPERATOR=" + compute); + auto dataType = inputs[0]->getType(); + if (dataType.code == halide_type_int){ + mBuildOptions.emplace("-DOPENCL_INPUT_INT"); + } } uint32_t EltwiseExecution::realSize(const Tensor* tensor) { diff --git a/source/backend/opencl/execution/image/MultiInputDWDeconvExecution.cpp b/source/backend/opencl/execution/image/MultiInputDWDeconvExecution.cpp index 6ef76a8a5..2d6b297ab 100644 --- a/source/backend/opencl/execution/image/MultiInputDWDeconvExecution.cpp +++ b/source/backend/opencl/execution/image/MultiInputDWDeconvExecution.cpp @@ -265,7 +265,7 @@ ErrorCode MultiInputDWDeconvExecution::onResize(const std::vector &inp mUnits[3].kernel = kernel; mUnits[3].localWorkSize = {lws[0], lws[1], lws[2]}; mUnits[3].globalWorkSize = {gws[0], gws[1], gws[2]}; - recordKernel2d(mUnits[2].kernel, gws, lws, runtime); + recordKernel3d(mUnits[3].kernel, gws, lws, runtime); } endRecord(runtime, mRecording); diff --git a/source/backend/opencl/execution/image/PoolExecution.cpp b/source/backend/opencl/execution/image/PoolExecution.cpp index b28585d91..f7fa44b3f 100644 --- a/source/backend/opencl/execution/image/PoolExecution.cpp +++ b/source/backend/opencl/execution/image/PoolExecution.cpp @@ -54,19 +54,6 @@ PoolExecution::PoolExecution(const std::vector &inputs, const MNN::Op mPaddings[0] = mPoolParams->padY() * 2; mPaddings[1] = mPoolParams->padX() * 2; mPadType = mPoolParams->padType(); - if (mPadType == PoolPadType_VALID) { - mPaddings[0] = 0; - mPaddings[1] = 0; - } - std::set buildOptions; - std::string kernelName = "pooling"; - auto runtime = mOpenCLBackend->getOpenCLRuntime(); - - if (mPoolType == PoolType_AVEPOOL) { - buildOptions.emplace("-DPOOL_AVG"); - } - mKernel = runtime->buildKernel("pooling", kernelName, buildOptions); - mMaxWorkGroupSize = static_cast(runtime->getMaxWorkGroupSize(mKernel)); } ErrorCode PoolExecution::onResize(const std::vector &inputs, const std::vector &outputs) { @@ -76,6 +63,8 @@ ErrorCode PoolExecution::onResize(const std::vector &inputs, const std startRecord(mOpenCLBackend->getOpenCLRuntime(), mRecording); auto input = inputs[0]; auto output = outputs[0]; + bool returnRedice = outputs.size() == 2; + auto redice = returnRedice ? outputs[1] : outputs[0]; if (mPoolParams->isGlobal()) { std::vector inputShape = tensorShapeFormat(inputs[0]); @@ -90,7 +79,38 @@ ErrorCode PoolExecution::onResize(const std::vector &inputs, const std mPaddings[0] = padNeededHeight; mPaddings[1] = padNeededWidth; + }else if (mPadType == PoolPadType_VALID) { + mPaddings[0] = mPaddings[1] = 0; + } + + auto countType = mPoolParams->countType(); + if (mPoolParams->pads() != nullptr && mPadType == PoolPadType_CAFFE) { + mPadType = PoolPadType_VALID; + } + + if (countType == MNN::AvgPoolCountType_DEFAULT) { + if (mPadType == MNN::PoolPadType_CAFFE) { + countType = MNN::AvgPoolCountType_INCLUDE_PADDING; + } else { + countType = MNN::AvgPoolCountType_EXCLUDE_PADDING; + } + } + + std::set buildOptions; + std::string kernelName = "pooling"; + auto runtime = mOpenCLBackend->getOpenCLRuntime(); + + if (mPoolType == PoolType_AVEPOOL) { + buildOptions.emplace("-DPOOL_AVG"); + if(countType == MNN::AvgPoolCountType_INCLUDE_PADDING){ + buildOptions.emplace("-DCOUNT_INCLUDE_PADDING"); + } } + if(returnRedice){ + buildOptions.emplace("-DRETURN_REDICE"); + } + mKernel = runtime->buildKernel("pooling", kernelName, buildOptions); + mMaxWorkGroupSize = static_cast(runtime->getMaxWorkGroupSize(mKernel)); MNN_ASSERT(mDilations[0] == 1 && mDilations[1] == 1); @@ -131,6 +151,7 @@ ErrorCode PoolExecution::onResize(const std::vector &inputs, const std ret |= mKernel.setArg(idx++, sizeof(strideShape), strideShape); ret |= mKernel.setArg(idx++, sizeof(kernelShape), kernelShape); ret |= mKernel.setArg(idx++, openCLImage(output)); + ret |= mKernel.setArg(idx++, openCLImage(redice)); MNN_CHECK_CL_SUCCESS(ret, "setArg PoolExecution"); recordKernel3d(mKernel, mGlobalWorkSize, mLocalWorkSize, mOpenCLBackend->getOpenCLRuntime()); diff --git a/source/backend/opencl/execution/image/UnaryExecution.cpp b/source/backend/opencl/execution/image/UnaryExecution.cpp index 8c472a23a..8e4ae8500 100644 --- a/source/backend/opencl/execution/image/UnaryExecution.cpp +++ b/source/backend/opencl/execution/image/UnaryExecution.cpp @@ -17,17 +17,22 @@ namespace OpenCL { UnaryExecution::UnaryExecution(const std::string& compute, Backend* backend) : Execution(backend) { auto openCLBackend = static_cast(backend); std::set buildOptions; - buildOptions.emplace(" -DOPERATOR=" + compute); - // FUNC_PRINT_ALL(buildOptions.begin()->c_str(), s); - auto runtime = openCLBackend->getOpenCLRuntime(); - mKernel = runtime->buildKernel("unary", "unary", buildOptions); - mMaxWorkGroupSize = static_cast(runtime->getMaxWorkGroupSize(mKernel)); + mBuildOptions.emplace(" -DOPERATOR=" + compute); } ErrorCode UnaryExecution::onResize(const std::vector& inputs, const std::vector& outputs) { Tensor* input = inputs[0]; Tensor* output = outputs[0]; auto openCLBackend = static_cast(backend()); - startRecord(openCLBackend->getOpenCLRuntime(), mRecording); + + auto dataType = inputs[0]->getType(); + if (dataType.code == halide_type_int){ + mBuildOptions.emplace("-DOPENCL_INPUT_INT"); + } + // FUNC_PRINT_ALL(buildOptions.begin()->c_str(), s); + auto runtime = openCLBackend->getOpenCLRuntime(); + mKernel = runtime->buildKernel("unary", "unary", mBuildOptions); + mMaxWorkGroupSize = static_cast(runtime->getMaxWorkGroupSize(mKernel)); + startRecord(runtime, mRecording); std::vector inputShape = tensorShapeFormat(input); std::vector outputShape = tensorShapeFormat(output); @@ -59,7 +64,7 @@ ErrorCode UnaryExecution::onResize(const std::vector& inputs, const std localWS3DDefault(mGlobalWorkSize, mMaxWorkGroupSize, openCLBackend->getOpenCLRuntime(), name, mKernel).first; mLocalSize = lws; recordKernel3d(mKernel, mGlobalWorkSize, mLocalSize, openCLBackend->getOpenCLRuntime()); - endRecord(openCLBackend->getOpenCLRuntime(), mRecording); + endRecord(runtime, mRecording); return NO_ERROR; } diff --git a/source/backend/opencl/execution/image/UnaryExecution.hpp b/source/backend/opencl/execution/image/UnaryExecution.hpp index d24aceab3..1fa8b4fe6 100644 --- a/source/backend/opencl/execution/image/UnaryExecution.hpp +++ b/source/backend/opencl/execution/image/UnaryExecution.hpp @@ -33,6 +33,7 @@ class UnaryExecution : public Execution, public CommonExtension { uint32_t mMaxWorkGroupSize; std::vector mGlobalWorkSize = {1, 1, 1}; std::vector mLocalSize = {1, 1, 1}; + std::set mBuildOptions; }; } // namespace OpenCL diff --git a/source/backend/opengl/GLBackend.hpp b/source/backend/opengl/GLBackend.hpp index 95783d7cd..b36140258 100644 --- a/source/backend/opengl/GLBackend.hpp +++ b/source/backend/opengl/GLBackend.hpp @@ -99,7 +99,7 @@ class GLBackend : public Backend { virtual void onExecuteEnd() const override; - ErrorCode onResizeEnd() { return NO_ERROR; } + virtual ErrorCode onResizeEnd() override { return NO_ERROR; } /// get execution virtual Execution* onCreate(const std::vector& inputs, const std::vector& outputs, diff --git a/source/core/Pipeline.cpp b/source/core/Pipeline.cpp index e83db1a96..d5a9953f7 100644 --- a/source/core/Pipeline.cpp +++ b/source/core/Pipeline.cpp @@ -79,6 +79,10 @@ static bool _supportQuant(const Op* op, const std::vector& inputs, cons return true; case OpType_LayerNorm: return true; + case OpType_UnaryOp: + return true; + case OpType_PReLU: + return true; default: break; } @@ -258,7 +262,7 @@ ErrorCode Pipeline::encode(bool supportDebug, bool permitCodegen) { #endif } // Propagate Scale and insert new command - if (mIsQuantModel && (mBackend->type() == MNN_FORWARD_CPU || mBackend->type() == MNN_FORWARD_CPU_EXTENSION || mBackend->type() == MNN_FORWARD_CUDA || mBackend->type() == MNN_FORWARD_NN)) { + if (mIsQuantModel && (mBackend->type() == MNN_FORWARD_CPU || mBackend->type() == MNN_FORWARD_CPU_EXTENSION || mBackend->type() == MNN_FORWARD_CUDA || mBackend->type() == MNN_FORWARD_NN || mBackend->type() == MNN_FORWARD_OPENCL)) { // get propagate map using PropagateMap = std::map>; PropagateMap forwardMap, backwardMap; @@ -768,6 +772,12 @@ static ErrorCode _InsertCopy(Schedule::PipelineInfo& mInfo, std::mapworkOutputs[v] = iter.workInputs[i]; + cmd.workInputs = {iter.workInputs[i]}; + } + } } } } diff --git a/source/geometry/GeometryDilation2D.cpp b/source/geometry/GeometryDilation2D.cpp index 0e1aa3149..26d6f960e 100644 --- a/source/geometry/GeometryDilation2D.cpp +++ b/source/geometry/GeometryDilation2D.cpp @@ -108,7 +108,7 @@ class GeometryDilation2D : public GeometryComputer { } { std::shared_ptr maxValue; - maxValue.reset(Tensor::createDevice({depth, 1, computeNum})); + maxValue.reset(Tensor::createDevice({depth, 1, computeNum}, Tensor::CAFFE)); E = maxValue.get(); auto cmd = GeometryComputerUtils::makeReduce(ReductionType_MAXIMUM, D, E); res.extras.emplace_back(maxValue); diff --git a/source/geometry/GeometryPoolGrad.cpp b/source/geometry/GeometryPoolGrad.cpp index fdfcbd643..740417e1e 100644 --- a/source/geometry/GeometryPoolGrad.cpp +++ b/source/geometry/GeometryPoolGrad.cpp @@ -350,11 +350,12 @@ class GeometryPoolGrad : public GeometryComputer { { tmpOutput.reset(new Tensor); tmpOutput->buffer().type = halide_type_of(); - tmpOutput->buffer().dimensions = 4; - tmpOutput->setLength(0, ob); - tmpOutput->setLength(1, oc); - tmpOutput->setLength(2, ih); - tmpOutput->setLength(3, iw); + tmpOutput->buffer().dimensions = 5; + tmpOutput->setLength(0, 1); + tmpOutput->setLength(1, ob); + tmpOutput->setLength(2, oc); + tmpOutput->setLength(3, ih); + tmpOutput->setLength(4, iw); auto des = TensorUtils::getDescribe(tmpOutput.get()); // des->memoryType = Tensor::InsideDescribe::MEMORY_VIRTUAL; des->dimensionFormat = MNN_DATA_FORMAT_NCHW; diff --git a/source/math/Vec.hpp b/source/math/Vec.hpp index 45813ba87..fc4d8e0d4 100644 --- a/source/math/Vec.hpp +++ b/source/math/Vec.hpp @@ -46,6 +46,61 @@ struct Vec { Vec(std::array&& v) { value = std::move(v); } + VecType operator==(const VecType& lr) const { + VecType dst; + for (int i = 0; i < N; ++i) { + if (value[i] == lr.value[i]) { + dst.value[i] = 1; + } else { + dst.value[i] = 0; + } + } + return dst; + } + VecType operator<(const VecType& lr) const { + VecType dst; + for (int i = 0; i < N; ++i) { + if (value[i] < lr.value[i]) { + dst.value[i] = 1; + } else { + dst.value[i] = 0; + } + } + return dst; + } + VecType operator<=(const VecType& lr) const { + VecType dst; + for (int i = 0; i < N; ++i) { + if (value[i] <= lr.value[i]) { + dst.value[i] = 1; + } else { + dst.value[i] = 0; + } + } + return dst; + } + VecType operator>(const VecType& lr) const { + VecType dst; + for (int i = 0; i < N; ++i) { + if (value[i] > lr.value[i]) { + dst.value[i] = 1; + } else { + dst.value[i] = 0; + } + } + return dst; + } + VecType operator>=(const VecType& lr) const { + VecType dst; + for (int i = 0; i < N; ++i) { + if (value[i] >= lr.value[i]) { + dst.value[i] = 1; + } else { + dst.value[i] = 0; + } + } + return dst; + } VecType operator*(const VecType& lr) const { VecType dst; for (int i = 0; i < N; ++i) { @@ -157,10 +212,172 @@ struct Vec { }; #ifdef MNN_USE_NEON +template<> +struct Vec { + using VecType = Vec; + int32x4_t value; + int32x4_t one = vdupq_n_s32(1); + int32x4_t zero = vdupq_n_s32(0); + Vec() { + } + Vec(const int32_t v) { + value = vdupq_n_s32(v); + } + Vec(const float v) { + value = vdupq_n_s32((int32_t)v); + } + Vec(const float32x4_t v) { + value = reinterpret_cast(v); + } + Vec(const int32x4_t v) { + value = v; + } + Vec(const VecType& lr) { + value = lr.value; + } + Vec(const VecType&& lr) { + value = std::move(lr.value); + } + float operator[](size_t i) { + return value[i]; + } + static VecType load(const float* addr) { + VecType v = { reinterpret_cast(vld1q_f32(addr)) }; + return v; + } + static VecType broadcast(const float* addr) { + VecType dst = { reinterpret_cast(vld1q_dup_f32(addr)) }; + return dst; + } + static VecType broadcast(const int32_t* addr) { + VecType dst = { vld1q_dup_s32(addr) }; + return dst; + } + static VecType load(const int32_t* addr) { + VecType v = { vld1q_s32(addr) }; + return v; + } + static void save(float* addr, const VecType& v) { + vst1q_f32(addr, reinterpret_cast(v.value)); + } + static void save(int32_t* addr, const VecType& v) { + vst1q_s32(addr, v.value); + } + static VecType max(const VecType& v1, const VecType& v2) { + VecType dst = { vmaxq_s32(v1.value, v2.value) }; + return dst; + } + static VecType min(const VecType& v1, const VecType& v2) { + VecType dst = { vminq_s32(v1.value, v2.value) }; + return dst; + } + static VecType fma(const VecType& v1, const VecType& v2, const VecType& v3) { + VecType dst = {vmlaq_s32(v1.value, v2.value, v3.value)}; + return dst; + } + static VecType fms(const VecType& v1, const VecType& v2, const VecType& v3) { + VecType dst = {vmlsq_s32(v1.value, v2.value, v3.value)}; + return dst; + } + static inline void transpose4(VecType& vec0, VecType& vec1, VecType& vec2, VecType& vec3) { +#ifdef __aarch64__ + auto m0 = vtrn1q_s32(vec0.value, vec1.value); + auto m1 = vtrn2q_s32(vec0.value, vec1.value); + auto m2 = vtrn1q_s32(vec2.value, vec3.value); + auto m3 = vtrn2q_s32(vec2.value, vec3.value); + vec0.value = vtrn1q_s64(reinterpret_cast(m0), reinterpret_cast(m2)); + vec1.value = vtrn1q_s64(reinterpret_cast(m1), reinterpret_cast(m3)); + vec2.value = vtrn2q_s64(reinterpret_cast(m0), reinterpret_cast(m2)); + vec3.value = vtrn2q_s64(reinterpret_cast(m1), reinterpret_cast(m3)); +#else + + auto m0m1 = vtrnq_s32(vec0.value, vec1.value); + auto m2m3 = vtrnq_s32(vec2.value, vec3.value); + vec0.value = m0m1.val[0]; + vec1.value = m0m1.val[1]; + vec2.value = m2m3.val[0]; + vec3.value = m2m3.val[1]; + vec0.value = vsetq_lane_s64(vgetq_lane_s64(m2m3.val[0], 0), vec0.value, 1); + vec1.value = vsetq_lane_s64(vgetq_lane_s64(m2m3.val[1], 0), vec1.value, 1); + vec2.value = vsetq_lane_s64(vgetq_lane_s64(m0m1.val[0], 1), vec2.value, 0); + vec3.value = vsetq_lane_s64(vgetq_lane_s64(m0m1.val[1], 1), vec3.value, 0); +#endif + } + + VecType operator+(const VecType& lr) const { + VecType dst = { vaddq_s32(value, lr.value) }; + return dst; + } + VecType operator-(const VecType& lr) const { + VecType dst = { vsubq_s32(value, lr.value) }; + return dst; + } + VecType operator+=(const VecType& lr) { + value = vaddq_s32(value, lr.value); + return *this; + } + VecType operator-=(const VecType& lr) { + value = vsubq_s32(value, lr.value); + return *this; + } + VecType operator*(int32_t lr) const { + VecType dst = { vmulq_n_s32(value, lr) }; + return dst; + } + VecType operator*(float lr) const { + VecType dst = { vmulq_n_s32(value, (int32_t)lr) }; + return dst; + } + VecType operator*(const VecType& lr) const { + VecType dst = { vmulq_s32(value, lr.value) }; + return dst; + } + VecType& operator=(const VecType& lr) { + value = lr.value; + return *this; + } + VecType& operator=(const VecType&& lr) { + value = std::move(lr.value); + return *this; + } + VecType operator-() { + VecType dst = { vnegq_s32(value) }; + return dst; + } + VecType operator<(const VecType& lr) const { + uint32x4_t res = vcltq_s32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator>(const VecType& lr) const { + uint32x4_t res = vcgtq_s32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator<=(const VecType& lr) const { + uint32x4_t res = vcleq_s32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator>=(const VecType& lr) const { + uint32x4_t res = vcgeq_s32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator==(const VecType& lr) const { + uint32x4_t res = vceqq_s32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } +}; + template<> struct Vec { using VecType = Vec; + using VecTypeInt32 = Vec; float32x4_t value; + int32x4_t one = vdupq_n_s32(1); + int32x4_t zero = vdupq_n_s32(0); Vec() { } Vec(const float v) { @@ -193,6 +410,9 @@ struct Vec { static void save(float* addr, const VecType& v) { vst1q_f32(addr, v.value); } + static void save(float* addr, const VecTypeInt32& v) { + vst1q_f32(addr, reinterpret_cast(v.value)); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { vmaxq_f32(v1.value, v2.value) }; return dst; @@ -280,14 +500,172 @@ struct Vec { VecType dst = { vnegq_f32(value) }; return dst; } + VecType operator<(const VecType& lr) const { + uint32x4_t res = vcltq_f32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator>(const VecType& lr) const { + uint32x4_t res = vcgtq_f32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator<=(const VecType& lr) const { + uint32x4_t res = vcleq_f32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator>=(const VecType& lr) const { + uint32x4_t res = vcgeq_f32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } + VecType operator==(const VecType& lr) const { + uint32x4_t res = vceqq_f32(value, lr.value); + VecType dst = { vbslq_s32(res, one, zero) }; + return dst; + } }; #elif defined(MNN_USE_SSE) +template<> +struct Vec { + using VecType = Vec; + using VecTypeArray = std::array; + __m128i value; + __m128i one = _mm_set1_epi32(1); + VecType operator+(const VecType& lr) const { + VecType dst = { _mm_add_epi32(value, lr.value) }; + return dst; + } + VecType operator-(const VecType& lr) const { + VecType dst = { _mm_sub_epi32(value, lr.value) }; + return dst; + } + VecType operator+=(const VecType& lr) { + value = _mm_add_epi32(value, lr.value); + return *this; + } + VecType operator-=(const VecType& lr) { + value = _mm_sub_epi32(value, lr.value); + return *this; + } + VecType operator*(const VecType& lr) const { + VecType dst = {_mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(value), _mm_cvtepi32_ps(lr.value)))}; + return dst; + } + + VecType& operator=(const VecType& lr) { + value = lr.value; + return *this; + } + VecType operator==(const VecType& lr) const { + __m128i mask = _mm_cmpeq_epi32(value, lr.value); + VecType dst = { _mm_and_si128(one, mask) }; + return dst; + } + VecType operator<(const VecType& lr) const { + __m128i mask = _mm_cmplt_epi32(value, lr.value); + VecType dst = { _mm_and_si128(one, mask) }; + return dst; + } + VecType operator<=(const VecType& lr) const { + __m128i mask = _mm_cmpgt_epi32(value, lr.value); + VecType dst = { _mm_andnot_si128(mask, one) }; + return dst; + } + VecType operator>(const VecType& lr) const { + __m128i mask = _mm_cmpgt_epi32(value, lr.value); + VecType dst = { _mm_and_si128(one, mask) }; + return dst; + } + VecType operator>=(const VecType& lr) const { + __m128i mask = _mm_cmplt_epi32(value, lr.value); + VecType dst = { _mm_andnot_si128(mask, one) }; + return dst; + } + VecType operator-() { + VecType dst; +#if defined(_MSC_VER) + dst.value = _mm_cvtps_epi32(_mm_xor_ps(_mm_cvtepi32_ps(value), _mm_set1_ps(-0.f))); // Using unary operation to SSE vec is GCC extension. We can not do this directly in MSVC. +#else + dst.value = -value; +#endif + return dst; + } + Vec() { + } + Vec(const float v) { + int u = static_cast(v); + value = _mm_set_epi32(u, u, u, u); + } + Vec(const int32_t v) { + value = _mm_set_epi32(v, v, v, v); + } + Vec(__m128i&& v) { + value = v; + } + Vec(__m128&& v) { + value = _mm_castps_si128(v); + } + Vec(const VecType& lr) { + value = lr.value; + } + float operator[](size_t i) { +#if defined(_MSC_VER) // X64 native only mandatory support SSE and SSE2 extension, and we can not find intrinsic function to extract element directly by index in SSE and SSE2 extension. + int32_t temp[4]; + _mm_storeu_si128((__m128i*)temp, value); + return temp[i]; +#else + return value[i]; +#endif + } + static VecType load(const int32_t* addr) { + VecType v = {_mm_loadu_si128((__m128i const*)(addr))}; + return v; + } + static VecType broadcast(const int32_t* addr) { + int32_t arr[4] = {*addr, 0, 0, 0}; + VecType dst = { _mm_loadu_si128((__m128i const*)(arr)) }; + return dst; + } + static void save(int32_t* addr, const VecType& v) { + _mm_storeu_si128((__m128i*)addr, v.value); + } + static VecType max(const VecType& v1, const VecType& v2) { + VecType dst = {_mm_cvtps_epi32(_mm_max_ps(_mm_cvtepi32_ps(v1.value), _mm_cvtepi32_ps(v2.value)))}; + return dst; + } + static VecType min(const VecType& v1, const VecType& v2) { + VecType dst = {_mm_cvtps_epi32(_mm_min_ps(_mm_cvtepi32_ps(v1.value), _mm_cvtepi32_ps(v2.value)))}; + return dst; + } + static VecType fma(const VecType& v1, const VecType& v2, const VecType& v3) { + return v1 + v2 * v3; // TODO: use fma instruction + } + static VecType fms(const VecType& v1, const VecType& v2, const VecType& v3) { + return v1 - v2 * v3; // TODO: use fma instruction + } + static inline void transpose4(VecType& vec0, VecType& vec1, VecType& vec2, VecType& vec3) { + __m128 tmp3, tmp2, tmp1, tmp0; + tmp0 = _mm_unpacklo_ps(_mm_castsi128_ps(vec0.value), _mm_castsi128_ps(vec1.value)); + tmp2 = _mm_unpacklo_ps(_mm_castsi128_ps(vec2.value), _mm_castsi128_ps(vec3.value)); + tmp1 = _mm_unpackhi_ps(_mm_castsi128_ps(vec0.value), _mm_castsi128_ps(vec1.value)); + tmp3 = _mm_unpackhi_ps(_mm_castsi128_ps(vec2.value), _mm_castsi128_ps(vec3.value)); + vec0.value = _mm_castps_si128(_mm_movelh_ps(tmp0, tmp2)); + vec1.value = _mm_castps_si128(_mm_movehl_ps(tmp2, tmp0)); + vec2.value = _mm_castps_si128(_mm_movelh_ps(tmp1, tmp3)); + vec3.value = _mm_castps_si128(_mm_movehl_ps(tmp3, tmp1)); + } +}; + template<> struct Vec { using VecType = Vec; + using VecTypeInt32 = Vec; using VecTypeArray = std::array; __m128 value; + __m128i one = _mm_set1_epi32(1); VecType operator+(const VecType& lr) const { VecType dst = { _mm_add_ps(value, lr.value) }; return dst; @@ -326,6 +704,31 @@ struct Vec { #endif return dst; } + VecType operator==(const VecType& lr) const { + __m128i mask = _mm_cmpeq_epi32(_mm_castps_si128(value), _mm_castps_si128(lr.value)); + VecType dst = { _mm_castsi128_ps(_mm_and_si128(one, mask)) }; + return dst; + } + VecType operator<(const VecType& lr) const { + __m128i mask = _mm_cmplt_epi32(_mm_castps_si128(value), _mm_castps_si128(lr.value)); + VecType dst = { _mm_castsi128_ps(_mm_and_si128(one, mask)) }; + return dst; + } + VecType operator<=(const VecType& lr) const { + __m128 mask = _mm_cmple_ps(value, lr.value); + VecType dst = { _mm_castsi128_ps(_mm_and_si128(one, _mm_castps_si128(mask))) }; + return dst; + } + VecType operator>(const VecType& lr) const { + __m128 mask = _mm_cmpgt_ps(value, lr.value); + VecType dst = { _mm_castsi128_ps(_mm_and_si128(one, _mm_castps_si128(mask))) }; + return dst; + } + VecType operator>=(const VecType& lr) const { + __m128 mask = _mm_cmpge_ps(value, lr.value); + VecType dst = { _mm_castsi128_ps(_mm_and_si128(one, _mm_castps_si128(mask))) }; + return dst; + } Vec() { } Vec(const float v) { @@ -354,13 +757,12 @@ struct Vec { VecType dst = { _mm_load_ss(addr) }; return dst; } - static VecType load(const int32_t* addr) { - VecType v = { _mm_cvtepi32_ps(_mm_loadu_si128((__m128i const*)(addr))) }; - return v; - } static void save(float* addr, const VecType& v) { _mm_storeu_ps(addr, v.value); } + static void save(float* addr, const VecTypeInt32& v) { + _mm_storeu_ps(addr, _mm_castsi128_ps(v.value)); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { _mm_max_ps(v1.value, v2.value) }; return dst; diff --git a/source/shape/ShapeBinaryOp.cpp b/source/shape/ShapeBinaryOp.cpp index 1e034ab6d..820128208 100644 --- a/source/shape/ShapeBinaryOp.cpp +++ b/source/shape/ShapeBinaryOp.cpp @@ -48,11 +48,13 @@ class BinaryOpComputer : public SizeComputer { } if (input0->getType().code != input1->getType().code) { +#ifdef DEBUG MNN_PRINT("Error for binary op: input0's type != input1's type, %d != %d, optype:%d, ", input0->getType().code, input1->getType().code, opType); if (nullptr != op->name()) { MNN_PRINT("op name: %s", op->name()->c_str()); } MNN_PRINT("\n"); +#endif return false; } diff --git a/source/shape/ShapePool.cpp b/source/shape/ShapePool.cpp index d18e26e13..58411c371 100644 --- a/source/shape/ShapePool.cpp +++ b/source/shape/ShapePool.cpp @@ -17,10 +17,17 @@ class PoolSizeComputer : public SizeComputer { virtual bool onComputeSize(const MNN::Op* op, const std::vector& inputs, const std::vector& outputs) const override { MNN_ASSERT(1 == inputs.size()); - MNN_ASSERT(1 == outputs.size()); + MNN_ASSERT(2 >= outputs.size()); auto input = inputs[0]; auto output = outputs[0]; + bool returnRedice = outputs.size() == 2; + Tensor *indice; + if(returnRedice){ + indice = outputs[1]; + ::memcpy(indice->buffer().dim, input->buffer().dim, input->buffer().dimensions * sizeof(halide_dimension_t)); + indice->buffer().dimensions = input->dimensions(); + } ::memcpy(output->buffer().dim, input->buffer().dim, input->buffer().dimensions * sizeof(halide_dimension_t)); output->buffer().dimensions = input->dimensions(); @@ -76,12 +83,24 @@ class PoolSizeComputer : public SizeComputer { if (format == MNN_DATA_FORMAT_NHWC) { output->buffer().dim[2].extent = outw; output->buffer().dim[1].extent = outh; + if(returnRedice){ + indice->buffer().dim[2].extent = outw; + indice->buffer().dim[1].extent = outh; + } } else { output->buffer().dim[3].extent = outw; output->buffer().dim[2].extent = outh; + if(returnRedice){ + indice->buffer().dim[3].extent = outw; + indice->buffer().dim[2].extent = outh; + } } TensorUtils::getDescribe(outputs[0])->dimensionFormat = format; output->buffer().type = input->buffer().type; + if(returnRedice){ + TensorUtils::getDescribe(outputs[1])->dimensionFormat = format; + indice->buffer().type = halide_type_of(); + } return true; } diff --git a/source/shape/ShapeReduction.cpp b/source/shape/ShapeReduction.cpp index 22dba9af3..01cfff5d2 100644 --- a/source/shape/ShapeReduction.cpp +++ b/source/shape/ShapeReduction.cpp @@ -29,7 +29,14 @@ class ReductionComputer : public SizeComputer { auto reduce = op->main_as_ReductionParam(); output->buffer().type = inputs[0]->buffer().type; if (nullptr == reduce->dim() && inputs.size() == 1) { - output->buffer().dimensions = 0; + if (reduce->keepDims()) { + output->buffer().dimensions = inputs[0]->dimensions(); + for (int i = 0; i < inputs[0]->dimensions(); i++) { + output->setLength(i, 1); + } + } else { + output->buffer().dimensions = 0; + } return true; } uint8_t reduceMask[MNN_MAX_TENSOR_DIM]; diff --git a/source/shape/ShapeScatterNd.cpp b/source/shape/ShapeScatterNd.cpp index 97d58bad1..acb152748 100644 --- a/source/shape/ShapeScatterNd.cpp +++ b/source/shape/ShapeScatterNd.cpp @@ -20,17 +20,13 @@ class ShapeScatterNd : public SizeComputer { auto updates = inputs[1]; auto shape = inputs[2]; auto output = outputs[0]; - MNN_CHECK(shape->dimensions() == 1, "shape rank should be one"); + //MNN_CHECK(shape->dimensions() == 1, "shape rank should be one"); const int indicesDimension = indices->dimensions(); //MNN_CHECK(indices->length(indicesDimension - 1) == 1, "indices.shape[-1] = shape.rank"); const int outerDims = indicesDimension - 1; - for (int i = 0; i < outerDims; ++i) { - MNN_CHECK(indices->length(i) == updates->length(i), "indices shape does not match updates'"); - } - const int dimension = shape->length(0); - MNN_CHECK(updates->dimensions() == dimension, "updates dimension should be equal to given shape"); + //MNN_CHECK(updates->dimensions() == dimension, "updates dimension should be equal to given shape"); output->buffer().dimensions = dimension; diff --git a/test.sh b/test.sh index 0039628f7..c70503027 100755 --- a/test.sh +++ b/test.sh @@ -267,6 +267,12 @@ unit_test() { echo '### 多线程单元测试失败,测试终止!' failed fi + + ./run_test.out op 3 1 4 + if [ $? -ne 0 ]; then + echo '### OpenCL单元测试失败,测试终止!' + failed + fi } model_test() { @@ -281,6 +287,12 @@ model_test() { echo '### 静态模型测试失败,测试终止!' failed fi + + ../tools/script/modelTest.py ~/AliNNModel 3 0.002 1 + if [ $? -ne 0 ]; then + echo '### OpenCL模型测试失败,测试终止!' + failed + fi } onnx_convert_test() { @@ -462,10 +474,17 @@ android_unit_test() { echo '### Android单元测试卷积FP16多线程失败,测试终止!' failed fi + adb shell "cd /data/local/tmp/MNN&&export LD_LIBRARY_PATH=.&&./run_test.out op 3 1 4 $1" + if [ $? -ne 0 ]; then + echo '### Android单元测试OpenCL失败,测试终止!' + failed + fi } android_model_test() { fail_num=0 pass_num=0 + fail_cl_num=0 + pass_cl_num=0 models=`ls ~/AliNNModel/OpTestResource/` for model in $models do @@ -475,6 +494,12 @@ android_model_test() { else pass_num=$[$pass_num+1] fi + adb shell "cd /data/local/tmp/MNN&&export LD_LIBRARY_PATH=.&&./testModel.out ../AliNNModel/OpTestResource/$model/temp.bin ../AliNNModel/OpTestResource/$model/input_0.txt ../AliNNModel/OpTestResource/$model/output_0.txt 3 0.002 1" + if [ $? -ne 0 ]; then + fail_cl_num=$[$fail_cl_num+1] + else + pass_cl_num=$[$pass_cl_num+1] + fi done models=`ls ~/AliNNModel/TestResource/` @@ -486,6 +511,12 @@ android_model_test() { else pass_num=$[$pass_num+1] fi + adb shell "cd /data/local/tmp/MNN&&export LD_LIBRARY_PATH=.&&./testModel.out ../AliNNModel/TestResource/$model/temp.bin ../AliNNModel/TestResource/$model/input_0.txt ../AliNNModel/TestResource/$model/output.txt 3 0.002 1" + if [ $? -ne 0 ]; then + fail_cl_num=$[$fail_cl_num+1] + else + pass_cl_num=$[$pass_cl_num+1] + fi done models=`ls ~/AliNNModel/TestWithDescribe/` @@ -497,12 +528,23 @@ android_model_test() { else pass_num=$[$pass_num+1] fi + adb shell "cd /data/local/tmp/MNN&&export LD_LIBRARY_PATH=.&&./testModelWithDescribe.out ../AliNNModel/TestWithDescribe/$model/temp.bin ../AliNNModel/TestWithDescribe/$model/config.txt 3 0.002 1" + if [ $? -ne 0 ]; then + fail_cl_num=$[$fail_cl_num+1] + else + pass_cl_num=$[$pass_cl_num+1] + fi done printf "TEST_NAME_ANDROID_MODEL_TEST_$1: Android_$1模型测试\nTEST_CASE_AMOUNT_ANDROID_MODEL_TEST_$1: {\"blocked\":0,\"failed\":$fail_num,\"passed\":$pass_num,\"skipped\":0}\n" if [ $fail_num -ne 0 ]; then echo '### Android模型测试失败,测试终止!' failed fi + printf "TEST_NAME_ANDROID_MODEL_OPENCL_TEST_$1: Android_$1模型测试\nTEST_CASE_AMOUNT_ANDROID_MODEL_TEST_$1: {\"blocked\":0,\"failed\":$fail_cl_num,\"passed\":$pass_cl_num,\"skipped\":0}\n" + if [ $fail_cl_num -ne 0 ]; then + echo '### Android OpenCL后端模型测试失败,测试终止!' + failed + fi } android_test() { diff --git a/test/op/BinaryOPTest.cpp b/test/op/BinaryOPTest.cpp index c1afda064..a7f25ef8f 100644 --- a/test/op/BinaryOPTest.cpp +++ b/test/op/BinaryOPTest.cpp @@ -33,6 +33,10 @@ class BinaryTestCommon : public MNNTestCase { for (int i = 0; i < shape_y.size(); ++i) { size_out *= shape_out[i]; } + if (format == NC4HW4 && data_x.size() > size_x) { + size_x = shape_x[0] * UP_DIV(shape_x[1], 4) * shape_x[2] * shape_x[3] * 4; + size_y = shape_y[0] * UP_DIV(shape_y[1], 4) * shape_y[2] * shape_y[3] * 4; + } auto input_x = _Input(shape_x, format, halide_type_of()); auto input_y = _Input(shape_y, format, halide_type_of()); @@ -53,9 +57,11 @@ class BinaryTestCommon : public MNNTestCase { input_x->unMap(); input_y->unMap(); auto output = opFunc(input_x, input_y); + if (quantScales[2] != -100){ output->writeScaleMap(quantScales[2], zeroPoints[2]); } + auto gotOutput = output->template readMap(); auto shape_got = output->getInfo()->dim; @@ -69,7 +75,7 @@ class BinaryTestCommon : public MNNTestCase { return false; } } - if (quantScales.size() > 0) { + if (quantScales[0] > 0) { for (int i = 0; i < size_out; ++i) { auto error = (int32_t)data_out[i] - (int32_t)gotOutput[i]; if (error * error > 1) { @@ -80,6 +86,28 @@ class BinaryTestCommon : public MNNTestCase { } return true; } + std::vector computeOut(size_out); + std::vector targetOut(size_out); + if (format == NC4HW4) { + int ob = output->getInfo()->dim[0]; + int oc = output->getInfo()->dim[1]; + int plane = output->getInfo()->dim[2] * output->getInfo()->dim[3]; + for (int b = 0; b < ob; ++b){ + for (int c = 0; c < oc; ++c) { + for (int p = 0; p < plane; ++p) { + int idx0 = p + c * plane + b * oc * plane; + int idx1 = (c % 4) + 4 * p + 4 * plane * b + 4 * plane * ob * (c / 4); + computeOut[idx0] = gotOutput[idx1]; + targetOut[idx0] = data_out[idx1]; + } + } + } + if (!checkVectorByRelativeError(computeOut.data(), targetOut.data(), size_out, threshold)) { + MNN_ERROR("%s test failed!\n", name.c_str()); + return false; + } + return true; + } if (!checkVectorByRelativeError(gotOutput, data_out.data(), size_out, threshold)) { MNN_ERROR("%s test failed!\n", name.c_str()); return false; @@ -281,18 +309,28 @@ class GreaterTest : public BinaryTestCommon { public: virtual ~GreaterTest() = default; virtual bool run(int precision) { - return test(MNN::Express::_Greater, "GreaterTest", 0, + auto res = test(MNN::Express::_Greater, "GreaterTest", 0, + {1, 2, 3, 4, 5, 6, 7, 8}, + {3, 4}, + {0, 0, 0, 0, 1, 1, 1, 1}, + {4, 2}, {2}, {4, 2}); + return res && test(MNN::Express::_Greater, "GreaterTest", 0, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, {3.0, 4.0}, {0, 0, 0, 0, 1, 1, 1, 1}, - {4, 2}, {2}, {4, 2}); + {4, 2}, {2}, {4, 2});; } }; class GreaterEqualTest : public BinaryTestCommon { public: virtual ~GreaterEqualTest() = default; virtual bool run(int precision) { - return test(MNN::Express::_GreaterEqual, "GreaterEqualTest", 0, + auto res = test(MNN::Express::_GreaterEqual, "GreaterEqualTest", 0, + {1, 2, 3, 4, 5, 6, 7, 8}, + {3, 4}, + {0, 0, 1, 1, 1, 1, 1, 1}, + {4, 2}, {2}, {4, 2}); + return res && test(MNN::Express::_GreaterEqual, "GreaterEqualTest", 0, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, {3.0, 4.0}, {0, 0, 1, 1, 1, 1, 1, 1}, @@ -303,7 +341,17 @@ class LessTest : public BinaryTestCommon { public: virtual ~LessTest() = default; virtual bool run(int precision) { - return test(MNN::Express::_Less, "LessTest", 0, + auto res0 = test(MNN::Express::_Less, "LessTest", 0, + {1, 2, 3, 4, 5, 6, 7, 8}, + {3, 4}, + {1, 1, 0, 0, 0, 0, 0, 0}, + {4, 2}, {2}, {4, 2}); + auto res1 = test(MNN::Express::_LessEqual, "LessEqualTest", 0, + {1, 2, 3, 4, 5, 6, 7, 8}, + {3, 4}, + {1, 1, 1, 1, 0, 0, 0, 0}, + {4, 2}, {2}, {4, 2}); + return res0 && res1 && test(MNN::Express::_Less, "LessTest", 0, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, {3.0, 4.0}, {1, 1, 0, 0, 0, 0, 0, 0}, @@ -608,18 +656,18 @@ class AddC4Test : public BinaryTestCommon { } } std::vector i1 = { - -1.0, -2.0, 0.f, 0.f - -3.0, -4.0, 0.f, 0.f - -5.0, -6.0, 0.f, 0.f + -1.0, -2.0, 0.f, 0.f, + -3.0, -4.0, 0.f, 0.f, + -5.0, -6.0, 0.f, 0.f, -7.0, -8.0, 0.f, 0.f }; std::vector i0 = { 1.0f, 0.0f, 0.f, 0.f }; std::vector i2 = { - 0.0, -1.0, 0.f, 0.f - -2.0, -3.0, 0.f, 0.f - -4.0, -5.0, 0.f, 0.f + 0.0, -1.0, 0.f, 0.f, + -2.0, -3.0, 0.f, 0.f, + -4.0, -5.0, 0.f, 0.f, -6.0, -7.0, 0.f, 0.f }; return test(MNN::Express::_BiasAdd, "AddC4FloatTest", 0.01, diff --git a/test/op/PReLUTest.cpp b/test/op/PReLUTest.cpp index 810828d67..d73050317 100644 --- a/test/op/PReLUTest.cpp +++ b/test/op/PReLUTest.cpp @@ -35,4 +35,38 @@ class PreluTest : public MNNTestCase { return true; } }; + +class PreluTestInt8 : public MNNTestCase { +public: + virtual ~PreluTestInt8() = default; + virtual bool run(int precision) { + auto input = _Input({1, 4, 4, 2}, NCHW); + input->setName("input_tensor"); + // set input data + input->writeScaleMap(0.03567, 1.0); + const float inpudata[] = {-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; + auto inputPtr = input->writeMap(); + memcpy(inputPtr, inpudata, 4 * sizeof(float)); + input->unMap(); + input = _Convert(input, NC4HW4); + auto output = _PRelu(input, {3.0, 1.5, 1.5, 1.5}); + output = _Convert(output, NCHW); + const std::vector expectedOutput = {-3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, -3.0, + 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, + -4.5, -4.5, -4.5, -4.5, -4.5, -4.5, -4.5, -4.5, + 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0}; + output->writeScaleMap(0.03567, 1.0); + auto gotOutput = output->readMap(); + if (!checkVector(gotOutput, expectedOutput.data(), 4, 0.05)) { + MNN_ERROR("PreluTest test failed!\n"); + return false; + } + return true; + } +}; + MNNTestSuiteRegister(PreluTest, "op/prelu"); +MNNTestSuiteRegister(PreluTestInt8, "op/preluInt8"); diff --git a/test/op/ROIPoolingTest.cpp b/test/op/ROIPoolingTest.cpp index a148cc6ab..56eac70ce 100644 --- a/test/op/ROIPoolingTest.cpp +++ b/test/op/ROIPoolingTest.cpp @@ -1,4 +1,4 @@ -// +// // ROIPoolingTest.cpp // MNNTests // @@ -94,6 +94,21 @@ class ROIPoolingTest : public MNNTestCase { 0.7854, 0.7854, 0.6683, 0.8361, 1.3241, 1.3241, 0.8832, // 0.0775, 0.0775, 0.6683, 0.6683, 1.3241, 1.3241, -0.0514}; + + const std::vector outputDataGPUAdreno = {// + 0.0625, 0.0625, -0.4681, -0.4681, -0.4972, -0.4972, -0.0097, + // + 0.0625, 0.0625, 1.6140, 1.6140, -0.1184, -0.1889, -0.0097, + // + -0.4087, -0.0865, 1.6140, 1.6140, 0.5204, 1.2189, 1.2189, + // + -0.1671, 1.3580, 1.3580, 1.2407, 0.5204, 1.2189, 1.8368, + // + 0.7854, 1.3580, 1.3580, 0.8361, 0.8361, 0.8832, 1.8368, + // + 0.7854, 0.7854, 0.6683, 0.8361, 1.3241, 1.3241, 0.8832, + // + 0.0775, 0.0775, 0.6683, 0.6683, 1.3241, 1.3241, 0.3862}; auto input = _Input({n, c, h, w}, NCHW, halide_type_of()); auto roi = _Input({1, 1, 1, 5}, NCHW, halide_type_of()); @@ -102,11 +117,15 @@ class ROIPoolingTest : public MNNTestCase { output = _Convert(output, NCHW); ::memcpy(input->writeMap(), inputData.data(), inputData.size() * sizeof(float)); ::memcpy(roi->writeMap(), roiData.data(), roiData.size() * sizeof(float)); + auto computeOutput = output->readMap(); float errorScale = precision <= MNN::BackendConfig::Precision_High ? 1 : 20; - if (!checkVectorByRelativeError(output->readMap(), outputData.data(), outputData.size(), + if (!checkVectorByRelativeError(computeOutput, outputData.data(), outputData.size(), 0.001 * errorScale)) { - MNN_ERROR("%s(%s) test failed!\n", testOpName.c_str(), deviceName.c_str()); - return false; + if(!checkVectorByRelativeError(computeOutput, outputDataGPUAdreno.data(), outputDataGPUAdreno.size(), + 0.001 * errorScale)){ + MNN_ERROR("%s(%s) test failed!\n", testOpName.c_str(), deviceName.c_str()); + return false; + } } } // case2 diff --git a/test/op/UnaryTest.cpp b/test/op/UnaryTest.cpp index 062a47e8e..75e9c28c3 100644 --- a/test/op/UnaryTest.cpp +++ b/test/op/UnaryTest.cpp @@ -9,17 +9,503 @@ #include #include #include +#include "MNN_generated.h" #include "MNNTestSuite.h" #include "TestUtils.h" - +#define MNN_DEFAULT_FLATBUFFER_SIZE 32 using namespace MNN::Express; using namespace std; +using namespace MNN; +static VARP _UnaryInt8(VARP x, UnaryOpOperation operation, std::vector buffer) { + flatbuffers::FlatBufferBuilder builder(MNN_DEFAULT_FLATBUFFER_SIZE); + auto bufferOffset = builder.CreateVector(buffer); + UnaryOpBuilder parameter(builder); + parameter.add_tableInt8(bufferOffset); + parameter.add_opType(operation); + auto paOffset = parameter.Finish(); + OpBuilder opB(builder); + opB.add_main(paOffset.Union()); + opB.add_type(OpType_UnaryOp); + opB.add_main_type(OpParameter_UnaryOp); + builder.Finish(opB.Finish()); + std::shared_ptr extra(new BufferStorage); + extra->storage = builder.ReleaseRaw(extra->allocated_size, extra->offset); + return Variable::create(Expr::create(extra, {x}, 1)); +} +VARP _SquareInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + float fx = (i - zero[0]) * scale[0]; + int qx = roundf((fx * fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + } + return _UnaryInt8(x, UnaryOpOperation_SQUARE, buffer); +} +VARP _SqrtInt8(VARP x, float* scale, float* zero) { + std::vector buffer(255, 0); + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx >= 0) { + int qx = roundf(sqrt(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + buffer[i + 127] = qx; + } + } + return _UnaryInt8(x, UnaryOpOperation_SQRT, buffer); +} +VARP _RsqrtInt8(VARP x, float* scale, float* zero) +{ + std::vector buffer(255, 0); + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx >= 0) { + int qx = 127; + if (sqrt(fx) != 0) { + qx = roundf((1.0f / sqrt(fx)) / scale[1]) + zero[1]; + } + if (qx > 127) { + qx = 127; + } + buffer[i + 127] = qx; + } + } + return _UnaryInt8(x, UnaryOpOperation_RSQRT, buffer); +} +VARP _ExpInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(exp(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_EXP, buffer); +} +VARP _LogInt8(VARP x, float* scale, float* zero) { + std::vector buffer(255, 0); + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx < 0) { + continue; + } + int qx = roundf(log(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer[i + 127] = qx; + + } + return _UnaryInt8(x, UnaryOpOperation_LOG, buffer); +} +VARP _SinInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(sin(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_SIN, buffer); +} +VARP _CosInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(cos(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_COS, buffer); +} +VARP _TanInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(tan(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_TAN, buffer); +} +VARP _AsinInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(asin(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ASIN, buffer); +} +VARP _AcosInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(acos(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ACOS, buffer); +} +VARP _AcoshInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx < 1) { + buffer.push_back(0); + continue; + } + float val = acosh(fx); + int qx = roundf(acosh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ACOSH, buffer); +} +VARP _AsinhInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(asinh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ASINH, buffer); +} +VARP _AtanhInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx <= -1 || fx >= 1) { + buffer.push_back(0); + continue; + } + int qx = roundf(atanh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ATANH, buffer); +} +VARP _CoshInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(cosh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_COSH, buffer); +} +VARP _SinhInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(sinh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_SINH, buffer); +} +VARP _ErfInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(erf(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ERF, buffer); +} +VARP _ErfcInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(erfc(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ERFC, buffer); +} +VARP _ErfinvInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + const int kDegree = 9; + const std::vector w_less_than_5_constants = { + 2.81022636e-08f, 3.43273939e-07f, -3.5233877e-06f, + -4.39150654e-06f, 0.00021858087f, -0.00125372503f, + -0.00417768164f, 0.246640727f, 1.50140941f}; + const std::vector w_greater_than_5_constants = { + -0.000200214257f, 0.000100950558f, 0.00134934322f, + -0.00367342844f, 0.00573950773f, -0.0076224613f, + 0.00943887047f, 1.00167406f, 2.83297682f}; + for (int i = -127; i <= 127; ++i) { + float fx = (i - zero[0]) * scale[0]; + auto w = -log(-fx * fx + 1); + bool lt = (w < 5.0); + auto coefficient = [&](int i) { + if (lt) { + return w_less_than_5_constants[i]; + } else { + return w_greater_than_5_constants[i]; + } + }; + if (lt) { + w = w - 2.5; + } else { + w = sqrt(w) - 3.0; + } + auto p = coefficient(0); + for (int i = 1; i < kDegree; i++) { + p = coefficient(i) + p * w; + } + auto result = p * fx; + float val = 0; + if (fabsf(fabsf(fx) - 1) < 1e-8) { + val = std::numeric_limits::infinity(); + } else { + val = result; + } + int qx = roundf(val / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ERFINV, buffer); +} +VARP _AtanInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(atan(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_ATAN, buffer); +} +VARP _ReciprocalInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + float fx = (i - zero[0]) * scale[0]; + if (fx == 0) { + buffer.push_back(0); + continue; + } + int qx = roundf((1.0f / fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_RECIPROCAL, buffer); +} +VARP _Log1pInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + if (fx + 1 <= 0) { + buffer.push_back(-127); + continue; + } + int qx = roundf(log(fx + 1) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_LOG1P, buffer); +} +VARP _GeluInt8(VARP x, float* scale, float* zero) { + auto tanhf_poly = [](float value) -> float { + if (value > 5.0f) { + return 1.0f; + } else if (value <= -5.0f) { + return -1.0f; + } else { + float x2 = value * value; + float a = value * (135135.0f + x2 * (17325.0f + x2 * (378.0f + x2))); + float b = 135135.0f + x2 * (62370.0f + x2 * (3150.0f + x2 * 28.0f)); + return a / b; + } + }; + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + float fx = (i - zero[0]) * scale[0]; + float temp = 0.044715f * fx * fx * fx; + temp = 0.79788458f * (temp + fx); + float val = (1.0f + tanhf_poly(temp)) * fx * 0.5f; + int qx = roundf(val / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_GELU, buffer); +} +VARP _TanhInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf(tanh(fx) / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + } + return _UnaryInt8(x, UnaryOpOperation_TANH, buffer); +} +VARP _SigmoidInt8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + float fx = (i - zero[0]) * scale[0]; + float val = 1.0f / (1 + exp(-fx)); + int qx = roundf(val / scale[1]) + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_SIGMOID, buffer); +} +VARP _Expm1Int8(VARP x, float* scale, float* zero) { + std::vector buffer; + for (int i = -127; i <= 127; ++i) { + double fx = (i - zero[0]) * scale[0]; + int qx = roundf((exp(fx) - 1.0f)) / scale[1] + zero[1]; + if (qx > 127) { + qx = 127; + } + if (qx < -127) { + qx = -127; + } + buffer.push_back(qx); + + } + return _UnaryInt8(x, UnaryOpOperation_EXPM1, buffer); +} class UnaryTestCommon : public MNNTestCase { protected: template bool test(VARP (*opFunc)(VARP), string name, Tout threshold, const vector& data_in, - const vector& data_out, const vector& shape_in, const vector& shape_out) { + const vector& data_out, const vector& shape_in, const vector& shape_out, float* quanScales=nullptr, float* quanZeroPoints=nullptr, VARP (*opFuncInt8)(VARP, float*, float*)=nullptr) { int size_in = 1, size_out = 1; for (int i = 0; i < shape_in.size(); ++i) { size_in *= shape_in[i]; @@ -30,11 +516,23 @@ class UnaryTestCommon : public MNNTestCase { auto input = _Input(shape_in, NCHW, halide_type_of()); input->setName("input_tensor"); + if (quanScales) { + input->writeScaleMap(quanScales[0], quanZeroPoints[0]); + } // set input data auto ptr_in = input->template writeMap(); memcpy(ptr_in, data_in.data(), size_in * sizeof(Tin)); input->unMap(); - auto output = opFunc(input); + VARP output; + if (quanScales && opFuncInt8) { + output = opFuncInt8(input, quanScales, quanZeroPoints); + } else { + output = opFunc(input); + } + + if (quanScales) { + output->writeScaleMap(quanScales[1], quanZeroPoints[1]); + } auto gotOutput = output->template readMap(); auto shape_got = output->getInfo()->dim; @@ -345,6 +843,297 @@ class GeluTest : public UnaryTestCommon { {10}, {10}); } }; +/* Unary Int8 test*/ +class AbsTestInt8 : public UnaryTestCommon { +public: + virtual ~AbsTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0315, 0.0315}, zeros[2] = {1.0, 1.0}; + return test(MNN::Express::_Abs, "AbsTestInt8", 0.01, + {-1.0, -2.0, 3.0, 4.0, -1.0, -2.0, 3.0, 4.0}, {1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0}, + {8}, {8}, scale, zeros); + } +}; +class SignTestInt8 : public UnaryTestCommon { +public: + virtual ~SignTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0125490196, 1.0}, zeros[2] = {1.0, 0.}; + return test(_Sign, "SignTestInt8", 0.01, + {-1.2, 0., 0.4, 1.6}, {-1., 0., 1., 1.}, + {4}, {4}, scale, zeros); + } +}; +class NegativeTestInt8 : public UnaryTestCommon { +public: + virtual ~NegativeTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0315, 0.0315}, zeros[2] = {1.0, 2.0}; + return test(_Negative, "NegativeTestInt8", 0.01, + {-1.0, -2.0, 3.0, 4.0, -1.0, -2.0, 3.0, 4.0}, {1.0, 2.0, -3.0, -4.0, 1.0, 2.0, -3.0, -4.0}, + {8}, {8}, scale, zeros); + } +}; +class SquareTestInt8 : public UnaryTestCommon { +public: + virtual ~SquareTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0315, 0.131}, zeros[2] = {1.0, 2.0}; + return test(_Square, "SquareTestInt8", 0.03, + {-1.0, -2.0, 3.0, 4.0, -1.0, -2.0, 3.0, 4.0}, {1.0, 4.0, 9.0, 16.0, 1.0, 4.0, 9.0, 16.0}, + {8}, {8}, scale, zeros, _SquareInt8); + } +}; +class SqrtTestInt8 : public UnaryTestCommon { +public: + virtual ~SqrtTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.502, 0.063}, zeros[2] = {1.0, 1.0}; + return test(_Sqrt, "SqrtTestInt8", 0.01, + {1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0}, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, + {8}, {8}, scale, zeros, _SqrtInt8); + } +}; +class RsqrtTestInt8 : public UnaryTestCommon { +public: + virtual ~RsqrtTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.502, 0.0112}, zeros[2] = {1.0, 1.0}; + return test(_Rsqrt, "RsqrtTestInt8", 0.01, + {1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0}, + {1.0, 1.0 / 2.0, 1.0 / 3.0, 1.0 / 4.0, 1.0 / 5.0, 1.0 / 6.0, 1.0 / 7.0, 1.0 / 8.0}, + {8}, {8}, scale, zeros, _RsqrtInt8); + } +}; +class ExpTestInt8 : public UnaryTestCommon { +public: + virtual ~ExpTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.032, 0.45}, zeros[2] = {1.0, 1.0}; + return test(_Exp, "ExpTestInt8", 0.01, + {1.0, 2.0, 3.0, 4.0}, {2.718, 7.389, 20.086, 54.598}, + {4}, {4}, scale, zeros, _ExpInt8); + } +}; +class LogTestInt8 : public UnaryTestCommon { +public: + virtual ~LogTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.46, 0.031589137243899765}, zeros[2] = {1.0, 1.0}; + return test(_Log, "LogTestInt8", 0.01, + {2.718, 7.389, 20.086, 54.598}, {1.0, 2.0, 3.0, 4.0}, + {4}, {4}, scale, zeros, _LogInt8); + } +}; +class SinTestInt8 : public UnaryTestCommon { +public: + virtual ~SinTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.037233, 0.0097}, zeros[2] = {1.0, 1.0}; + float errorScale = precision <= MNN::BackendConfig::Precision_High ? 1 : 10; + return test(_Sin, "SinTestInt8", 0.01 * errorScale, + {0.0, 3.14 / 2.0, 3.14, 3.14 * 3.0 / 2.0}, {0.0, 1.0, 0.0, -1.0}, + {4}, {4}, scale, zeros, _SinInt8); + } +}; +class CosTestInt8 : public UnaryTestCommon { +public: + virtual ~CosTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.038, 0.0087}, zeros[2] = {1.0, 1.0}; + float errorScale = precision <= MNN::BackendConfig::Precision_High ? 1 : 10; + return test(_Cos, "CosTestInt8", 0.01 * errorScale, + {0.0, 3.14 / 2.0, 3.14, 3.14 * 3.0 / 2.0}, {1.0, 0.0, -1.0, 0.0}, + {4}, {4}, scale, zeros, _CosInt8); + } +}; +class AtanTestInt8 : public UnaryTestCommon { +public: + virtual ~AtanTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.016, 0.013}, zeros[2] = {1.0, 1.0}; + return test(_Atan, "AtanTestInt8", 0.01, + {-2.0, -1.0, 0.0, 1.0}, {-1.11, -3.14 / 4.0, 0.0, 3.14 / 4.0}, + {4}, {4}, scale, zeros, _AtanInt8); + } +}; +class ReciprocalTestInt8 : public UnaryTestCommon { +public: + virtual ~ReciprocalTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.04743, 0.125346}, zeros[2] = {1.0, 1.0}; + return test(_Reciprocal, "ReciprocalTestInt8", 0.01, + {-2.0, -4.0, 2.0, 4.0}, {-0.5, -0.25, 0.50, 0.25}, + {4}, {4}, scale, zeros, _ReciprocalInt8); + } +}; +class Log1PTestInt8 : public UnaryTestCommon { +public: + virtual ~Log1PTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.024, 0.011}, zeros[2] = {1.0, 1.0}; + return test(_Log1p, "Log1pTestInt8", 0.01, + {0.0, 1.0, 2.0, 3.0}, {0.0, 0.69, 1.10, 1.39}, + {4}, {4}, scale, zeros, _Log1pInt8); + } +}; +class TanhTestInt8 : public UnaryTestCommon { +public: + virtual ~TanhTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.024, 0.008}, zeros[2] = {1.0, 1.0}; + return test(_Tanh, "TanhTestInt8", 0.01, + {-1.0f, 0.0f, 1.0f, 2.0f, -98.0f, 90.0f}, {-0.76f, 0.0f, 0.76f, 0.96f, -1.0f, 1.0f}, + {6}, {6}, scale, zeros, _TanhInt8); + } +}; +class SigmoidTestInt8 : public UnaryTestCommon { +public: + virtual ~SigmoidTestInt8() = default; + virtual bool run(int precision) { + int size = 15; + float scale[2] = {0.03162, 0.003956}, zeros[2] = {1.0, 1.0}; + std::vector data_in(size), data_out(size); + for (int i = 0; i < size; ++i) { + data_in[i] = 0.25 * i - 4; + data_out[i] = 1 / (1 + expf(-data_in[i])); + } + return test(_Sigmoid, "SigmoidTestInt8", 0.05, + data_in, data_out, {size}, {size}, scale, zeros, _SigmoidInt8); + } +}; +class AcoshTestInt8 : public UnaryTestCommon { +public: + virtual ~AcoshTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.032, 0.018}, zeros[2] = {1.0, 1.0}; + return test(_Acosh, "AcoshTestInt8", 0.01, + {1.0, 2.0, 3.0, 4.0}, {0., 1.3169579, 1.76274717, 2.06343707}, + {4}, {4}, scale, zeros, _AcoshInt8); + } +}; +class AsinhTestInt8 : public UnaryTestCommon { +public: + virtual ~AsinhTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0316, 0.0248}, zeros[2] = {1.0, 1.0}; + return test(_Asinh, "AsinhTestInt8", 0.01, + {1.0, 2.0, 3.0, 4.0}, {0.88137359, 1.44363548, 1.81844646, 2.09471255}, + {4}, {4}, scale, zeros, _AsinhInt8); + } +}; +class AtanhTestInt8 : public UnaryTestCommon { +public: + virtual ~AtanhTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.00237, 0.002476}, zeros[2] = {1.0, 1.0}; + return test(_Atanh, "AtanhTestInt8", 0.05, + {0., -0.3, 0.2, 0.3}, {0., -0.3095196, 0.20273255, 0.3095196}, + {4}, {4}, scale, zeros, _AtanhInt8); + } +}; +class CoshTestInt8 : public UnaryTestCommon { +public: + virtual ~CoshTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0127, 0.0248}, zeros[2] = {1.0, 1.0}; + return test(_Cosh, "CoshTestInt8", 0.01, + {-1.2, 0., 0.4, 1.6}, {1.81065557, 1., 1.08107237, 2.57746447}, + {4}, {4}, scale, zeros, _CoshInt8); + } +}; +class ErfTestInt8 : public UnaryTestCommon { +public: + virtual ~ErfTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0127, 0.0078}, zeros[2] = {1.0, 1.0}; + return test(_Erf, "ErfTestInt8", 0.01, + {-1.2, 0., 0.4, 1.6}, {-0.91031396, 0., 0.42839235, 0.9763484}, + {4}, {4}, scale, zeros, _ErfInt8); + } +}; +class ErfcTestInt8 : public UnaryTestCommon { +public: + virtual ~ErfcTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0127, 0.02}, zeros[2] = {1.0, 1.0}; + return test(_Erfc, "ErfcTestInt8", 0.01, + {-1.2, 0., 0.4, 1.6}, {1.910314, 1., 0.57160765, 0.02365161}, + {4}, {4}, scale, zeros, _ErfcInt8); + } +}; +class ErfinvTestInt8 : public UnaryTestCommon { +public: + virtual ~ErfinvTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0128, 0.016}, zeros[2] = {1.0, 1.0}; + return test(_Erfinv, "ErfinvTestInt8", 0.05, + {0, 0.4, 0.6, 0.9}, {0., 0.37080714, 0.5951161, 1.1630871}, + {4}, {4}, scale, zeros, _ErfinvInt8); + } +}; +class Expm1TestInt8 : public UnaryTestCommon { +public: + virtual ~Expm1TestInt8() = default; + float scale[2] = {0.0127, 0.0145}, zeros[2] = {1.0, 1.0}; + virtual bool run(int precision) { + return test(_Expm1, "Expm1TestInt8", 0.01, + {-1.2, 0, 0.4, 1.6}, {-0.6988058, 0., 0.49182472, 3.9530325}, + {4}, {4}); + } +}; +class SinhTestInt8 : public UnaryTestCommon { +public: + virtual ~SinhTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0316, 0.0242}, zeros[2] = {1.0, 1.0}; + return test(_Sinh, "SinhTestInt8", 0.01, + {-1.2, 0, 0.4, 1.6}, {-1.5094614, 0., 0.41075233, 2.375568}, + {4}, {4}, scale, zeros, _SinhInt8); + } +}; +class GeluTestInt8 : public UnaryTestCommon { +public: + virtual ~GeluTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0123, 0.01173}, zeros[2] = {1.0, 1.0}; + return test(_Gelu, "GeluTestInt8", 0.01, + {-1.1126, 1.5541, -0.9805, 1.5448, 0.1681, 0.5264, -0.6206, -0.1101, 0.3287, -0.0688}, + {-0.1479, 1.4607, -0.1602, 1.4503, 0.0952, 0.3689, -0.1660, -0.0502, 0.2067, -0.0325}, + {10}, {10}, scale, zeros, _GeluInt8); + } +}; +class AsinTestInt8 : public UnaryTestCommon { +public: + virtual ~AsinTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0079, 0.0124}, zeros[2] = {1.0, 1.0}; + return test(_Asin, "AsinTestInt8", 0.01, + {-1.0, 0.0, 1.0, 0.707}, {-3.14 / 2.0, 0.0, 3.14 / 2.0, 3.14 / 4.0}, + {4}, {4}); + } +}; +class TanTestInt8 : public UnaryTestCommon { +public: + virtual ~TanTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {3.162, 0.0124}, zeros[2] = {1.0, 1.0}; + return test(_Tan, "TanTestInt8", 0.01, + {100.0, 200.0, 300.0, 400.0}, {-0.59, -1.79, 45.24, 1.62}, + {4}, {4}); + } +}; +class AcosTestInt8 : public UnaryTestCommon { +public: + virtual ~AcosTestInt8() = default; + virtual bool run(int precision) { + float scale[2] = {0.0079, 0.0248}, zeros[2] = {1.0, 1.0}; + return test(_Acos, "AcosTestInt8", 0.01, + {-1.0, 0.0, 1.0, 0.707}, {3.14, 1.57, 0.0, 3.14 / 4.0}, + {4}, {4}); + } +}; +/* MNNTestSuiteRegister(AbsTest, "op/unary/abs"); MNNTestSuiteRegister(NegativeTest, "op/unary/negative"); MNNTestSuiteRegister(FloorTest, "op/unary/floor"); @@ -375,4 +1164,31 @@ MNNTestSuiteRegister(ErfcTest, "op/unary/erfc"); MNNTestSuiteRegister(ErfinvTest, "op/unary/erfinv"); MNNTestSuiteRegister(Expm1Test, "op/unary/expm1"); MNNTestSuiteRegister(SinhTest, "op/unary/sinh"); -MNNTestSuiteRegister(GeluTest, "op/unary/gelu"); +MNNTestSuiteRegister(GeluTest, "op/unary/gelu");*/ +MNNTestSuiteRegister(AbsTestInt8, "op/unary/absInt8"); +MNNTestSuiteRegister(SignTestInt8, "op/unary/signInt8"); +MNNTestSuiteRegister(NegativeTestInt8, "op/unary/negativeInt8"); +MNNTestSuiteRegister(SquareTestInt8, "op/unary/squareInt8"); +MNNTestSuiteRegister(SqrtTestInt8, "op/unary/sqrtInt8"); +MNNTestSuiteRegister(ExpTestInt8, "op/unary/expInt8"); +MNNTestSuiteRegister(LogTestInt8, "op/unary/logInt8"); +MNNTestSuiteRegister(SinTestInt8, "op/unary/sinInt8"); +MNNTestSuiteRegister(CosTestInt8, "op/unary/cosInt8"); +MNNTestSuiteRegister(TanTestInt8, "op/unary/tanInt8"); +MNNTestSuiteRegister(AsinTestInt8, "op/unary/asinInt8"); +MNNTestSuiteRegister(AcosTestInt8, "op/unary/acosInt8"); +MNNTestSuiteRegister(AtanTestInt8, "op/unary/atanInt8"); +MNNTestSuiteRegister(ReciprocalTestInt8, "op/unary/reciprocalInt8"); +MNNTestSuiteRegister(Log1PTestInt8, "op/unary/log1pInt8"); +MNNTestSuiteRegister(TanhTestInt8, "op/unary/tanhInt8"); +MNNTestSuiteRegister(SigmoidTestInt8, "op/unary/sigmoidInt8"); +MNNTestSuiteRegister(AcoshTestInt8, "op/unary/acoshInt8"); +MNNTestSuiteRegister(AsinhTestInt8, "op/unary/asinhInt8"); +MNNTestSuiteRegister(AtanhTestInt8, "op/unary/atanhInt8"); +MNNTestSuiteRegister(CoshTestInt8, "op/unary/coshInt8"); +MNNTestSuiteRegister(ErfTestInt8, "op/unary/erfInt8"); +MNNTestSuiteRegister(ErfcTestInt8, "op/unary/erfcInt8"); +MNNTestSuiteRegister(ErfinvTestInt8, "op/unary/erfinvInt8"); +MNNTestSuiteRegister(Expm1TestInt8, "op/unary/expm1Int8"); +MNNTestSuiteRegister(SinhTestInt8, "op/unary/sinhInt8"); +MNNTestSuiteRegister(GeluTestInt8, "op/unary/geluInt8"); diff --git a/tools/converter/include/config.hpp b/tools/converter/include/config.hpp index 991ad1217..6fd87eaff 100644 --- a/tools/converter/include/config.hpp +++ b/tools/converter/include/config.hpp @@ -46,7 +46,7 @@ class MNN_PUBLIC modelConfig { std::string compressionParamsFile = ""; bool saveStaticModel = false; int optimizePrefer = 0; - float targetVersion = 1.3; + float targetVersion = (float)MNN_VERSION_MAJOR + (float)MNN_VERSION_MINOR * 0.1f; int defaultBatchSize = 0; int optimizeLevel = 1; bool keepInputFormat = false; diff --git a/tools/converter/source/common/cli.cpp b/tools/converter/source/common/cli.cpp index 0e58b0d45..e1b58473a 100644 --- a/tools/converter/source/common/cli.cpp +++ b/tools/converter/source/common/cli.cpp @@ -222,7 +222,7 @@ bool Cli::initializeMNNConvertArgs(modelConfig &modelPath, int argc, char **argv ) ( "targetVersion", - "compability for old mnn engine, default: 1.2f", + "compability for old mnn engine, default the same as converter", cxxopts::value() ) ( @@ -477,7 +477,7 @@ bool Cli::convertModel(modelConfig& modelPath) { dumpModelInfo(modelPath.modelFile.c_str()); return true; } - std::cout << "Start to Convert Other Model Format To MNN Model..." << std::endl; + std::cout << "Start to Convert Other Model Format To MNN Model..., target version: " << modelPath.targetVersion << std::endl; std::unique_ptr netT = std::unique_ptr(new MNN::NetT()); int parseRes = 1; if (modelPath.model == modelConfig::CAFFE) { diff --git a/tools/converter/source/optimizer/Program.cpp b/tools/converter/source/optimizer/Program.cpp index 04e75f246..4df3e7ae5 100644 --- a/tools/converter/source/optimizer/Program.cpp +++ b/tools/converter/source/optimizer/Program.cpp @@ -75,26 +75,6 @@ void Program::createUnit(std::map& varMap, std::vector& inputInd VARPS Program::input(const std::unordered_map& inputs, bool lazy) { VARPS inputUpdate; - for (auto& it : mVars) { - auto var = it.second; - auto expr = var->expr().first; - if (expr->get() != nullptr || expr->inputType() != VARP::INPUT) { - continue; - } - if (inputs.count(var->name())) { - VARP input = inputs.at(var->name()); - inputUpdate.emplace_back(var); - if (lazy) { - // only replace expr, not do getInfo, avoid unnecessary getInfo error - // replace will override var(and expr)'s name, remain them so we can track input var - // origin input var will be used when save program to net - mOriginInputs.emplace_back(var, var->expr().first, var->expr().second); - var->setExpr(input->expr().first, input->expr().second); - } else { - var->input(input); - } - } - } return inputUpdate; } @@ -148,9 +128,6 @@ std::shared_ptr Program::create(const std::vector> } std::shared_ptr newProgram(new Program); Program& program = *newProgram; - if (saveAllVars) { - program.mVars = std::move(varMap); - } for (auto output : outputs) { program.mOutputs.emplace_back(output.second); } diff --git a/tools/converter/source/optimizer/merge/FuseTemplateOp.cpp b/tools/converter/source/optimizer/merge/FuseTemplateOp.cpp index bcea82781..bfa266afb 100644 --- a/tools/converter/source/optimizer/merge/FuseTemplateOp.cpp +++ b/tools/converter/source/optimizer/merge/FuseTemplateOp.cpp @@ -191,6 +191,55 @@ static auto gRegister = []() { }; TemplateMerge::getInstance("Merge").insertTemplateV2("FuseHardSwish", transform2); } + { + auto zero0 = _Scalar(0); + auto zero1 = _Scalar(0); + auto one0 = _Scalar(1); + auto one1 = _Scalar(1); + auto input0 = _Input({}, NHWC, halide_type_of()); + auto input1 = _Input({}, NHWC, halide_type_of()); + std::vector binaryAddZero({ + zero0 + input1, + input1 + zero0, + zero1 + input1, + input1 + zero1, + input0 - zero0, + input1 - zero1, + input0 * one0, + one0 * input0, + input1 * one1, + one1 * input1, + }); + auto transform2 = [binaryAddZero, input0, input1](EXPRP expr) { + std::map inputConst; + for (int index=0; indexexpr().first, expr, inputConst)) { + auto inputVarIter0 = inputConst.find(input0->expr().first); + auto inputVarIter1 = inputConst.find(input1->expr().first); + MNN::Express::VARP inputVar; + if (inputVarIter0 == inputConst.end() && inputVarIter1 == inputConst.end()) { + MNN_ERROR("Invalid Match, may be something is wrong for Fuse\n"); + return false; + } + if (inputVarIter0 != inputConst.end()) { + inputVar = inputVarIter0->second; + } else { + inputVar = inputVarIter1->second; + } + std::unique_ptr newOp(new OpT); + newOp->type = OpType_Identity; + newOp->main.type = OpParameter_NONE; + auto newVar = Variable::create(Expr::create(newOp.get(), {inputVar}, 1)); + newVar->setName(expr->outputName(0)); + Expr::replace(expr, newVar->expr().first); + return true; + } + } + return false; + }; + TemplateMerge::getInstance("Merge").insertTemplateV2("RemoveUselessBinary", transform2); + } { auto input0 = _Input({}, NCHW); auto input1 = _Input({}, NCHW); @@ -346,7 +395,7 @@ static auto gRegister = []() { newUnary->type = OpType_UnaryOp; newUnary->main.type = OpParameter_UnaryOp; newUnary->main.value = new UnaryOpT; - newUnary->main.AsUnaryOp()->opType = UnaryOpOperation_GELU_STANDARD; + newUnary->main.AsUnaryOp()->opType = UnaryOpOperation_GELU; auto newVar = MNN::Express::Variable::create(MNN::Express::Expr::create(newUnary.get(), {inputVar})); newVar->setName(expr->outputName(0)); Expr::replace(expr, newVar->expr().first); diff --git a/tools/converter/source/optimizer/onnxextra/OnnxEinsum.cpp b/tools/converter/source/optimizer/onnxextra/OnnxEinsum.cpp index 6e747bc35..3aad1f9cc 100644 --- a/tools/converter/source/optimizer/onnxextra/OnnxEinsum.cpp +++ b/tools/converter/source/optimizer/onnxextra/OnnxEinsum.cpp @@ -94,6 +94,68 @@ class OnnxEinsumTransform : public OnnxExtraManager::Transform { auto iPos = left.find(","); auto input0 = left.substr(0, iPos); auto input1 = left.substr(iPos+1, left.size()); + auto var0 = expr->inputs()[0]; + auto var1 = expr->inputs()[1]; + // dim = 4 + if (right.size() == 4) { + // batch align: + // bhwc,bhkc -> bhwk batch = `bh`, reduce_dim = `c` + // bhwc,hkc -> bhwk batch = `bh`, reduce_dim = `c`, need broadcast + // bhwc,wkc -> bhwk batch = `bhw`, reduce_dim = `c`, need unsqeeze, broadcast, squeeze + int sqeeze_axis = -1; + if (input0.size() != input1.size()) { + int pos0 = 0, pos1 = 0; + if (input0.size() > input1.size()) { + for (int i = 0; i < input1.size(); i++) { + auto c = input1[i]; + bool right_has = right.find(c) != std::string::npos; + auto upos0 = input0.find(c); + bool input0_has = upos0 != std::string::npos; + if (right_has && input0_has) { + pos0 = static_cast(upos0); + pos1 = i; + } + } + } else { + for (int i = 0; i < input0.size(); i++) { + auto c = input0[i]; + bool right_has = right.find(c) != std::string::npos; + auto upos1 = input1.find(c); + bool input1_has = upos1 != std::string::npos; + if (right_has && input1_has) { + pos0 = i; + pos1 = static_cast(upos1); + } + } + } + if (input0.size() - pos0 < 3) { + sqeeze_axis = pos0 + 1; + var0 = _Unsqueeze(var0, {sqeeze_axis}); + } + if (input1.size() - pos1 < 3) { + sqeeze_axis = pos1 + 1; + var1 = _Unsqueeze(var1, {sqeeze_axis}); + } + } + // find reduce dim + char reduce_dim; + for (int i = 0; i < input0.size(); ++i) { + auto c = input0[i]; + if (right.find(c) == std::string::npos) { + reduce_dim = c; + break; + } + } + auto need_transpose = input1.find(reduce_dim) == (input1.size() - 1); + // matmul: matmul auto broadcast such: `bhwc @ hkc` -> `bhwc @ bhkc` + auto output = _MatMul(var0, var1, false, need_transpose); + // squeeze + if (sqeeze_axis >= 0) { + output = _Squeeze(output, {sqeeze_axis}); + } + output->setName(expr->name()); + return output->expr().first; + } std::map input0Pos; for (int i=0; iinputs()[0]; - auto var1 = expr->inputs()[1]; + // dim < 4 if (sumPos.empty()) { // Broadcast Mul { @@ -254,7 +315,7 @@ class OnnxEinsumTransform : public OnnxExtraManager::Transform { var0 = _BroadcastTo(var0, _Concat({outsideLength, ALength, sumLength}, 0)); } else { var0 = _ReshapeF(var0, _Concat({outsideLength, ALength, sumLength}, 0), MNN::MNN_DATA_FORMAT_NCHW); - } + } } { // Transpose diff --git a/tools/converter/source/optimizer/onnxextra/OnnxPooling.cpp b/tools/converter/source/optimizer/onnxextra/OnnxPooling.cpp index c23c14c5f..51cc75258 100644 --- a/tools/converter/source/optimizer/onnxextra/OnnxPooling.cpp +++ b/tools/converter/source/optimizer/onnxextra/OnnxPooling.cpp @@ -60,6 +60,7 @@ class OnnxPoolingTransform : public OnnxExtraManager::Transform { } virtual EXPRP onExecute(EXPRP expr) const override { auto inputs = expr->inputs(); + auto outputSize = expr->outputSize(); auto op = expr->get(); std::unique_ptr poolOp(new OpT); poolOp->name = op->name()->c_str(); @@ -186,7 +187,7 @@ class OnnxPoolingTransform : public OnnxExtraManager::Transform { res->setName(expr->outputName(0)); return res->expr().first; } - auto poolExpr = Expr::create(poolOp.get(), {inputs[0]}); + auto poolExpr = Expr::create(poolOp.get(), {inputs[0]}, outputSize); auto res = Variable::create(poolExpr); poolExpr->setName(expr->name()); return res->expr().first; diff --git a/tools/converter/source/optimizer/onnxextra/OnnxScatterND.cpp b/tools/converter/source/optimizer/onnxextra/OnnxScatterND.cpp index 7784a2345..cf3b05b68 100644 --- a/tools/converter/source/optimizer/onnxextra/OnnxScatterND.cpp +++ b/tools/converter/source/optimizer/onnxextra/OnnxScatterND.cpp @@ -33,21 +33,29 @@ class OnnxScatterNdTransformer : public OnnxExtraManager::Transform { } auto indice = inputs[1]; auto update = inputs[2]; + auto config = Global::Get(); + auto version = config->targetVersion; auto shape = _Shape(data, true); - auto tfRes = _ScatterNd(indice, update, shape); - VARP tfMask; - if (type.code == halide_type_float) { - auto updateOne = _Fill(_Shape(update, NCHW), _Scalar(1.0f)); - auto mask = _ScatterNd(indice, updateOne, shape); - tfMask = _Cast(_Less(mask, _Scalar(0.5f))); - } else { - auto updateOne = _Fill(_Shape(update, NCHW), _Scalar(1)); - auto mask = _ScatterNd(indice, updateOne, shape); - tfMask = _Less(mask, _Scalar(1)); + if (version < 2.0f) { + // For target version < 2.0 , don't support 4 input scatternd + auto tfRes = _ScatterNd(indice, update, shape); + VARP tfMask; + if (type.code == halide_type_float) { + auto updateOne = _Fill(_Shape(update, NCHW), _Scalar(1.0f)); + auto mask = _ScatterNd(indice, updateOne, shape); + tfMask = _Cast(_Less(mask, _Scalar(0.5f))); + } else { + auto updateOne = _Fill(_Shape(update, NCHW), _Scalar(1)); + auto mask = _ScatterNd(indice, updateOne, shape); + tfMask = _Less(mask, _Scalar(1)); + } + auto dst = data * tfMask + tfRes; + dst->setName(expr->name()); + return dst->expr().first; } - auto dst = data * tfMask + tfRes; - dst->setName(expr->name()); - return dst->expr().first; + auto tfRes = _ScatterNd(indice, update, shape, data); + tfRes->setName(expr->name()); + return tfRes->expr().first; } }; diff --git a/tools/converter/source/optimizer/postconvert/ReIndexTensor.cpp b/tools/converter/source/optimizer/postconvert/ReIndexTensor.cpp index 61f1df699..f2dce71a5 100644 --- a/tools/converter/source/optimizer/postconvert/ReIndexTensor.cpp +++ b/tools/converter/source/optimizer/postconvert/ReIndexTensor.cpp @@ -90,7 +90,9 @@ class ReIndexTensor : public PostConverter { defaultName << EnumNameOpType(op->type); defaultName << i; op->name = defaultName.str(); +#ifdef DEBUG MNN_PRINT("%d op name is empty or dup, set to %s\n", i, op->name.c_str()); +#endif opName = op->name; } names.insert(opName); diff --git a/tools/cpp/revertMNNModel.cpp b/tools/cpp/revertMNNModel.cpp index f4d326d7d..3663267dc 100644 --- a/tools/cpp/revertMNNModel.cpp +++ b/tools/cpp/revertMNNModel.cpp @@ -77,7 +77,7 @@ const size_t Revert::getBufferSize() const { } void Revert::writeExtraDescribeTensor(float* scale, float* offset) { - int opCounts = mMNNNet->oplists.size(); + int opCounts = static_cast(mMNNNet->oplists.size()); for (int opIndex = 0; opIndex < opCounts; ++opIndex) { std::unique_ptr describe(new MNN::TensorDescribeT); describe->index = opIndex; @@ -95,12 +95,16 @@ void Revert::writeExtraDescribeTensor(float* scale, float* offset) { continue; } // Conv/ConvDepthwise/Deconv weight quant. + const float inputScale = *scale; + const float outputScale = *scale; + const int outputChannel = static_cast(op->outputIndexes.size()); + auto param = op->main.AsConvolution2D(); float* originWeight = param->weight.data(); - int weightSize = param->weight.size(); const int channels = param->common->outputCount; param->symmetricQuan.reset(new MNN::QuantizedFloatParamT); param->symmetricQuan->nbits = 8; + const int weightSize = static_cast(param->weight.size()); param->common->inputCount = weightSize / (channels * param->common->kernelX * param->common->kernelY); std::vector quantizedWeight(weightSize); std::vector quantizedWeightScale(channels); diff --git a/tools/quantization/TensorStatistic.cpp b/tools/quantization/TensorStatistic.cpp index 918c80ed6..734fa1a92 100644 --- a/tools/quantization/TensorStatistic.cpp +++ b/tools/quantization/TensorStatistic.cpp @@ -77,6 +77,12 @@ void TensorStatistic::updateRange() { mRange.second = maxValue; } } +// if (mRange.first > 0.0f) { +// mRange.first = 0.0f; +// } +// if (mRange.second < 0.0f) { +// mRange.second = 0.0f; +// } mVisited = true; } @@ -105,7 +111,8 @@ void TensorStatistic::updateDistribution() { if (area == 0) { area = 1; } - +// float midValue = (mRange.second + mRange.first) / 2.0f; + float midValue = 0.f; for (int n = 0; n < batch; ++n) { auto dataBatch = mHostTensor->host() + n * mHostTensor->stride(0); for (int c = 0; c < channel; ++c) { @@ -116,7 +123,7 @@ void TensorStatistic::updateDistribution() { auto target = mDistribution.data(); auto dataChannel = dataBatch + c * mHostTensor->stride(1); for (int v = 0; v < area; ++v) { - auto data = dataChannel[v]; + auto data = dataChannel[v] - midValue; if (data == 0) { continue; } @@ -230,9 +237,9 @@ int TensorStatistic::_computeThreshold(const std::vector& distribution) { return threshold; } -float TensorStatistic::finishAndCompute() { +std::pair TensorStatistic::finishAndCompute() { if (!mValid) { - return 0.f; + return std::make_pair(0.f, 0); } float sum = 0.0f; std::for_each(mDistribution.begin(), mDistribution.end(), [&](float n) { sum += n; }); @@ -241,10 +248,14 @@ float TensorStatistic::finishAndCompute() { auto threshold = _computeThreshold(mDistribution); mScale = ((float)threshold + 0.5) / mInterval / mFeatureClampValue; // MNN_PRINT("==> %s == %d, %f, %f\n", mName.c_str(),threshold, 1.0f / mIntervals[0], mScale * mFeatureClampValue); - return mScale; + float midValue = (mRange.second + mRange.first) / 2.0f; +// mZeroPoint = fmaxf(fminf(-midValue / mScale, 127.0f), -127.0f); + mZeroPoint = 0; + + return std::make_pair(mScale, mZeroPoint); } -float TensorStatistic::computeScaleADMM() { +std::pair TensorStatistic::computeScaleADMM() { const int count = mOriginTensor->elementSize(); float max = 0; const float bound = mFeatureClampValue; @@ -283,7 +294,8 @@ float TensorStatistic::computeScaleADMM() { // DLOG(INFO) << "alpha final: " << alpha; mScale = alpha; mVisited = true; - return mScale; + mZeroPoint = 0; + return std::make_pair(mScale, mZeroPoint); } std::pair, float> TensorStatistic::fakeQuantFeature() { @@ -295,9 +307,9 @@ std::pair, float> TensorStatistic::fakeQuantFeature() { int overflowCount = 0; for (int i = 0; i < count; i++) { - float dataQuant = std::roundf(originData[i] / scale); + float dataQuant = std::roundf(originData[i] / scale) + mZeroPoint; dataQuant = std::fmin(bound, std::fmax(-bound, dataQuant)); - float dataDequant = dataQuant * scale; + float dataDequant = (dataQuant - mZeroPoint) * scale; originData[i] = dataDequant; fakeQuantedFeature.emplace_back(dataDequant); diff --git a/tools/quantization/TensorStatistic.hpp b/tools/quantization/TensorStatistic.hpp index 73aa90de6..d60662e07 100644 --- a/tools/quantization/TensorStatistic.hpp +++ b/tools/quantization/TensorStatistic.hpp @@ -35,10 +35,10 @@ class TensorStatistic { void setThresholdMethod(GET_THRESHOLD_METHOD thresholdMethod); - float finishAndCompute(); + std::pair finishAndCompute(); // only this one for ADMM - float computeScaleADMM(); + std::pair computeScaleADMM(); std::string name() { return mName; @@ -79,5 +79,6 @@ class TensorStatistic { GET_THRESHOLD_METHOD mThresholdMethod = THRESHOLD_KL; bool mVisited = false; float mScale; + int8_t mZeroPoint = 0; float mFeatureClampValue = 127.0f; }; diff --git a/tools/quantization/calibration.cpp b/tools/quantization/calibration.cpp index 45624c377..2805a5737 100644 --- a/tools/quantization/calibration.cpp +++ b/tools/quantization/calibration.cpp @@ -37,7 +37,7 @@ #include "train/source/transformer/Transformer.hpp" #include "cpp/ConvertToFullQuant.hpp" #include "core/ConvolutionCommon.hpp" - +#include using namespace MNN::CV; using namespace MNN::Train; @@ -57,6 +57,7 @@ Calibration::Calibration(MNN::NetT* model, const uint8_t* modelBuffer, const int document.Parse(outputStr.c_str()); if (document.HasParseError()) { MNN_ERROR("Invalid json\n"); + mValid = false; return; } } @@ -578,7 +579,7 @@ void Calibration::_computeFeatureScaleADMM() { MNN_PRINT("\n"); _scales.clear(); - const int totalLayers = _featureInfo.size(); + const int totalLayers = static_cast(_featureInfo.size()); count = 0; MNN::TensorCallBackWithInfo before = [&](const std::vector& nTensors, const MNN::OperatorInfo* info) { @@ -642,7 +643,7 @@ void Calibration::_fake_quant_weights() { auto param = op->main.AsConvolution2D(); const int kernelNum = param->common->outputCount; std::vector weights = param->weight; - const int weightSize = weights.size(); + const int weightSize = static_cast(weights.size()); const int kernelSize = weightSize / kernelNum; for (int i = 0; i < kernelNum; i++) { @@ -669,9 +670,13 @@ void Calibration::_insertScale() { for (const auto iter : _scales) { std::unique_ptr describe(new MNN::TensorDescribeT); auto des = TensorUtils::getDescribe(iter.first); + if (des->index < 0) { + continue; + } describe->index = des->index; describe->quantInfo.reset(new MNN::TensorQuantInfoT); - describe->quantInfo->scale = iter.second; + describe->quantInfo->scale = iter.second.first; + describe->quantInfo->zero = iter.second.second; describe->quantInfo->type = MNN::DataType_DT_INT8; describe->quantInfo->min = -1 * _featureClampValue; describe->quantInfo->max = 1 * _featureClampValue; @@ -694,8 +699,8 @@ void Calibration::_insertScale() { auto inputTensor = _tensorMap[op->inputIndexes[0]]; auto outputTensor = _tensorMap[op->outputIndexes[0]]; // below is Conv/DepthwiseConv weight quant - const float inputScale = _scales[inputTensor]; - const float outputScale = _scales[outputTensor]; + const float inputScale = _scales[inputTensor].first; + const float outputScale = _scales[outputTensor].first; const int inputChannel = inputTensor->channel(); const int outputChannel = outputTensor->channel(); auto param = op->main.AsConvolution2D(); @@ -704,7 +709,7 @@ void Calibration::_insertScale() { param->symmetricQuan.reset(new MNN::QuantizedFloatParamT); param->symmetricQuan->nbits = _quant_bits; const float* originWeight = param->weight.data(); - int originWeightSize = param->weight.size(); + int originWeightSize = static_cast(param->weight.size()); auto conv2d = param; std::shared_ptr quanCommon; std::unique_ptr externalWeightTensor, externalBiasTensor; @@ -976,6 +981,23 @@ void Calibration::_quantizeModelEMA() { } Variable::save(predicts, _destModelFile.c_str()); ConvertToFullQuant::convert(_destModelFile); + + std::unique_ptr netT; + { + std::ifstream input(_destModelFile, std::ifstream::in | std::ifstream::binary); + std::ostringstream outputOs; + outputOs << input.rdbuf(); + netT = MNN::UnPackNet(outputOs.str().c_str()); + } + ComputeUnaryBuffer(netT.get()); + { + flatbuffers::FlatBufferBuilder builderOutput(1024); + builderOutput.ForceDefaults(true); + auto len = MNN::Net::Pack(builderOutput, netT.get()); + builderOutput.Finish(len); + std::ofstream output(_destModelFile, std::ofstream::binary); + output.write((const char*)builderOutput.GetBufferPointer(), builderOutput.GetSize()); + } } void Calibration::runQuantizeModel() { @@ -993,13 +1015,14 @@ void Calibration::runQuantizeModel() { _computeQuantError(); } _insertScale(); + ComputeUnaryBuffer(_originalModel); { flatbuffers::FlatBufferBuilder builderOutput(1024); builderOutput.ForceDefaults(true); auto len = MNN::Net::Pack(builderOutput, _originalModel); builderOutput.Finish(len); - std::ofstream output(_destModelFile); + std::ofstream output(_destModelFile, std::ofstream::binary); output.write((const char*)builderOutput.GetBufferPointer(), builderOutput.GetSize()); } } @@ -1025,7 +1048,7 @@ void Calibration::dumpTensorScales(const std::string& modelFile) { writer.String(rapidjson::StringRef(name.c_str(), name.size())); auto& inputIndexes = op->inputIndexes; - const int inputSize = inputIndexes.size(); + const int inputSize = static_cast(inputIndexes.size()); if (inputSize > 0) { writer.Key("inputs"); @@ -1042,7 +1065,12 @@ void Calibration::dumpTensorScales(const std::string& modelFile) { writer.Key("scales"); writer.StartArray(); - writer.Double(inputOpScale); + writer.Double(inputOpScale.first); + writer.EndArray(); + + writer.Key("zeropoint"); + writer.StartArray(); + writer.Double(inputOpScale.second); writer.EndArray(); writer.EndObject(); @@ -1051,7 +1079,7 @@ void Calibration::dumpTensorScales(const std::string& modelFile) { } auto& outputIndexes = op->outputIndexes; - const int outputSize = outputIndexes.size(); + const int outputSize = static_cast(outputIndexes.size()); if (outputSize > 0) { writer.Key("outputs"); @@ -1068,7 +1096,12 @@ void Calibration::dumpTensorScales(const std::string& modelFile) { writer.Key("scales"); writer.StartArray(); - writer.Double(outputOpScale); + writer.Double(outputOpScale.first); + writer.EndArray(); + + writer.Key("zeropoint"); + writer.StartArray(); + writer.Double(outputOpScale.second); writer.EndArray(); writer.EndObject(); @@ -1089,3 +1122,188 @@ void Calibration::dumpTensorScales(const std::string& modelFile) { std::cerr << "open scale file " << scaleFile << " fail. error code:" << os.failbit << std::endl; } } + +typedef VARP (*unaryProc)(VARP input); +static unaryProc selectUnaryProc(int type) { + switch (type) { + case UnaryOpOperation_ABS: + return MNN::Express::_Abs; + case UnaryOpOperation_SQUARE: + return MNN::Express::_Square; + case UnaryOpOperation_NEG: + return MNN::Express::_Negative; + case UnaryOpOperation_RSQRT: + return MNN::Express::_Rsqrt; + case UnaryOpOperation_EXP: + return MNN::Express::_Exp; + case UnaryOpOperation_COS: + return MNN::Express::_Cos; + case UnaryOpOperation_SIN: + return MNN::Express::_Sin; + case UnaryOpOperation_SIGMOID: + return MNN::Express::_Sigmoid; + case UnaryOpOperation_TANH: + return MNN::Express::_Tanh; + case UnaryOpOperation_TAN: + return MNN::Express::_Tan; + case UnaryOpOperation_ATAN: + return MNN::Express::_Atan; + case UnaryOpOperation_SQRT: + return MNN::Express::_Sqrt; + case UnaryOpOperation_RECIPROCAL: + return MNN::Express::_Reciprocal; + case UnaryOpOperation_LOG1P: + return MNN::Express::_Log1p; + case UnaryOpOperation_LOG: + return MNN::Express::_Log; + case UnaryOpOperation_ACOSH: + return MNN::Express::_Acosh; + case UnaryOpOperation_SINH: + return MNN::Express::_Sinh; + case UnaryOpOperation_ASINH: + return MNN::Express::_Asinh; + case UnaryOpOperation_ATANH: + return MNN::Express::_Atanh; + case UnaryOpOperation_SIGN: + return MNN::Express::_Sign; + case UnaryOpOperation_COSH: + return MNN::Express::_Cosh; + case UnaryOpOperation_ERF: + return MNN::Express::_Erf; + case UnaryOpOperation_ERFC: + return MNN::Express::_Erfc; + case UnaryOpOperation_ERFINV: + return MNN::Express::_Erfinv; + case UnaryOpOperation_EXPM1: + return MNN::Express::_Expm1; + case UnaryOpOperation_ASIN: + return MNN::Express::_Asin; + case UnaryOpOperation_ACOS: + return MNN::Express::_Acos; + case UnaryOpOperation_HARDSWISH: + return MNN::Express::_Hardswish; + case UnaryOpOperation_GELU: + return MNN::Express::_Gelu; + default: + MNN_ASSERT(false); + break; + } + return nullptr; +} +void Calibration::ComputeUnaryBuffer(MNN::NetT* net) { + for (auto iter = net->oplists.begin(); iter != net->oplists.end(); ++iter) { + auto op = iter->get(); + const auto opType = op->type; + std::map describes; + for (auto& des : _originalModel->extraTensorDescribe) { + describes.insert(std::make_pair(des->index, des.get())); + } + if (opType == MNN::OpType_UnaryOp) { + auto type = op->main.AsUnaryOp()->opType; + if (type == UnaryOpOperation_ABS || type == UnaryOpOperation_NEG || type == UnaryOpOperation_SIGN) { + continue; + } + op->main.AsUnaryOp()->tableInt8.resize(255); + auto unaryParam = op->main.AsUnaryOp()->tableInt8.data(); + + auto outputId = op->outputIndexes[0]; + if (describes.find(outputId) == describes.end()) { + continue; + } + auto unaryDes = describes.find(outputId)->second; + float outScale = unaryDes->quantInfo->scale; + float outZero = unaryDes->quantInfo->zero; + auto inputId = op->inputIndexes[0]; + if (describes.find(inputId) == describes.end()) { + MNN_ERROR("Can't find extraTensorDescribe for %s\n", op->name.c_str()); + } + unaryDes = describes.find(inputId)->second; + float inpScale = unaryDes->quantInfo->scale; + float inpZero = unaryDes->quantInfo->zero; + + // Read input data. + std::vector dataInput; + float fx = 0.f; + for (int i = -127; i <= 127; ++i) { + fx = (i - inpZero) * inpScale; + dataInput.push_back(fx); + } + auto input = _Input({255}, NCHW, halide_type_of()); + input->setName("input_tensor"); + auto ptr_in = input->template writeMap(); + memcmp(ptr_in, dataInput.data(), 255); + input->unMap(); + // Compute output data. + VARP output; + auto func = selectUnaryProc(type); + if (nullptr == func) { + MNN_ERROR("Don't support quantizing UnaryOP: %s to Int8\n", op->name.c_str()); + } + output = func(input); + auto gotOutput = output->template readMap(); + // Write output data. + int val; + for (int i = 0; i < 255; ++i) { + val = gotOutput[i] / outScale + outZero; + if (val > 127) { + val = 127; + } + if (val < -127) { + val = -127; + } + unaryParam[i] = val; + } + } + } +} + +int quant_main(int argc, const char* argv[]) { + if (argc < 4) { + DLOG(INFO) << "Usage: ./quantized.out src.mnn dst.mnn preTreatConfig.json\n"; + return 0; + } + const char* modelFile = argv[1]; + const char* preTreatConfig = argv[3]; + const char* dstFile = argv[2]; + DLOG(INFO) << ">>> modelFile: " << modelFile; + DLOG(INFO) << ">>> preTreatConfig: " << preTreatConfig; + DLOG(INFO) << ">>> dstFile: " << dstFile; + std::unique_ptr netT; + { + std::shared_ptr interp(MNN::Interpreter::createFromFile(modelFile), MNN::Interpreter::destroy); + if (nullptr == interp.get()) { + return 0; + } + netT = MNN::UnPackNet(interp->getModelBuffer().first); + } + + // temp build net for inference + flatbuffers::FlatBufferBuilder builder(1024); + auto offset = MNN::Net::Pack(builder, netT.get()); + builder.Finish(offset); + int size = builder.GetSize(); + auto ocontent = builder.GetBufferPointer(); + + // model buffer for creating mnn Interpreter + std::unique_ptr modelForInference(new uint8_t[size]); + memcpy(modelForInference.get(), ocontent, size); + + std::unique_ptr modelOriginal(new uint8_t[size]); + memcpy(modelOriginal.get(), ocontent, size); + + netT.reset(); + netT = MNN::UnPackNet(modelOriginal.get()); + + // quantize model's weight + DLOG(INFO) << "Calibrate the feature and quantize model..."; + std::shared_ptr calibration( + new Calibration(netT.get(), modelForInference.get(), size, preTreatConfig, std::string(modelFile), std::string(dstFile))); + if (!calibration->valid()) { + return 0; + } + calibration->runQuantizeModel(); + calibration->dumpTensorScales(dstFile); + DLOG(INFO) << "Quantize model done!"; + + return 0; +} diff --git a/tools/quantization/calibration.hpp b/tools/quantization/calibration.hpp index 87540c41f..64d3e9806 100644 --- a/tools/quantization/calibration.hpp +++ b/tools/quantization/calibration.hpp @@ -16,6 +16,7 @@ #include "TensorStatistic.hpp" #include "MNN_generated.h" #include "Helper.hpp" +#include "logkit.h" // Calibration find the optimal threshold according to KL-divergence // process: the below process is applied on the whole Conv|DepthwiseConv layers @@ -31,11 +32,15 @@ class Calibration { void runQuantizeModel(); void dumpTensorScales(const std::string& modelFile); - + void ComputeUnaryBuffer(MNN::NetT* net); + bool valid() const { + return mValid; + } private: Calibration(); MNN::NetT* _originalModel; std::shared_ptr _process; + bool mValid = true; const int _binNums = 2048; int _calibrationFileNum = 0; int _width; @@ -58,7 +63,7 @@ class Calibration { std::map _tensorMap; // The scale results - std::map _scales; + std::map> _scales; std::shared_ptr _interpreter; // keep mnn forward information @@ -94,5 +99,6 @@ class Calibration { void _computeQuantError(); void _insertScale(); }; +int quant_main(int argc, const char* argv[]); #endif // CALIBRATION_HPP diff --git a/tools/quantization/quantized.cpp b/tools/quantization/quantized.cpp index 710827233..26f85ee73 100644 --- a/tools/quantization/quantized.cpp +++ b/tools/quantization/quantized.cpp @@ -10,52 +10,7 @@ #include #include #include "calibration.hpp" -#include "logkit.h" int main(int argc, const char* argv[]) { - if (argc < 4) { - DLOG(INFO) << "Usage: ./quantized.out src.mnn dst.mnn preTreatConfig.json\n"; - return 0; - } - const char* modelFile = argv[1]; - const char* preTreatConfig = argv[3]; - const char* dstFile = argv[2]; - DLOG(INFO) << ">>> modelFile: " << modelFile; - DLOG(INFO) << ">>> preTreatConfig: " << preTreatConfig; - DLOG(INFO) << ">>> dstFile: " << dstFile; - std::unique_ptr netT; - { - //std::ifstream input(modelFile); - std::ifstream input(modelFile, std::ifstream::in | std::ifstream::binary); - std::ostringstream outputOs; - outputOs << input.rdbuf(); - netT = MNN::UnPackNet(outputOs.str().c_str()); - } - - // temp build net for inference - flatbuffers::FlatBufferBuilder builder(1024); - auto offset = MNN::Net::Pack(builder, netT.get()); - builder.Finish(offset); - int size = builder.GetSize(); - auto ocontent = builder.GetBufferPointer(); - - // model buffer for creating mnn Interpreter - std::unique_ptr modelForInference(new uint8_t[size]); - memcpy(modelForInference.get(), ocontent, size); - - std::unique_ptr modelOriginal(new uint8_t[size]); - memcpy(modelOriginal.get(), ocontent, size); - - netT.reset(); - netT = MNN::UnPackNet(modelOriginal.get()); - - // quantize model's weight - DLOG(INFO) << "Calibrate the feature and quantize model..."; - std::shared_ptr calibration( - new Calibration(netT.get(), modelForInference.get(), size, preTreatConfig, std::string(modelFile), std::string(dstFile))); - calibration->runQuantizeModel(); - calibration->dumpTensorScales(dstFile); - DLOG(INFO) << "Quantize model done!"; - - return 0; + return quant_main(argc, argv); } diff --git a/tools/script/testPTQ.py b/tools/script/testPTQ.py index a1a09dbc6..a43050a05 100755 --- a/tools/script/testPTQ.py +++ b/tools/script/testPTQ.py @@ -36,10 +36,12 @@ def compare(origin, quant, jsonFile): originIdx, originPoint = parseRes(origin_res) quantIdx, quantPoint = parseRes(quant_res) + print(originIdx, originPoint) + print(quantIdx, quantPoint) idxRate = len(originIdx & quantIdx) / max(len(originIdx), len(quantIdx)) pointRate = quantPoint / originPoint print(name, idxRate, pointRate) - if idxRate < 0.5 or pointRate < 0.5 or pointRate > 2.0: + if idxRate < 0.5: print('False') return False return True @@ -138,4 +140,4 @@ def testacc(modelpath, imagepath, path, labelpath): print(w) print('BATCH_TEST_NAME_PTQ: PTQ测试\nTEST_CASE_AMOUNT_PTQ: {\"blocked\":0,\"failed\":%d,\"passed\":%d,\"skipped\":0}\n'%(len(gWrong), total_num - len(gWrong))) if len(gWrong) > 0: - exit(1) \ No newline at end of file + exit(1) From 067d0be89645b53b40d3e21ede58cdaf038af0c3 Mon Sep 17 00:00:00 2001 From: xiaying Date: Wed, 18 Oct 2023 11:20:19 +0800 Subject: [PATCH 2/5] [MNN:Bugfix] Fix bug for ios compile error of unary int8 --- source/backend/cpu/CPUUnary.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/backend/cpu/CPUUnary.cpp b/source/backend/cpu/CPUUnary.cpp index ce4bb9f50..a32178a99 100644 --- a/source/backend/cpu/CPUUnary.cpp +++ b/source/backend/cpu/CPUUnary.cpp @@ -480,14 +480,14 @@ ErrorCode CPUUnary::onExecute(const std::vector &inputs, const std::ve return NO_ERROR; } if (mProcInt8) { - QuanPrePostParameters params; - params.inputScale = mInpScale.data(); - params.outputScale = mOupScale.data(); - params.inputZeroPoint= mInpZeroPoint.data(); - params.outputZeroPoint = mOupZeroPoint.data(); - params.maxValue = mMaxMinValue[1]; - params.minValue = mMaxMinValue[0]; MNN_CONCURRENCY_BEGIN(tId, schedule.second) { + QuanPrePostParameters params; + params.inputScale = mInpScale.data(); + params.outputScale = mOupScale.data(); + params.inputZeroPoint= mInpZeroPoint.data(); + params.outputZeroPoint = mOupZeroPoint.data(); + params.maxValue = mMaxMinValue[1]; + params.minValue = mMaxMinValue[0]; int start = schedule.first * (int)tId; int realSize = schedule.first; if (tId == schedule.second -1 ) { From a8c1f1ac20056c46f7cd9f7054915bde40a6f65e Mon Sep 17 00:00:00 2001 From: xiaying Date: Wed, 18 Oct 2023 11:36:32 +0800 Subject: [PATCH 3/5] [MNN:Bugfix] Fix compile bug of ios from CPUBinaryInt8 --- source/backend/cpu/CPUBinaryInt8.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/source/backend/cpu/CPUBinaryInt8.cpp b/source/backend/cpu/CPUBinaryInt8.cpp index cf465318b..cdb4c563e 100644 --- a/source/backend/cpu/CPUBinaryInt8.cpp +++ b/source/backend/cpu/CPUBinaryInt8.cpp @@ -76,16 +76,15 @@ ErrorCode CPUBinaryInt8::onExecute(const std::vector& inputs, const std int inpBytes = 1; int outBytes = 1; - QuanPrePostParameters params; - - params.inputScale = mInputScales.data(); - params.outputScale = mOutputScales.data(); - params.outputZeroPoint = mOutputZeros.data(); - params.inputZeroPoint = mInputZeros.data(); - params.minValue = (ssize_t)TensorUtils::getDescribe(outputs[0])->quantAttr->min; - params.maxValue = (ssize_t)TensorUtils::getDescribe(outputs[0])->quantAttr->max; - MNN_CONCURRENCY_BEGIN(tId, schedule.second) { + QuanPrePostParameters params; + + params.inputScale = mInputScales.data(); + params.outputScale = mOutputScales.data(); + params.outputZeroPoint = mOutputZeros.data(); + params.inputZeroPoint = mInputZeros.data(); + params.minValue = (ssize_t)TensorUtils::getDescribe(outputs[0])->quantAttr->min; + params.maxValue = (ssize_t)TensorUtils::getDescribe(outputs[0])->quantAttr->max; int start = schedule.first * (int)tId; int realSize = schedule.first; if (tId == schedule.second -1 ) { From b019bef591d1d6ae8aadaaf7f44f90bda5a7a0b5 Mon Sep 17 00:00:00 2001 From: xiaying Date: Thu, 19 Oct 2023 10:12:55 +0800 Subject: [PATCH 4/5] [MNN:Bugfix] Fix bug of no sse poolgrad --- source/backend/cpu/BinaryUtils.hpp | 32 ++++++++++++------------- source/backend/cpu/x86_x64/avx/Vec8.hpp | 3 +++ source/math/Vec.hpp | 6 +++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/source/backend/cpu/BinaryUtils.hpp b/source/backend/cpu/BinaryUtils.hpp index be05589ad..368e46b41 100644 --- a/source/backend/cpu/BinaryUtils.hpp +++ b/source/backend/cpu/BinaryUtils.hpp @@ -187,14 +187,14 @@ struct BinaryBitwiseXor { } }; -template +template void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, int elementSize, int needBroadcastIndex) { Func compute; const int sizeDivUnit = elementSize / pack; const int remainCount = elementSize - sizeDivUnit * pack; auto src0 = (const U*)(inputRaw0); auto src1 = (const U*)(inputRaw1); - auto dst = (U*)outputRaw; + auto dst = (Tout*)outputRaw; if (-1 == needBroadcastIndex) { if (sizeDivUnit > 0) { @@ -210,7 +210,7 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i if (remainCount > 0) { U tempSrc0[pack]; U tempSrc1[pack]; - U tempDst[pack]; + Tout tempDst[pack]; ::memcpy(tempSrc0, src0, remainCount * sizeof(U)); ::memcpy(tempSrc1, src1, remainCount * sizeof(U)); V a = V::load(tempSrc0); @@ -233,7 +233,7 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i } if (remainCount > 0) { U tempSrc1[pack]; - U tempDst[pack]; + Tout tempDst[pack]; ::memcpy(tempSrc1, src1, remainCount * sizeof(U)); V b = V::load(tempSrc1); V::save(tempDst, compute(a, b)); @@ -254,7 +254,7 @@ void executeVec(void* outputRaw, const void* inputRaw0, const void* inputRaw1, i } if (remainCount > 0) { U tempSrc0[pack]; - U tempDst[pack]; + Tout tempDst[pack]; ::memcpy(tempSrc0, src0, remainCount * sizeof(U)); V a = V::load(tempSrc0); V::save(tempDst, compute(a, b)); @@ -415,27 +415,27 @@ template MNNBinaryExecute selectVector(int type) { switch (type) { case BinaryOpOperation_ADD: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_SUB: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_MUL: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_MINIMUM: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_MAXIMUM: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_SquaredDifference: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, U>; case BinaryOpOperation_LESS: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, int32_t>; case BinaryOpOperation_LESS_EQUAL: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, int32_t>; case BinaryOpOperation_GREATER: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, int32_t>; case BinaryOpOperation_GREATER_EQUAL: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, int32_t>; case BinaryOpOperation_EQUAL: - return executeVec, V, pack, U>; + return executeVec, V, pack, U, int32_t>; } return nullptr; } diff --git a/source/backend/cpu/x86_x64/avx/Vec8.hpp b/source/backend/cpu/x86_x64/avx/Vec8.hpp index 501768e69..d5a2a7b5c 100644 --- a/source/backend/cpu/x86_x64/avx/Vec8.hpp +++ b/source/backend/cpu/x86_x64/avx/Vec8.hpp @@ -168,6 +168,9 @@ struct Vec8 { static void save(float* addr, const VecType& v) { _mm256_storeu_ps(addr, v.value); } + static void save(int32_t* addr, const VecType& v) { + _mm256_storeu_ps((float*)addr, v.value); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { _mm256_max_ps(v1.value, v2.value) }; return dst; diff --git a/source/math/Vec.hpp b/source/math/Vec.hpp index fc4d8e0d4..7ca82319b 100644 --- a/source/math/Vec.hpp +++ b/source/math/Vec.hpp @@ -413,6 +413,9 @@ struct Vec { static void save(float* addr, const VecTypeInt32& v) { vst1q_f32(addr, reinterpret_cast(v.value)); } + static void save(int32_t* addr, const VecType& v) { + vst1q_s32(addr, reinterpret_cast(v.value)); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { vmaxq_f32(v1.value, v2.value) }; return dst; @@ -763,6 +766,9 @@ struct Vec { static void save(float* addr, const VecTypeInt32& v) { _mm_storeu_ps(addr, _mm_castsi128_ps(v.value)); } + static void save(int32_t* addr, const VecType& v) { + _mm_storeu_si128((__m128i*)addr, _mm_castps_si128(v.value)); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { _mm_max_ps(v1.value, v2.value) }; return dst; From 476083aa8b1e6ab8ddb98b106d535c0b12dcdb7a Mon Sep 17 00:00:00 2001 From: xiaying Date: Fri, 20 Oct 2023 14:55:10 +0800 Subject: [PATCH 5/5] [MNN:Bugfix] Fix bug for avx512 compile --- .../backend/cpu/arm/arm32/MNNBinarySqdInt8.S | 4 +- source/backend/cpu/x86_x64/avx/Vec8.hpp | 2 +- .../cpu/x86_x64/avx512/PackedFunction.cpp | 2 +- source/backend/cpu/x86_x64/avx512/Vec16.hpp | 38 +++++++++++++++++++ test/op/BinaryOPTest.cpp | 2 +- 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/source/backend/cpu/arm/arm32/MNNBinarySqdInt8.S b/source/backend/cpu/arm/arm32/MNNBinarySqdInt8.S index 18bf17fce..86bb76813 100644 --- a/source/backend/cpu/arm/arm32/MNNBinarySqdInt8.S +++ b/source/backend/cpu/arm/arm32/MNNBinarySqdInt8.S @@ -154,7 +154,7 @@ L4Loop: vqmovn.s32 d8, q10 vqmovn.s32 d9, q11 vdup.8 q12, r3 - vdup.8 q15, r11 + vdup.8 q1, r11 vaddw.s8 q3, q3, d4 vaddw.s8 q4, q4, d4 @@ -162,7 +162,7 @@ L4Loop: vqmovn.s16 d12, q3 vqmovn.s16 d13, q4 vmax.s8 q6, q6, q12 - vmin.s8 q6, q6, q15 + vmin.s8 q6, q6, q1 cmp r6, #4 vst1.32 {q6}, [r0]! bge L4Loop diff --git a/source/backend/cpu/x86_x64/avx/Vec8.hpp b/source/backend/cpu/x86_x64/avx/Vec8.hpp index d5a2a7b5c..dc2cdb4b9 100644 --- a/source/backend/cpu/x86_x64/avx/Vec8.hpp +++ b/source/backend/cpu/x86_x64/avx/Vec8.hpp @@ -109,7 +109,7 @@ struct Vec8 { return dst; } VecType operator>(const VecType& lr) { - __m256 mask = _mm256_cmp_ps(lr.value, value, 0x01); + __m256 mask = _mm256_cmp_ps(value, lr.value, 14); VecType dst = { _mm256_and_ps(one, mask) } ; return dst; } diff --git a/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp b/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp index c1e51006c..732833db6 100644 --- a/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp +++ b/source/backend/cpu/x86_x64/avx512/PackedFunction.cpp @@ -146,7 +146,7 @@ void _AVX512_MNNConvRunForLineDepthwise(float* dst, const float* src, const floa } static MNNBinaryExecute _AVX512_MNNSelectBinaryFunctionForFloat(int opType) { - auto vecF = MNN::selectVector(opType); + auto vecF = MNN::selectVector(opType); if (nullptr != vecF) { return vecF; } diff --git a/source/backend/cpu/x86_x64/avx512/Vec16.hpp b/source/backend/cpu/x86_x64/avx512/Vec16.hpp index 43850daa3..ff0368593 100644 --- a/source/backend/cpu/x86_x64/avx512/Vec16.hpp +++ b/source/backend/cpu/x86_x64/avx512/Vec16.hpp @@ -259,6 +259,41 @@ struct Vec16 { return value[i]; #endif } + VecType operator==(const VecType& lr) const { + __m512 one = _mm512_set1_ps(1.0f); + __m512 zero = _mm512_set1_ps(0.0f); + __mmask16 mask = _mm512_cmp_ps_mask(value, lr.value, 0); + VecType dst = { _mm512_mask_blend_ps(mask, zero, one) } ; + return dst; + } + VecType operator>(const VecType& lr) { + __m512 one = _mm512_set1_ps(1.0f); + __m512 zero = _mm512_set1_ps(0.0f); + __mmask16 mask = _mm512_cmp_ps_mask(value, lr.value, 14); + VecType dst = { _mm512_mask_blend_ps(mask, zero, one) } ; + return dst; + } + VecType operator>=(const VecType& lr) { + __m512 one = _mm512_set1_ps(1.0f); + __m512 zero = _mm512_set1_ps(0.0f); + __mmask16 mask = _mm512_cmp_ps_mask(value, lr.value, 13); + VecType dst = { _mm512_mask_blend_ps(mask, zero, one) } ; + return dst; + } + VecType operator<(const VecType& lr) { + __m512 one = _mm512_set1_ps(1.0f); + __m512 zero = _mm512_set1_ps(0.0f); + __mmask16 mask = _mm512_cmp_ps_mask(value, lr.value, 0x01); + VecType dst = { _mm512_mask_blend_ps(mask, zero, one) } ; + return dst; + } + VecType operator<=(const VecType& lr) { + __m512 one = _mm512_set1_ps(1.0f); + __m512 zero = _mm512_set1_ps(0.0f); + __mmask16 mask = _mm512_cmp_ps_mask(value, lr.value, 0x02); + VecType dst = { _mm512_mask_blend_ps(mask, zero, one) } ; + return dst; + } static VecType load(const float* addr) { VecType v = { _mm512_loadu_ps(addr) }; return v; @@ -270,6 +305,9 @@ struct Vec16 { static void save(float* addr, const VecType& v) { _mm512_storeu_ps(addr, v.value); } + static void save(int32_t* addr, const VecType& v) { + _mm512_storeu_ps((float*)addr, v.value); + } static VecType max(const VecType& v1, const VecType& v2) { VecType dst = { _mm512_max_ps(v1.value, v2.value) }; return dst; diff --git a/test/op/BinaryOPTest.cpp b/test/op/BinaryOPTest.cpp index a7f25ef8f..a30be18f9 100644 --- a/test/op/BinaryOPTest.cpp +++ b/test/op/BinaryOPTest.cpp @@ -30,7 +30,7 @@ class BinaryTestCommon : public MNNTestCase { for (int i = 0; i < shape_y.size(); ++i) { size_y *= shape_y[i]; } - for (int i = 0; i < shape_y.size(); ++i) { + for (int i = 0; i < shape_out.size(); ++i) { size_out *= shape_out[i]; } if (format == NC4HW4 && data_x.size() > size_x) {