From 9d1d224caf9f936e0ace0ee5854caefb660800f2 Mon Sep 17 00:00:00 2001 From: Daniel Smilkov Date: Thu, 3 Aug 2017 13:45:13 -0400 Subject: [PATCH] initial commit --- .gitignore | 5 + .npmignore | 9 + .npmrc | 1 + .vscode/settings.json | 15 + .vscode/tasks.json | 16 + CONTRIBUTING.md | 26 + LICENSE | 202 + README.md | 100 + bower.json | 38 + demos/adder/adder.ts | 71 + demos/adder/index.html | 34 + demos/benchmarks/benchmark-demo.html | 49 + demos/benchmarks/benchmark.ts | 40 + demos/benchmarks/bundle.js | 3788 +++++++ demos/benchmarks/conv_gpu_benchmark.ts | 90 + .../conv_transpose_gpu_benchmark.ts | 85 + demos/benchmarks/logsumexp_cpu_benchmark.ts | 32 + demos/benchmarks/logsumexp_gpu_benchmark.ts | 51 + demos/benchmarks/math-benchmark-run-groups.ts | 79 + demos/benchmarks/math-benchmark.html | 95 + demos/benchmarks/math-benchmark.ts | 225 + .../max_pool_backprop_gpu_benchmark.ts | 82 + demos/benchmarks/max_pool_gpu_benchmark.ts | 121 + demos/benchmarks/mulmat_cpu_benchmark.ts | 37 + demos/benchmarks/mulmat_gpu_benchmark.ts | 98 + demos/benchmarks/tex_util_benchmark.ts | 80 + demos/chartjs.d.ts | 448 + demos/deeplearnjs.ts | 18 + demos/demo-footer.html | 56 + demos/demo-footer.ts | 15 + demos/demo-header.html | 48 + demos/demo-header.ts | 15 + demos/font-embedding/bundle.js | 8217 ++++++++++++++ demos/homepage/Gemfile | 26 + demos/homepage/Gemfile.lock | 205 + demos/homepage/_config.yml | 32 + demos/homepage/_includes/footer.html | 32 + demos/homepage/_includes/header.html | 31 + demos/homepage/_layouts/default.html | 36 + demos/homepage/_layouts/page.html | 30 + demos/homepage/assets/style.css | 280 + demos/homepage/bundle.js | 8216 ++++++++++++++ demos/homepage/index.md | 256 + demos/homepage/index.ts | 150 + demos/imagenet/bundle.js | 9296 ++++++++++++++++ demos/imagenet/imagenet-demo.html | 49 + demos/imagenet/imagenet-demo.ts | 220 + demos/imagenet/imagenet.html | 114 + demos/imagenet/images/beerbottle.jpg | Bin 0 -> 4257 bytes demos/imagenet/images/cat.jpg | Bin 0 -> 24557 bytes demos/imagenet/images/dog1.jpg | Bin 0 -> 12279 bytes demos/imagenet/images/dog2.jpg | Bin 0 -> 14872 bytes demos/imagenet/images/piano.jpg | Bin 0 -> 10271 bytes demos/imagenet/images/saxophone.jpg | Bin 0 -> 5423 bytes demos/images/benchmark.png | Bin 0 -> 47293 bytes demos/images/font-embedding.png | Bin 0 -> 57566 bytes demos/images/imagenet.png | Bin 0 -> 417161 bytes demos/images/model-builder.png | Bin 0 -> 100516 bytes demos/images/nn-art.png | Bin 0 -> 96832 bytes demos/images/paint-image.png | Bin 0 -> 457134 bytes demos/mnist/fully_connected_feed.py | 253 + demos/mnist/hidden1_biases | 3 + demos/mnist/hidden1_weights | Bin 0 -> 401408 bytes demos/mnist/hidden2_biases | 1 + demos/mnist/hidden2_weights | Bin 0 -> 16384 bytes demos/mnist/index.html | 3 + demos/mnist/manifest.json | 41 + demos/mnist/mnist.md | 125 + demos/mnist/mnist.ts | 139 + demos/mnist/sample_data.json | 1 + demos/mnist/softmax_linear_biases | 1 + demos/mnist/softmax_linear_weights | Bin 0 -> 1280 bytes demos/model-builder/bundle.js | 9461 +++++++++++++++++ demos/model-builder/cifar10-conv.json | 1 + demos/model-builder/layer_builder.ts | 370 + demos/model-builder/mnist-conv.json | 1 + .../model-builder/mnist-fully-connected.json | 1 + .../model-builder-datasets-config.json | 43 + demos/model-builder/model-builder-demo.html | 55 + demos/model-builder/model-builder.html | 345 + demos/model-builder/model-builder.ts | 839 ++ demos/model-builder/model-layer.html | 135 + demos/model-builder/model-layer.ts | 190 + demos/model-builder/model_builder_util.ts | 18 + demos/model-builder/tensorflow.ts | 200 + demos/models/imagenet_classes.ts | 1034 ++ demos/models/imagenet_util.ts | 160 + demos/models/squeezenet.ts | 191 + demos/ndarray-image-visualizer.html | 25 + demos/ndarray-image-visualizer.ts | 90 + demos/ndarray-logits-visualizer.html | 45 + demos/ndarray-logits-visualizer.ts | 98 + demos/nn-art/bundle.js | 8264 ++++++++++++++ demos/nn-art/cppn.ts | 186 + demos/nn-art/nn-art-demo.html | 55 + demos/nn-art/nn-art.html | 97 + demos/nn-art/nn-art.ts | 126 + demos/nn-art/nn_art_util.ts | 179 + demos/one_plus_one/index.html | 22 + demos/one_plus_one/one_plus_one.ts | 52 + demos/paint-image/bundle.js | 8333 +++++++++++++++ demos/polymer-spec.ts | 64 + demos/xhr-dataset.ts | 195 + docs/roadmap.md | 72 + docs/tutorials/index.md | 24 + docs/tutorials/intro.md | 310 + docs/tutorials/ml_beginners.md | 303 + karma.conf.js | 31 + package.json | 35 + scripts/build-demo | 28 + scripts/build-npm.sh | 20 + scripts/build-standalone.sh | 18 + scripts/convert_uint8_tensor_to_png.py | 63 + scripts/deploy-demo | 36 + scripts/dump_checkpoint_vars.py | 100 + scripts/make-website.sh | 53 + scripts/watch-demo | 33 + src/checkpoint_loader.ts | 137 + src/checkpoint_loader_test.ts | 99 + src/dataset.ts | 266 + src/dataset_test.ts | 100 + src/graph.ts | 905 ++ src/graph_layers.ts | 51 + src/graph_runner.ts | 357 + src/graph_runner_test.ts | 204 + src/graph_test.ts | 621 ++ src/graph_util.ts | 132 + src/graph_util_test.ts | 225 + src/index.ts | 37 + src/initializers.ts | 125 + src/input_provider.ts | 169 + src/input_provider_test.ts | 140 + src/math/activation_functions.ts | 84 + src/math/activation_functions_test.ts | 97 + src/math/concat3d_util.ts | 49 + src/math/concat3d_util_test.ts | 66 + src/math/conv_util.ts | 73 + src/math/copy2d_util.ts | 27 + src/math/cost_functions.ts | 49 + src/math/cost_functions_test.ts | 49 + src/math/math.ts | 1065 ++ src/math/math_cpu.ts | 898 ++ src/math/math_cpu_test.ts | 1550 +++ src/math/math_gpu.ts | 1303 +++ src/math/math_gpu_test.ts | 2134 ++++ src/math/ndarray.ts | 569 + src/math/ndarray_test.ts | 338 + src/math/webgl/addscaledmat_gpu.ts | 84 + src/math/webgl/addscaledmat_gpu_test.ts | 75 + src/math/webgl/addsubmuldiv_gpu.ts | 116 + src/math/webgl/addsubmuldiv_gpu_test.ts | 214 + src/math/webgl/argmaxequals_gpu.ts | 62 + src/math/webgl/argmaxequals_gpu_test.ts | 63 + src/math/webgl/argminmax_gpu.ts | 90 + src/math/webgl/argminmax_gpu_test.ts | 119 + src/math/webgl/avg_pool_gpu.ts | 30 + src/math/webgl/avg_pool_gpu_test.ts | 112 + src/math/webgl/batchnorm_gpu.ts | 131 + src/math/webgl/batchnorm_gpu_test.ts | 217 + src/math/webgl/binaryop_gpu.ts | 78 + src/math/webgl/concat3d_gpu.ts | 74 + src/math/webgl/concat3d_gpu_test.ts | 105 + src/math/webgl/conv_backprop_gpu.ts | 252 + .../webgl/conv_backprop_gpu_derbias_test.ts | 74 + .../conv_backprop_gpu_derweights_test.ts | 120 + .../webgl/conv_backprop_transpose_gpu_test.ts | 144 + src/math/webgl/conv_gpu.ts | 151 + src/math/webgl/conv_gpu_getbiasvalue_test.ts | 85 + .../conv_gpu_getmatrixvalueorzeropad_test.ts | 139 + src/math/webgl/conv_gpu_test.ts | 413 + src/math/webgl/copy_gpu.ts | 63 + src/math/webgl/copy_gpu_test.ts | 189 + src/math/webgl/exp_gpu.ts | 36 + src/math/webgl/exp_gpu_test.ts | 49 + src/math/webgl/gpgpu_context.ts | 310 + src/math/webgl/gpgpu_context_test.ts | 421 + src/math/webgl/gpgpu_util.ts | 258 + src/math/webgl/gpgpu_util_test.ts | 117 + src/math/webgl/log_gpu.ts | 36 + src/math/webgl/log_gpu_test.ts | 50 + src/math/webgl/logsumexp_gpu.ts | 73 + src/math/webgl/logsumexp_gpu_test.ts | 54 + src/math/webgl/max_pool_backprop_gpu.ts | 101 + src/math/webgl/max_pool_backprop_gpu_test.ts | 141 + src/math/webgl/max_pool_gpu.ts | 44 + src/math/webgl/max_pool_gpu_test.ts | 114 + src/math/webgl/max_pool_positions_gpu_test.ts | 110 + src/math/webgl/min_pool_gpu.ts | 30 + src/math/webgl/min_pool_gpu_test.ts | 112 + src/math/webgl/minmax_gpu.ts | 66 + src/math/webgl/minmax_gpu_test.ts | 103 + src/math/webgl/mulbcast_gpu.ts | 90 + src/math/webgl/mulbcast_gpu_test.ts | 140 + src/math/webgl/mulmat_gpu.ts | 62 + src/math/webgl/mulmat_gpu_test.ts | 382 + src/math/webgl/mulmat_packed_gpu.ts | 130 + src/math/webgl/mulmat_packed_gpu_test.ts | 404 + src/math/webgl/neg_gpu.ts | 36 + src/math/webgl/neg_gpu_test.ts | 50 + src/math/webgl/pool_gpu.ts | 121 + src/math/webgl/reducesum_gpu.ts | 63 + src/math/webgl/reducesum_gpu_test.ts | 65 + src/math/webgl/relu_gpu.ts | 39 + src/math/webgl/relu_gpu_test.ts | 57 + src/math/webgl/render_ndarray_gpu_util.ts | 59 + .../webgl/render_ndarray_gpu_util_test.ts | 70 + src/math/webgl/reshape_gpu.ts | 65 + src/math/webgl/reshape_gpu_test.ts | 88 + src/math/webgl/resize_bilinear_gpu.ts | 92 + src/math/webgl/resize_bilinear_gpu_test.ts | 127 + src/math/webgl/shader_compiler.ts | 126 + src/math/webgl/sigmoid_gpu.ts | 37 + src/math/webgl/sigmoid_gpu_test.ts | 38 + src/math/webgl/step_gpu.ts | 39 + src/math/webgl/step_gpu_test.ts | 65 + src/math/webgl/tex_util.ts | 233 + src/math/webgl/tex_util_test.ts | 301 + src/math/webgl/texture_manager.ts | 92 + src/math/webgl/trig_gpu.ts | 66 + src/math/webgl/trig_gpu_test.ts | 57 + src/math/webgl/unaryop_gpu.ts | 55 + src/math/webgl/webgl_util.ts | 418 + src/operation_emitter.ts | 125 + src/ops/add.ts | 102 + src/ops/add_test.ts | 193 + src/ops/argmax.ts | 47 + src/ops/argmax_test.ts | 67 + src/ops/argmaxequals.ts | 50 + src/ops/argmaxequals_test.ts | 80 + src/ops/concat3d.ts | 57 + src/ops/concat3d_test.ts | 115 + src/ops/convolution.ts | 100 + src/ops/convolution_test.ts | 352 + src/ops/divide.ts | 114 + src/ops/divide_test.ts | 158 + src/ops/element_wise_activation.ts | 93 + src/ops/element_wise_activation_test.ts | 145 + src/ops/element_wise_cost.ts | 80 + src/ops/element_wise_cost_test.ts | 73 + src/ops/exp.ts | 56 + src/ops/exp_test.ts | 74 + src/ops/linear_combination.ts | 83 + src/ops/linear_combination_test.ts | 99 + src/ops/log.ts | 56 + src/ops/log_test.ts | 74 + src/ops/matmul.ts | 93 + src/ops/matmul_test.ts | 204 + src/ops/max_pool.ts | 72 + src/ops/max_pool_test.ts | 158 + src/ops/multiply.ts | 99 + src/ops/multiply_test.ts | 150 + src/ops/op.ts | 35 + src/ops/reduce_sum.ts | 62 + src/ops/reduce_sum_test.ts | 79 + src/ops/reshape.ts | 53 + src/ops/softmax.ts | 99 + src/ops/softmax_test.ts | 79 + src/ops/split.ts | 62 + src/ops/split_test.ts | 76 + src/ops/subtract.ts | 102 + src/ops/subtract_test.ts | 197 + src/optimizer.ts | 47 + src/priority_queue.ts | 223 + src/priority_queue_test.ts | 199 + src/session.ts | 285 + src/session_test.ts | 314 + src/session_util.ts | 304 + src/session_util_test.ts | 464 + src/sgd_optimizer.ts | 93 + src/tensor_array_map.ts | 107 + src/tensor_array_map_test.ts | 108 + src/test_util.ts | 92 + src/util.ts | 182 + src/util_test.ts | 89 + tsconfig-doc.json | 31 + tsconfig.json | 13 + tslint.json | 55 + 277 files changed, 93457 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .npmrc create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bower.json create mode 100644 demos/adder/adder.ts create mode 100644 demos/adder/index.html create mode 100644 demos/benchmarks/benchmark-demo.html create mode 100644 demos/benchmarks/benchmark.ts create mode 100644 demos/benchmarks/bundle.js create mode 100644 demos/benchmarks/conv_gpu_benchmark.ts create mode 100644 demos/benchmarks/conv_transpose_gpu_benchmark.ts create mode 100644 demos/benchmarks/logsumexp_cpu_benchmark.ts create mode 100644 demos/benchmarks/logsumexp_gpu_benchmark.ts create mode 100644 demos/benchmarks/math-benchmark-run-groups.ts create mode 100644 demos/benchmarks/math-benchmark.html create mode 100644 demos/benchmarks/math-benchmark.ts create mode 100644 demos/benchmarks/max_pool_backprop_gpu_benchmark.ts create mode 100644 demos/benchmarks/max_pool_gpu_benchmark.ts create mode 100644 demos/benchmarks/mulmat_cpu_benchmark.ts create mode 100644 demos/benchmarks/mulmat_gpu_benchmark.ts create mode 100644 demos/benchmarks/tex_util_benchmark.ts create mode 100644 demos/chartjs.d.ts create mode 100644 demos/deeplearnjs.ts create mode 100644 demos/demo-footer.html create mode 100644 demos/demo-footer.ts create mode 100644 demos/demo-header.html create mode 100644 demos/demo-header.ts create mode 100644 demos/font-embedding/bundle.js create mode 100644 demos/homepage/Gemfile create mode 100644 demos/homepage/Gemfile.lock create mode 100644 demos/homepage/_config.yml create mode 100644 demos/homepage/_includes/footer.html create mode 100644 demos/homepage/_includes/header.html create mode 100644 demos/homepage/_layouts/default.html create mode 100644 demos/homepage/_layouts/page.html create mode 100644 demos/homepage/assets/style.css create mode 100644 demos/homepage/bundle.js create mode 100644 demos/homepage/index.md create mode 100644 demos/homepage/index.ts create mode 100644 demos/imagenet/bundle.js create mode 100644 demos/imagenet/imagenet-demo.html create mode 100644 demos/imagenet/imagenet-demo.ts create mode 100644 demos/imagenet/imagenet.html create mode 100644 demos/imagenet/images/beerbottle.jpg create mode 100644 demos/imagenet/images/cat.jpg create mode 100644 demos/imagenet/images/dog1.jpg create mode 100644 demos/imagenet/images/dog2.jpg create mode 100644 demos/imagenet/images/piano.jpg create mode 100644 demos/imagenet/images/saxophone.jpg create mode 100644 demos/images/benchmark.png create mode 100644 demos/images/font-embedding.png create mode 100644 demos/images/imagenet.png create mode 100644 demos/images/model-builder.png create mode 100644 demos/images/nn-art.png create mode 100644 demos/images/paint-image.png create mode 100644 demos/mnist/fully_connected_feed.py create mode 100644 demos/mnist/hidden1_biases create mode 100644 demos/mnist/hidden1_weights create mode 100644 demos/mnist/hidden2_biases create mode 100644 demos/mnist/hidden2_weights create mode 100644 demos/mnist/index.html create mode 100644 demos/mnist/manifest.json create mode 100644 demos/mnist/mnist.md create mode 100644 demos/mnist/mnist.ts create mode 100644 demos/mnist/sample_data.json create mode 100644 demos/mnist/softmax_linear_biases create mode 100644 demos/mnist/softmax_linear_weights create mode 100644 demos/model-builder/bundle.js create mode 100644 demos/model-builder/cifar10-conv.json create mode 100644 demos/model-builder/layer_builder.ts create mode 100644 demos/model-builder/mnist-conv.json create mode 100644 demos/model-builder/mnist-fully-connected.json create mode 100644 demos/model-builder/model-builder-datasets-config.json create mode 100644 demos/model-builder/model-builder-demo.html create mode 100644 demos/model-builder/model-builder.html create mode 100644 demos/model-builder/model-builder.ts create mode 100644 demos/model-builder/model-layer.html create mode 100644 demos/model-builder/model-layer.ts create mode 100644 demos/model-builder/model_builder_util.ts create mode 100644 demos/model-builder/tensorflow.ts create mode 100644 demos/models/imagenet_classes.ts create mode 100644 demos/models/imagenet_util.ts create mode 100644 demos/models/squeezenet.ts create mode 100644 demos/ndarray-image-visualizer.html create mode 100644 demos/ndarray-image-visualizer.ts create mode 100644 demos/ndarray-logits-visualizer.html create mode 100644 demos/ndarray-logits-visualizer.ts create mode 100644 demos/nn-art/bundle.js create mode 100644 demos/nn-art/cppn.ts create mode 100644 demos/nn-art/nn-art-demo.html create mode 100644 demos/nn-art/nn-art.html create mode 100644 demos/nn-art/nn-art.ts create mode 100644 demos/nn-art/nn_art_util.ts create mode 100644 demos/one_plus_one/index.html create mode 100644 demos/one_plus_one/one_plus_one.ts create mode 100644 demos/paint-image/bundle.js create mode 100644 demos/polymer-spec.ts create mode 100644 demos/xhr-dataset.ts create mode 100644 docs/roadmap.md create mode 100644 docs/tutorials/index.md create mode 100644 docs/tutorials/intro.md create mode 100644 docs/tutorials/ml_beginners.md create mode 100644 karma.conf.js create mode 100644 package.json create mode 100755 scripts/build-demo create mode 100755 scripts/build-npm.sh create mode 100755 scripts/build-standalone.sh create mode 100644 scripts/convert_uint8_tensor_to_png.py create mode 100755 scripts/deploy-demo create mode 100644 scripts/dump_checkpoint_vars.py create mode 100755 scripts/make-website.sh create mode 100755 scripts/watch-demo create mode 100644 src/checkpoint_loader.ts create mode 100644 src/checkpoint_loader_test.ts create mode 100644 src/dataset.ts create mode 100644 src/dataset_test.ts create mode 100644 src/graph.ts create mode 100644 src/graph_layers.ts create mode 100644 src/graph_runner.ts create mode 100644 src/graph_runner_test.ts create mode 100644 src/graph_test.ts create mode 100644 src/graph_util.ts create mode 100644 src/graph_util_test.ts create mode 100644 src/index.ts create mode 100644 src/initializers.ts create mode 100644 src/input_provider.ts create mode 100644 src/input_provider_test.ts create mode 100644 src/math/activation_functions.ts create mode 100644 src/math/activation_functions_test.ts create mode 100644 src/math/concat3d_util.ts create mode 100644 src/math/concat3d_util_test.ts create mode 100644 src/math/conv_util.ts create mode 100644 src/math/copy2d_util.ts create mode 100644 src/math/cost_functions.ts create mode 100644 src/math/cost_functions_test.ts create mode 100644 src/math/math.ts create mode 100644 src/math/math_cpu.ts create mode 100644 src/math/math_cpu_test.ts create mode 100644 src/math/math_gpu.ts create mode 100644 src/math/math_gpu_test.ts create mode 100644 src/math/ndarray.ts create mode 100644 src/math/ndarray_test.ts create mode 100644 src/math/webgl/addscaledmat_gpu.ts create mode 100644 src/math/webgl/addscaledmat_gpu_test.ts create mode 100644 src/math/webgl/addsubmuldiv_gpu.ts create mode 100644 src/math/webgl/addsubmuldiv_gpu_test.ts create mode 100644 src/math/webgl/argmaxequals_gpu.ts create mode 100644 src/math/webgl/argmaxequals_gpu_test.ts create mode 100644 src/math/webgl/argminmax_gpu.ts create mode 100644 src/math/webgl/argminmax_gpu_test.ts create mode 100644 src/math/webgl/avg_pool_gpu.ts create mode 100644 src/math/webgl/avg_pool_gpu_test.ts create mode 100644 src/math/webgl/batchnorm_gpu.ts create mode 100644 src/math/webgl/batchnorm_gpu_test.ts create mode 100644 src/math/webgl/binaryop_gpu.ts create mode 100644 src/math/webgl/concat3d_gpu.ts create mode 100644 src/math/webgl/concat3d_gpu_test.ts create mode 100644 src/math/webgl/conv_backprop_gpu.ts create mode 100644 src/math/webgl/conv_backprop_gpu_derbias_test.ts create mode 100644 src/math/webgl/conv_backprop_gpu_derweights_test.ts create mode 100644 src/math/webgl/conv_backprop_transpose_gpu_test.ts create mode 100644 src/math/webgl/conv_gpu.ts create mode 100644 src/math/webgl/conv_gpu_getbiasvalue_test.ts create mode 100644 src/math/webgl/conv_gpu_getmatrixvalueorzeropad_test.ts create mode 100644 src/math/webgl/conv_gpu_test.ts create mode 100644 src/math/webgl/copy_gpu.ts create mode 100644 src/math/webgl/copy_gpu_test.ts create mode 100644 src/math/webgl/exp_gpu.ts create mode 100644 src/math/webgl/exp_gpu_test.ts create mode 100644 src/math/webgl/gpgpu_context.ts create mode 100644 src/math/webgl/gpgpu_context_test.ts create mode 100644 src/math/webgl/gpgpu_util.ts create mode 100644 src/math/webgl/gpgpu_util_test.ts create mode 100644 src/math/webgl/log_gpu.ts create mode 100644 src/math/webgl/log_gpu_test.ts create mode 100644 src/math/webgl/logsumexp_gpu.ts create mode 100644 src/math/webgl/logsumexp_gpu_test.ts create mode 100644 src/math/webgl/max_pool_backprop_gpu.ts create mode 100644 src/math/webgl/max_pool_backprop_gpu_test.ts create mode 100644 src/math/webgl/max_pool_gpu.ts create mode 100644 src/math/webgl/max_pool_gpu_test.ts create mode 100644 src/math/webgl/max_pool_positions_gpu_test.ts create mode 100644 src/math/webgl/min_pool_gpu.ts create mode 100644 src/math/webgl/min_pool_gpu_test.ts create mode 100644 src/math/webgl/minmax_gpu.ts create mode 100644 src/math/webgl/minmax_gpu_test.ts create mode 100644 src/math/webgl/mulbcast_gpu.ts create mode 100644 src/math/webgl/mulbcast_gpu_test.ts create mode 100644 src/math/webgl/mulmat_gpu.ts create mode 100644 src/math/webgl/mulmat_gpu_test.ts create mode 100644 src/math/webgl/mulmat_packed_gpu.ts create mode 100644 src/math/webgl/mulmat_packed_gpu_test.ts create mode 100644 src/math/webgl/neg_gpu.ts create mode 100644 src/math/webgl/neg_gpu_test.ts create mode 100644 src/math/webgl/pool_gpu.ts create mode 100644 src/math/webgl/reducesum_gpu.ts create mode 100644 src/math/webgl/reducesum_gpu_test.ts create mode 100644 src/math/webgl/relu_gpu.ts create mode 100644 src/math/webgl/relu_gpu_test.ts create mode 100644 src/math/webgl/render_ndarray_gpu_util.ts create mode 100644 src/math/webgl/render_ndarray_gpu_util_test.ts create mode 100644 src/math/webgl/reshape_gpu.ts create mode 100644 src/math/webgl/reshape_gpu_test.ts create mode 100644 src/math/webgl/resize_bilinear_gpu.ts create mode 100644 src/math/webgl/resize_bilinear_gpu_test.ts create mode 100644 src/math/webgl/shader_compiler.ts create mode 100644 src/math/webgl/sigmoid_gpu.ts create mode 100644 src/math/webgl/sigmoid_gpu_test.ts create mode 100644 src/math/webgl/step_gpu.ts create mode 100644 src/math/webgl/step_gpu_test.ts create mode 100644 src/math/webgl/tex_util.ts create mode 100644 src/math/webgl/tex_util_test.ts create mode 100644 src/math/webgl/texture_manager.ts create mode 100644 src/math/webgl/trig_gpu.ts create mode 100644 src/math/webgl/trig_gpu_test.ts create mode 100644 src/math/webgl/unaryop_gpu.ts create mode 100644 src/math/webgl/webgl_util.ts create mode 100644 src/operation_emitter.ts create mode 100644 src/ops/add.ts create mode 100644 src/ops/add_test.ts create mode 100644 src/ops/argmax.ts create mode 100644 src/ops/argmax_test.ts create mode 100644 src/ops/argmaxequals.ts create mode 100644 src/ops/argmaxequals_test.ts create mode 100644 src/ops/concat3d.ts create mode 100644 src/ops/concat3d_test.ts create mode 100644 src/ops/convolution.ts create mode 100644 src/ops/convolution_test.ts create mode 100644 src/ops/divide.ts create mode 100644 src/ops/divide_test.ts create mode 100644 src/ops/element_wise_activation.ts create mode 100644 src/ops/element_wise_activation_test.ts create mode 100644 src/ops/element_wise_cost.ts create mode 100644 src/ops/element_wise_cost_test.ts create mode 100644 src/ops/exp.ts create mode 100644 src/ops/exp_test.ts create mode 100644 src/ops/linear_combination.ts create mode 100644 src/ops/linear_combination_test.ts create mode 100644 src/ops/log.ts create mode 100644 src/ops/log_test.ts create mode 100644 src/ops/matmul.ts create mode 100644 src/ops/matmul_test.ts create mode 100644 src/ops/max_pool.ts create mode 100644 src/ops/max_pool_test.ts create mode 100644 src/ops/multiply.ts create mode 100644 src/ops/multiply_test.ts create mode 100644 src/ops/op.ts create mode 100644 src/ops/reduce_sum.ts create mode 100644 src/ops/reduce_sum_test.ts create mode 100644 src/ops/reshape.ts create mode 100644 src/ops/softmax.ts create mode 100644 src/ops/softmax_test.ts create mode 100644 src/ops/split.ts create mode 100644 src/ops/split_test.ts create mode 100644 src/ops/subtract.ts create mode 100644 src/ops/subtract_test.ts create mode 100644 src/optimizer.ts create mode 100644 src/priority_queue.ts create mode 100644 src/priority_queue_test.ts create mode 100644 src/session.ts create mode 100644 src/session_test.ts create mode 100644 src/session_util.ts create mode 100644 src/session_util_test.ts create mode 100644 src/sgd_optimizer.ts create mode 100644 src/tensor_array_map.ts create mode 100644 src/tensor_array_map_test.ts create mode 100644 src/test_util.ts create mode 100644 src/util.ts create mode 100644 src/util_test.ts create mode 100644 tsconfig-doc.json create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..70399bf242 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +demos/**/*.js +demos/**/*.js.map +coverage/ +bower_components/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000..ea77052f14 --- /dev/null +++ b/.npmignore @@ -0,0 +1,9 @@ +demos/ +dist/demos/ +dist/**/*.js.map +docs/ +scripts/ +src/ +coverage/ +bower_components/ +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..43c97e719a --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..6d098153f1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "coverage/": true, + "dist/": true + }, + "tslint.configFile": "tslint.json", + "tslint.enable": true, + "tslint.run": "onType", + "editor.tabSize": 2, + "editor.insertSpaces": true, + "files.insertFinalNewline": true +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..dd81624820 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "command": "npm", + "taskName": "lint", + "type": "shell", + "suppressTaskName": true, + "args": [ "run", "lint"], + "problemMatcher": { + "base": "$tslint5", + "fileLocation": "absolute" + } + } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..a9851beff2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution, +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +We require unit tests for most code, instructions for running our unit test +suites are in the documentation. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..7eb2ce669a --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +--- +layout: page +order: 1 +--- + +# Getting started + +**deeplearn.js** is an open source hardware-accelerated JavaScript library for +machine intelligence. **deeplearn.js** brings performant machine learning +building blocks to the web, allowing you to train neural networks in a browser +or run pre-trained models in inference mode. + +**deeplearn.js** has two APIs, an immediate execution model (think NumPy) and a +deferred execution model mirroring the TensorFlow API. +**deeplearn.js** +was originally developed by the Google Brain PAIR team to build powerful +interactive machine learning tools for the browser, but it can be used for +everything from education, to model understanding, to art projects. + +## Usage + +#### From JavaScript + +Typescript is the preferred language of choice for **deeplearn.js**, however +you can use it with plain JavaScript. + +For this use case, you can load the library directly from Google CDN: + +```html + +``` + +#### From TypeScript + +To build **deeplearn.js** from source, we need to clone the project and prepare +the dev environment: + +```bash +$ git clone https://pair-code.github.com/learnjs +$ cd learnjs +$ npm run prep # Installs node modules and bower components. +``` + +To build a standalone library that can be used directly in the browser using a +` + diff --git a/demos/benchmarks/benchmark-demo.html b/demos/benchmarks/benchmark-demo.html new file mode 100644 index 0000000000..ba981fb1ee --- /dev/null +++ b/demos/benchmarks/benchmark-demo.html @@ -0,0 +1,49 @@ + + + + + + + + + + LearnJS Benchmarks + + + + + + + + + + diff --git a/demos/benchmarks/benchmark.ts b/demos/benchmarks/benchmark.ts new file mode 100644 index 0000000000..c069c3602c --- /dev/null +++ b/demos/benchmarks/benchmark.ts @@ -0,0 +1,40 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export interface BenchmarkRunGroup { + name: string; + // Min and max steps to run the benchmark test over. + min: number; + max: number; + // The size of the step to take between benchmark runs. + stepSize: number; + // A transformation of step to the size passed to the benchmark test. + stepToSizeTransformation?: (step: number) => number; + benchmarkRuns: BenchmarkRun[]; +} + +export class BenchmarkRun { + name: string; + benchmarkTest: BenchmarkTest; + + chartData: ChartData[]; + constructor(name: string, benchmarkTest: BenchmarkTest) { + this.name = name; + this.benchmarkTest = benchmarkTest; + this.chartData = []; + } +} + +export interface BenchmarkTest { (size: number): number; } diff --git a/demos/benchmarks/bundle.js b/demos/benchmarks/bundle.js new file mode 100644 index 0000000000..2a98c40f07 --- /dev/null +++ b/demos/benchmarks/bundle.js @@ -0,0 +1,3788 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o benchmarkRunGroup.max || + this.stopMessages[benchmarkRunGroupIndex]) { + this.stopMessages[benchmarkRunGroupIndex] = false; + runNumbersTable.style.display = ''; + var canvas = this.querySelectorAll('.run-plot')[benchmarkRunGroupIndex]; + canvas.style.display = 'block'; + chart.update(); + var runMessage = this.querySelectorAll('.run-message')[benchmarkRunGroupIndex]; + runMessage.style.display = 'none'; + return; + } + var runNumberRowElement = document.createElement('div'); + runNumberRowElement.className = 'run-numbers-row math-benchmark'; + var rowValues = ['' + step]; + for (var i = 0; i < benchmarkRunGroup.benchmarkRuns.length; i++) { + var benchmarkRun = benchmarkRunGroup.benchmarkRuns[i]; + var benchmarkTest = benchmarkRun.benchmarkTest; + var size = benchmarkRunGroup.stepToSizeTransformation != null ? + benchmarkRunGroup.stepToSizeTransformation(step) : + step; + var resultString = void 0; + var logString = void 0; + var time = 0; + var success = true; + try { + time = benchmarkTest(size); + resultString = time.toFixed(3) + 'ms'; + logString = resultString; + } + catch (e) { + success = false; + resultString = 'Error'; + logString = e.message; + } + if (time >= 0) { + if (success) { + benchmarkRun.chartData.push({ x: step, y: time }); + } + rowValues.push(resultString); + } + console.log(benchmarkRun.name + '[' + step + ']: ' + logString); + } + runNumbersTable.appendChild(this.buildRunNumbersRow(rowValues)); + step += benchmarkRunGroup.stepSize; + setTimeout(function () { return _this.runBenchmarkSteps(chart, benchmarkRunGroup, benchmarkRunGroupIndex, step); }, 100); + }; + return MathBenchmark; +}(exports.MathBenchmarkPolymer)); +exports.MathBenchmark = MathBenchmark; +document.registerElement(MathBenchmark.prototype.is, MathBenchmark); + +},{"../demo-footer":13,"../demo-header":14,"../polymer-spec":15,"./math-benchmark-run-groups":6}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../../src/math/conv_util"); +var gpgpu_context_1 = require("../../src/math/webgl/gpgpu_context"); +var max_pool_backprop_gpu = require("../../src/math/webgl/max_pool_backprop_gpu"); +var test_util = require("../../src/test_util"); +var util = require("../../src/util"); +var OP_RUNS = 100; +exports.BENCHMARK_TEST = function (size) { + var dyShapeRCD = [size, size, 1]; + var outputDepth = 1; + var fieldSize = 11; + var stride = 1; + var zeroPad = conv_util.computeDefaultPad(dyShapeRCD, fieldSize, stride); + var outputShapeRCD = conv_util.computeOutputShape3D(dyShapeRCD, fieldSize, outputDepth, stride, zeroPad); + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dyShapeRCD, fieldSize, stride, zeroPad)); + var dyTexture = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + var maxPositionsTexture = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + var outputTexture = gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + var dyData = test_util.randomArrayInRange(dyTexShapeRC[0] * dyTexShapeRC[1], -1, 1); + var maxPositionsData = new Float32Array(util.sizeFromShape(dyShapeRCD)); + for (var i = 0; i < maxPositionsData.length; i++) { + maxPositionsData[i] = Math.floor(Math.random() * fieldSize * fieldSize); + } + gpgpu.uploadMatrixToTexture(dyTexture, dyTexShapeRC[0], dyTexShapeRC[1], dyData); + gpgpu.uploadMatrixToTexture(maxPositionsTexture, dyTexShapeRC[0], dyTexShapeRC[1], maxPositionsData); + var start = performance.now(); + for (var i = 0; i < OP_RUNS; i++) { + max_pool_backprop_gpu.maxPoolBackprop(gpgpu, program, dyTexture, maxPositionsTexture, outputTexture, outputTexShapeRC); + } + gpgpu.downloadMatrixFromTexture(outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + var end = performance.now(); + var avgTime = (end - start) / OP_RUNS; + gpgpu.deleteMatrixTexture(dyTexture); + gpgpu.deleteMatrixTexture(maxPositionsTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return avgTime; +}; + +},{"../../src/math/conv_util":17,"../../src/math/webgl/gpgpu_context":24,"../../src/math/webgl/max_pool_backprop_gpu":27,"../../src/test_util":35,"../../src/util":36}],9:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../../src/math/conv_util"); +var gpgpu_context_1 = require("../../src/math/webgl/gpgpu_context"); +var max_pool_gpu = require("../../src/math/webgl/max_pool_gpu"); +var test_util = require("../../src/test_util"); +var OP_RUNS = 100; +exports.MAX_POOL_BENCHMARK_TEST = function (size) { + var inputShapeRCD = [size, size, 1]; + var outputDepth = 1; + var fieldSize = 11; + var stride = 1; + var zeroPad = conv_util.computeDefaultPad(inputShapeRCD, fieldSize, stride); + var outputShapeRCD = conv_util.computeOutputShape3D(inputShapeRCD, fieldSize, outputDepth, stride, zeroPad); + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(max_pool_gpu.getFragmentShaderMaxPoolSource(inputShapeRCD, fieldSize, stride, zeroPad)); + var inputTexture = gpgpu.createMatrixTexture(inputTexShapeRC[0], inputTexShapeRC[1]); + var outputTexture = gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + var inputData = test_util.randomArrayInRange(inputTexShapeRC[0] * inputTexShapeRC[1], -1, 1); + gpgpu.uploadMatrixToTexture(inputTexture, inputTexShapeRC[0], inputTexShapeRC[1], inputData); + var start = performance.now(); + for (var i = 0; i < OP_RUNS; i++) { + max_pool_gpu.maxPoolCommon(gpgpu, program, inputTexture, outputTexture, outputTexShapeRC); + } + gpgpu.downloadMatrixFromTexture(outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + var end = performance.now(); + var avgTime = (end - start) / OP_RUNS; + gpgpu.deleteMatrixTexture(inputTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return avgTime; +}; +exports.MAX_POOL_POSNS_BENCHMARK_TEST = function (size) { + var inputShapeRCD = [size, size, 1]; + var outputDepth = 1; + var fieldSize = 11; + var stride = 1; + var zeroPad = conv_util.computeDefaultPad(inputShapeRCD, fieldSize, stride); + var outputShapeRCD = conv_util.computeOutputShape3D(inputShapeRCD, fieldSize, outputDepth, stride, zeroPad); + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(inputShapeRCD, fieldSize, stride, zeroPad)); + var inputTexture = gpgpu.createMatrixTexture(inputTexShapeRC[0], inputTexShapeRC[1]); + var outputTexture = gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + var inputData = test_util.randomArrayInRange(inputTexShapeRC[0] * inputTexShapeRC[1], -1, 1); + gpgpu.uploadMatrixToTexture(inputTexture, inputTexShapeRC[0], inputTexShapeRC[1], inputData); + var start = performance.now(); + for (var i = 0; i < OP_RUNS; i++) { + max_pool_gpu.maxPoolCommon(gpgpu, program, inputTexture, outputTexture, outputTexShapeRC); + } + gpgpu.downloadMatrixFromTexture(outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + var end = performance.now(); + var avgTime = (end - start) / OP_RUNS; + gpgpu.deleteMatrixTexture(inputTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return avgTime; +}; + +},{"../../src/math/conv_util":17,"../../src/math/webgl/gpgpu_context":24,"../../src/math/webgl/max_pool_gpu":28,"../../src/test_util":35}],10:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_cpu_1 = require("../../src/math/math_cpu"); +var ndarray_1 = require("../../src/math/ndarray"); +var OPS_PER_SMALL_RUN = 10; +exports.BENCHMARK_TEST = function (size) { + if (size > 512) { + return -1; + } + var math = new math_cpu_1.NDArrayMathCPU(); + var a = ndarray_1.NDArray.randUniform([size, size], -1, 1); + var b = ndarray_1.NDArray.randUniform([size, size], -1, 1); + var runs = (size < 192) ? OPS_PER_SMALL_RUN : 1; + var start = performance.now(); + for (var i = 0; i < runs; i++) { + math.matMul(a, b); + } + var end = performance.now(); + return (end - start) / runs; +}; + +},{"../../src/math/math_cpu":20,"../../src/math/ndarray":21}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../../src/math/math"); +var ndarray_1 = require("../../src/math/ndarray"); +var gpgpu_context_1 = require("../../src/math/webgl/gpgpu_context"); +var mulmat_gpu = require("../../src/math/webgl/mulmat_gpu"); +var mulmat_packed_gpu = require("../../src/math/webgl/mulmat_packed_gpu"); +var test_util = require("../../src/test_util"); +var OP_RUNS = 100; +exports.BENCHMARK_TEST = function (size) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var aTexture = gpgpu.createMatrixTexture(size, size); + var bTexture = gpgpu.createMatrixTexture(size, size); + var resultTexture = gpgpu.createMatrixTexture(size, size); + var aArr = new ndarray_1.Array2D([size, size], { texture: aTexture, textureShapeRC: [size, size] }); + var bArr = new ndarray_1.Array2D([size, size], { texture: bTexture, textureShapeRC: [size, size] }); + var resArr = new ndarray_1.Array2D([size, size], { texture: resultTexture, textureShapeRC: [size, size] }); + var program = gpgpu.createProgram(mulmat_gpu.getFragmentShader(aArr, bArr, resArr, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.REGULAR)); + var a = test_util.randomArrayInRange(size * size, -1, 1); + var b = test_util.randomArrayInRange(size * size, -1, 1); + gpgpu.uploadMatrixToTexture(aTexture, size, size, a); + gpgpu.uploadMatrixToTexture(bTexture, size, size, b); + var start = performance.now(); + for (var i = 0; i < OP_RUNS; i++) { + mulmat_gpu.multiplyMatrix(gpgpu, program, aTexture, bTexture, resultTexture, [size, size]); + } + var actual = gpgpu.downloadMatrixFromTexture(resultTexture, size, size); + var avgTime = (performance.now() - start) / OP_RUNS; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + var expected = test_util.cpuMultiplyMatrix(a, size, size, b, size, size); + test_util.expectArraysClose(actual, expected, 0.001); + return avgTime; +}; +exports.BENCHMARK_TEST_PACKED = function (size) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(mulmat_packed_gpu.getFragmentShaderSource(size, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.REGULAR)); + var aTexture = gpgpu.createPackedMatrixTexture(size, size); + var bTexture = gpgpu.createPackedMatrixTexture(size, size); + var resultTexture = gpgpu.createPackedMatrixTexture(size, size); + var a = test_util.randomArrayInRange(size * size, -1, 1); + var b = test_util.randomArrayInRange(size * size, -1, 1); + gpgpu.uploadMatrixToPackedTexture(aTexture, size, size, a); + gpgpu.uploadMatrixToPackedTexture(bTexture, size, size, b); + var start = performance.now(); + for (var i = 0; i < OP_RUNS; i++) { + mulmat_packed_gpu.multiplyMatrixPacked(gpgpu, program, aTexture, bTexture, resultTexture, [size, size]); + } + var actual = gpgpu.downloadMatrixFromPackedTexture(resultTexture, size, size); + var avgTime = (performance.now() - start) / OP_RUNS; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + var expected = test_util.cpuMultiplyMatrix(a, size, size, b, size, size); + test_util.expectArraysClose(actual, expected, 0.001); + return avgTime; +}; + +},{"../../src/math/math":19,"../../src/math/ndarray":21,"../../src/math/webgl/gpgpu_context":24,"../../src/math/webgl/mulmat_gpu":29,"../../src/math/webgl/mulmat_packed_gpu":30,"../../src/test_util":35}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("../../src/math/webgl/tex_util"); +var webgl_util = require("../../src/math/webgl/webgl_util"); +var test_util = require("../../src/test_util"); +var OPS_PER_RUN = 100; +exports.BENCHMARK_ENCODE_UNPACKED = function (size) { + var matrix = test_util.randomArrayInRange(size * size, -1, 1); + var channelsPerTexture = webgl_util.getChannelsPerTexture(); + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + var start = performance.now(); + for (var i = 0; i < OPS_PER_RUN; ++i) { + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + } + var end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; +exports.BENCHMARK_ENCODE_PACKED = function (size) { + var matrix = test_util.randomArrayInRange(size * size, -1, 1); + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(size, size)); + var start = performance.now(); + for (var i = 0; i < OPS_PER_RUN; ++i) { + tex_util.encodeMatrixToPackedRGBA(matrix, size, size, packedRGBA); + } + var end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; +exports.BENCHMARK_DECODE_UNPACKED = function (size) { + var matrix = test_util.randomArrayInRange(size * size, -1, 1); + var channelsPerTexture = webgl_util.getChannelsPerTexture(); + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + var start = performance.now(); + for (var i = 0; i < OPS_PER_RUN; ++i) { + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + } + var end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; +exports.BENCHMARK_DECODE_PACKED = function (size) { + var matrix = test_util.randomArrayInRange(size * size, -1, 1); + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(size, size)); + tex_util.encodeMatrixToPackedRGBA(matrix, size, size, packedRGBA); + var start = performance.now(); + for (var i = 0; i < OPS_PER_RUN; ++i) { + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, size, size, matrix); + } + var end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; + +},{"../../src/math/webgl/tex_util":33,"../../src/math/webgl/webgl_util":34,"../../src/test_util":35}],13:[function(require,module,exports){ +Polymer({ is: 'demo-footer' }); + +},{}],14:[function(require,module,exports){ +Polymer({ is: 'demo-header' }); + +},{}],15:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function PolymerElement(spec) { + return Polymer.Class(spec); +} +exports.PolymerElement = PolymerElement; + +},{}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":36}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":36}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":36,"./concat3d_util":16,"./copy2d_util":18,"./ndarray":21}],20:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":17,"../util":36,"./concat3d_util":16,"./copy2d_util":18,"./math":19,"./ndarray":21}],21:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":36,"./webgl/webgl_util":34}],22:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":17,"./conv_gpu":23}],23:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":17}],24:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":25,"./tex_util":33,"./webgl_util":34}],25:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":33,"./webgl_util":34}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":24}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":17}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":31}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":19,"./shader_compiler":32}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(sharedDimension, aOrientation, bOrientation) { + var sharedDimensionPacked = Math.ceil(sharedDimension / 2); + var aSample = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + 'center, resultUV.t' : + 'resultUV.t, center'; + var bSample = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + 'resultUV.s, center' : + 'center, resultUV.s'; + var aSwizzle = (aOrientation === math_1.MatrixOrientation.REGULAR) ? ['a.xxzz', 'a.yyww'] : + ['a.xxyy', 'a.zzww']; + var bSwizzle = (bOrientation === math_1.MatrixOrientation.REGULAR) ? ['b.xyxy', 'b.zwzw'] : + ['b.xzxz', 'b.ywyw']; + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n const float sharedDimension = " + sharedDimensionPacked + ".0;\n\n vec4 dot2x2ARowBCol() {\n vec4 result = vec4(0, 0, 0, 0);\n for (float i = 0.0; i < sharedDimension; i += 1.0) {\n float center = (i + 0.5) / sharedDimension;\n vec4 a = texture2D(matrixA, vec2(" + aSample + "));\n vec4 b = texture2D(matrixB, vec2(" + bSample + "));\n result +=\n (" + aSwizzle[0] + " * " + bSwizzle[0] + ") + (" + aSwizzle[1] + " * " + bSwizzle[1] + ");\n }\n return result;\n }\n\n void main() {\n gl_FragColor = dot2x2ARowBCol();\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function multiplyMatrixPacked(gpgpu, multiplyProgram, a, b, result, resultShapeRowCol) { + gpgpu.setOutputPackedMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrixPacked = multiplyMatrixPacked; +function uploadMultiplyMatrixPackedDownload(a, aShapeRowCol, b, bShapeRowCol, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var resultNumRows = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + aShapeRowCol[0] : + aShapeRowCol[1]; + var resultNumCols = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + bShapeRowCol[1] : + bShapeRowCol[0]; + var sharedDimension = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + aShapeRowCol[1] : + aShapeRowCol[0]; + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(sharedDimension, aOrientation, bOrientation)); + var aTexture = gpgpu.createPackedMatrixTexture(aShapeRowCol[0], aShapeRowCol[1]); + var bTexture = gpgpu.createPackedMatrixTexture(bShapeRowCol[0], bShapeRowCol[1]); + var resultTexture = gpgpu.createPackedMatrixTexture(resultNumRows, resultNumCols); + gpgpu.uploadMatrixToPackedTexture(aTexture, aShapeRowCol[0], aShapeRowCol[1], a); + gpgpu.uploadMatrixToPackedTexture(bTexture, bShapeRowCol[0], bShapeRowCol[1], b); + multiplyMatrixPacked(gpgpu, program, aTexture, bTexture, resultTexture, [resultNumRows, resultNumCols]); + var result = gpgpu.downloadMatrixFromPackedTexture(resultTexture, resultNumRows, resultNumCols); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadMultiplyMatrixPackedDownload = uploadMultiplyMatrixPackedDownload; + +},{"../math":19,"./gpgpu_context":24}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":17,"./webgl_util":34}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":36}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":36}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function expectArraysClose(actual, expected, epsilon) { + if (actual.length !== expected.length) { + throw new Error('Matrices have different lengths (' + actual.length + ' vs ' + + expected.length + ').'); + } + for (var i = 0; i < expected.length; ++i) { + var a = actual[i]; + var e = expected[i]; + if (isNaN(a) && isNaN(e)) { + continue; + } + if (isNaN(a) || isNaN(e) || Math.abs(a - e) > epsilon) { + var actualStr = 'actual[' + i + '] === ' + a; + var expectedStr = 'expected[' + i + '] === ' + e; + throw new Error('Arrays differ: ' + actualStr + ', ' + expectedStr); + } + } +} +exports.expectArraysClose = expectArraysClose; +function randomArrayInRange(n, minValue, maxValue) { + var v = new Float32Array(n); + var range = maxValue - minValue; + for (var i = 0; i < n; ++i) { + v[i] = (Math.random() * range) + minValue; + } + return v; +} +exports.randomArrayInRange = randomArrayInRange; +function makeIdentity(n) { + var i = new Float32Array(n * n); + for (var j = 0; j < n; ++j) { + i[(j * n) + j] = 1; + } + return i; +} +exports.makeIdentity = makeIdentity; +function setValue(m, mNumRows, mNumCols, v, row, column) { + if (row >= mNumRows) { + throw new Error('row (' + row + ') must be in [0 ' + mNumRows + '].'); + } + if (column >= mNumCols) { + throw new Error('column (' + column + ') must be in [0 ' + mNumCols + '].'); + } + m[(row * mNumCols) + column] = v; +} +exports.setValue = setValue; +function cpuMultiplyMatrix(a, aRow, aCol, b, bRow, bCol) { + var result = new Float32Array(aRow * bCol); + for (var r = 0; r < aRow; ++r) { + for (var c = 0; c < bCol; ++c) { + var d = 0; + for (var k = 0; k < aCol; ++k) { + d += a[(r * aCol) + k] * b[(k * bCol) + c]; + } + result[(r * bCol) + c] = d; + } + } + return result; +} +exports.cpuMultiplyMatrix = cpuMultiplyMatrix; +function cpuDotProduct(a, b) { + if (a.length !== b.length) { + throw new Error('cpuDotProduct: incompatible vectors.'); + } + var d = 0; + for (var i = 0; i < a.length; ++i) { + d += a[i] * b[i]; + } + return d; +} +exports.cpuDotProduct = cpuDotProduct; + +},{}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[7]) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJkZW1vcy9iZW5jaG1hcmtzL2JlbmNobWFyay50cyIsImRlbW9zL2JlbmNobWFya3MvY29udl9ncHVfYmVuY2htYXJrLnRzIiwiZGVtb3MvYmVuY2htYXJrcy9jb252X3RyYW5zcG9zZV9ncHVfYmVuY2htYXJrLnRzIiwiZGVtb3MvYmVuY2htYXJrcy9sb2dzdW1leHBfY3B1X2JlbmNobWFyay50cyIsImRlbW9zL2JlbmNobWFya3MvbG9nc3VtZXhwX2dwdV9iZW5jaG1hcmsudHMiLCJkZW1vcy9iZW5jaG1hcmtzL21hdGgtYmVuY2htYXJrLXJ1bi1ncm91cHMudHMiLCJkZW1vcy9iZW5jaG1hcmtzL21hdGgtYmVuY2htYXJrLnRzIiwiZGVtb3MvYmVuY2htYXJrcy9tYXhfcG9vbF9iYWNrcHJvcF9ncHVfYmVuY2htYXJrLnRzIiwiZGVtb3MvYmVuY2htYXJrcy9tYXhfcG9vbF9ncHVfYmVuY2htYXJrLnRzIiwiZGVtb3MvYmVuY2htYXJrcy9tdWxtYXRfY3B1X2JlbmNobWFyay50cyIsImRlbW9zL2JlbmNobWFya3MvbXVsbWF0X2dwdV9iZW5jaG1hcmsudHMiLCJkZW1vcy9iZW5jaG1hcmtzL3RleF91dGlsX2JlbmNobWFyay50cyIsImRlbW9zL2RlbW8tZm9vdGVyLnRzIiwiZGVtb3MvZGVtby1oZWFkZXIudHMiLCJkZW1vcy9wb2x5bWVyLXNwZWMudHMiLCJzcmMvbWF0aC9jb25jYXQzZF91dGlsLnRzIiwic3JjL21hdGgvY29udl91dGlsLnRzIiwic3JjL21hdGgvY29weTJkX3V0aWwudHMiLCJzcmMvbWF0aC9tYXRoLnRzIiwic3JjL21hdGgvbWF0aF9jcHUudHMiLCJzcmMvbWF0aC9uZGFycmF5LnRzIiwic3JjL21hdGgvd2ViZ2wvY29udl9iYWNrcHJvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb252X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQudHMiLCJzcmMvbWF0aC93ZWJnbC9ncGdwdV91dGlsLnRzIiwic3JjL21hdGgvd2ViZ2wvbG9nc3VtZXhwX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL211bG1hdF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9tdWxtYXRfcGFja2VkX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvc2hhZGVyX2NvbXBpbGVyLnRzIiwic3JjL21hdGgvd2ViZ2wvdGV4X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC93ZWJnbF91dGlsLnRzIiwic3JjL3Rlc3RfdXRpbC50cyIsInNyYy91dGlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7QUMyQkE7SUFLRSxzQkFBWSxJQUFZLEVBQUUsYUFBNEI7UUFDcEQsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7UUFDakIsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUM7UUFDbkMsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUNILG1CQUFDO0FBQUQsQ0FWQSxBQVVDLElBQUE7QUFWWSxvQ0FBWTs7Ozs7QUNaekIsb0RBQXNEO0FBQ3RELHdEQUEwRDtBQUMxRCxvRUFBZ0U7QUFDaEUsK0NBQWlEO0FBSWpELElBQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQztBQUVQLFFBQUEsY0FBYyxHQUFrQixVQUFDLElBQVk7SUFDeEQsSUFBTSxhQUFhLEdBQTZCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNoRSxJQUFNLFdBQVcsR0FBRyxDQUFDLENBQUM7SUFDdEIsSUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDO0lBQ3JCLElBQU0sTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNqQixJQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsaUJBQWlCLENBQUMsYUFBYSxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM5RSxJQUFNLGNBQWMsR0FDaEIsU0FBUyxDQUFDLG9CQUFvQixDQUMxQixhQUFhLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFaEUsSUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3ZFLElBQU0sZ0JBQWdCLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQ3pFLElBQU0saUJBQWlCLEdBQUcsU0FBUyxDQUFDLHNCQUFzQixDQUN0RCxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzlDLElBQU0sZ0JBQWdCLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBRXRFLElBQU0sT0FBTyxHQUFHLElBQUksQ0FBQztJQUNyQixJQUFNLEtBQUssR0FBRyxJQUFJLDRCQUFZLEVBQUUsQ0FBQztJQUNqQyxJQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FDaEUsYUFBYSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBRXRFLElBQU0sWUFBWSxHQUNkLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdEUsSUFBTSxjQUFjLEdBQ2hCLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFFLElBQU0sYUFBYSxHQUNmLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3hFLElBQU0sYUFBYSxHQUNmLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXhFLElBQU0sU0FBUyxHQUFHLFNBQVMsQ0FBQyxrQkFBa0IsQ0FDMUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNwRCxJQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQzVDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxHQUFHLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3hELElBQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQyxrQkFBa0IsQ0FDM0MsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEdBQUcsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFdEQsS0FBSyxDQUFDLHFCQUFxQixDQUN2QixZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNyRSxLQUFLLENBQUMscUJBQXFCLENBQ3ZCLGNBQWMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsRUFBRSxXQUFXLENBQUMsQ0FBQztJQUM3RSxLQUFLLENBQUMscUJBQXFCLENBQ3ZCLGFBQWEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUV6RSxJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNqQyxRQUFRLENBQUMsUUFBUSxDQUNiLEtBQUssRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLGNBQWMsRUFBRSxhQUFhLEVBQzNELGFBQWEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFRCxLQUFLLENBQUMseUJBQXlCLENBQzNCLGFBQWEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdELElBQU0sR0FBRyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUU5QixJQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUM7SUFFeEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3hDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUMxQyxLQUFLLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDekMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3pDLEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBRWhCLE1BQU0sQ0FBQyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDOzs7OztBQzFFRixvREFBc0Q7QUFDdEQsMEVBQTRFO0FBQzVFLG9FQUFnRTtBQUNoRSwrQ0FBaUQ7QUFJakQsSUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDO0FBRVAsUUFBQSxjQUFjLEdBQWtCLFVBQUMsSUFBWTtJQUN4RCxJQUFNLFNBQVMsR0FBNkIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzVELElBQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztJQUMxQixJQUFNLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDckIsSUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFDO0lBQ3JCLElBQU0sT0FBTyxHQUFHLENBQUMsQ0FBQztJQUVsQixJQUFNLEtBQUssR0FBRyxJQUFJLDRCQUFZLEVBQUUsQ0FBQztJQUNqQyxLQUFLLENBQUMsOEJBQThCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0MsSUFBTSxjQUFjLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLElBQU0sR0FBRyxHQUFHLGlCQUFpQixDQUFDLG9DQUFvQyxDQUM5RCxTQUFTLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3RFLElBQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUM7SUFHekMsSUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQy9ELElBQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdkUsSUFBTSxLQUFLLEdBQ1AsU0FBUyxDQUFDLGtCQUFrQixDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDekUsS0FBSyxDQUFDLHFCQUFxQixDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBR3pFLElBQU0sV0FBVyxHQUFHLFNBQVMsQ0FBQyxzQkFBc0IsQ0FDaEQsY0FBYyxFQUFFLGVBQWUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNoRCxJQUFNLEtBQUssR0FDUCxTQUFTLENBQUMsa0JBQWtCLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN6RSxJQUFNLElBQUksR0FBRyxLQUFLLENBQUMsbUJBQW1CLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZFLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUd6RSxJQUFNLFNBQVMsR0FDWCxTQUFTLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDekUsSUFBTSxHQUFHLEdBQUcsU0FBUyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUM7SUFDcEMsSUFBTSxjQUFjLEdBQUcsU0FBUyxDQUFDLG9CQUFvQixDQUNqRCxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsZUFBZSxDQUFDLEVBQUUsU0FBUyxFQUFFLGNBQWMsRUFDeEUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBRVosSUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQ3BFLElBQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFNUUsSUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDakMsaUJBQWlCLENBQUMsYUFBYSxDQUMzQixLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxXQUFXLENBQUMsQ0FBQztJQUNoRSxDQUFDO0lBRUQsSUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLHlCQUF5QixDQUNyQyxTQUFTLEVBQUUsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRS9DLElBQU0sR0FBRyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUU5QixJQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUM7SUFFeEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3JDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxLQUFLLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDaEMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QixLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7SUFFaEIsTUFBTSxDQUFDLE9BQU8sQ0FBQztBQUNqQixDQUFDLENBQUM7Ozs7O0FDckVGLG9EQUF1RDtBQUN2RCxrREFBd0Q7QUFJeEQsSUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDO0FBRVYsUUFBQSxjQUFjLEdBQWtCLFVBQUMsSUFBWTtJQUN4RCxJQUFNLElBQUksR0FBRyxJQUFJLHlCQUFjLEVBQUUsQ0FBQztJQUNsQyxJQUFNLENBQUMsR0FBRyxpQkFBTyxDQUFDLFdBQVcsQ0FBVSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUM1RCxJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxXQUFXLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNyQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BCLENBQUM7SUFDRCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLFdBQVcsQ0FBQztBQUNyQyxDQUFDLENBQUM7Ozs7O0FDaEJGLG9FQUFnRTtBQUNoRSxrRUFBb0U7QUFDcEUsK0NBQWlEO0FBSWpELElBQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQztBQUVQLFFBQUEsY0FBYyxHQUFrQixVQUFDLElBQVk7SUFDeEQsSUFBTSxLQUFLLEdBQUcsSUFBSSw0QkFBWSxFQUFFLENBQUM7SUFFakMsSUFBTSxPQUFPLEdBQ1QsS0FBSyxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsdUJBQXVCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFFM0UsSUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLG1CQUFtQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2RCxJQUFNLGFBQWEsR0FBRyxLQUFLLENBQUMsbUJBQW1CLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBRTVELElBQU0sQ0FBQyxHQUFHLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzNELEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztJQUVyRCxJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNqQyxhQUFhLENBQUMsU0FBUyxDQUNuQixLQUFLLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLGFBQWEsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFRCxLQUFLLENBQUMseUJBQXlCLENBQUMsYUFBYSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMzRCxJQUFNLE9BQU8sR0FBRyxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUM7SUFFdEQsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN6QyxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzdCLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUVoQixNQUFNLENBQUMsT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQzs7Ozs7QUNuQ0YseUNBQTREO0FBQzVELHlEQUEyRDtBQUMzRCw2RUFBK0U7QUFDL0UsbUVBQXFFO0FBQ3JFLG1FQUFxRTtBQUNyRSxtRkFBcUY7QUFDckYsaUVBQW1FO0FBQ25FLDZEQUErRDtBQUMvRCw2REFBK0Q7QUFDL0QseURBQTJEO0FBRTlDLFFBQUEsb0JBQW9CLEdBQXdCO0lBQ3ZEO1FBQ0UsSUFBSSxFQUFFLGtEQUFrRDtRQUN4RCxHQUFHLEVBQUUsQ0FBQztRQUNOLEdBQUcsRUFBRSxJQUFJO1FBQ1QsUUFBUSxFQUFFLEVBQUU7UUFDWix3QkFBd0IsRUFBRSxVQUFDLElBQVksSUFBSyxPQUFBLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxFQUFqQixDQUFpQjtRQUM3RCxhQUFhLEVBQUU7WUFDYixJQUFJLHdCQUFZLENBQ1osaUJBQWlCLEVBQUUsa0JBQWtCLENBQUMseUJBQXlCLENBQUM7WUFDcEUsSUFBSSx3QkFBWSxDQUNaLGVBQWUsRUFBRSxrQkFBa0IsQ0FBQyx1QkFBdUIsQ0FBQztZQUNoRSxJQUFJLHdCQUFZLENBQ1osaUJBQWlCLEVBQUUsa0JBQWtCLENBQUMseUJBQXlCLENBQUM7WUFDcEUsSUFBSSx3QkFBWSxDQUNaLGVBQWUsRUFBRSxrQkFBa0IsQ0FBQyx1QkFBdUIsQ0FBQztTQUNqRTtLQUNGO0lBQ0Q7UUFDRSxJQUFJLEVBQUUsb0NBQW9DO1FBQzFDLEdBQUcsRUFBRSxDQUFDO1FBQ04sR0FBRyxFQUFFLElBQUk7UUFDVCxRQUFRLEVBQUUsRUFBRTtRQUNaLHdCQUF3QixFQUFFLFVBQUMsSUFBWSxJQUFLLE9BQUEsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQWpCLENBQWlCO1FBQzdELGFBQWEsRUFBRTtZQUNiLElBQUksd0JBQVksQ0FBQyxZQUFZLEVBQUUsb0JBQW9CLENBQUMsY0FBYyxDQUFDO1lBQ25FLElBQUksd0JBQVksQ0FDWixtQkFBbUIsRUFBRSxvQkFBb0IsQ0FBQyxxQkFBcUIsQ0FBQztZQUNwRSxJQUFJLHdCQUFZLENBQUMsWUFBWSxFQUFFLG9CQUFvQixDQUFDLGNBQWMsQ0FBQztTQUNwRTtLQUNGO0lBQ0Q7UUFDRSxJQUFJLEVBQUUsd0JBQXdCO1FBQzlCLEdBQUcsRUFBRSxDQUFDO1FBQ04sR0FBRyxFQUFFLElBQUk7UUFDVCxRQUFRLEVBQUUsRUFBRTtRQUNaLHdCQUF3QixFQUFFLFVBQUMsSUFBWSxJQUFLLE9BQUEsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQWpCLENBQWlCO1FBQzdELGFBQWEsRUFBRTtZQUNiLElBQUksd0JBQVksQ0FBQyxlQUFlLEVBQUUsdUJBQXVCLENBQUMsY0FBYyxDQUFDO1lBQ3pFLElBQUksd0JBQVksQ0FBQyxlQUFlLEVBQUUsdUJBQXVCLENBQUMsY0FBYyxDQUFDO1NBQzFFO0tBQ0Y7SUFDRDtRQUNFLElBQUksRUFBRSxtQkFBbUI7UUFDekIsR0FBRyxFQUFFLENBQUM7UUFDTixHQUFHLEVBQUUsSUFBSTtRQUNULFFBQVEsRUFBRSxFQUFFO1FBQ1osd0JBQXdCLEVBQUUsVUFBQyxJQUFZLElBQUssT0FBQSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsRUFBakIsQ0FBaUI7UUFDN0QsYUFBYSxFQUFFLENBQUMsSUFBSSx3QkFBWSxDQUM1Qix1QkFBdUIsRUFBRSxrQkFBa0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztLQUNqRTtJQUNEO1FBQ0UsSUFBSSxFQUFFLDhCQUE4QjtRQUNwQyxHQUFHLEVBQUUsQ0FBQztRQUNOLEdBQUcsRUFBRSxJQUFJO1FBQ1QsUUFBUSxFQUFFLEVBQUU7UUFDWix3QkFBd0IsRUFBRSxVQUFDLElBQVksSUFBSyxPQUFBLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxFQUFqQixDQUFpQjtRQUM3RCxhQUFhLEVBQUUsQ0FBQyxJQUFJLHdCQUFZLENBQzVCLHVCQUF1QixFQUFFLDRCQUE0QixDQUFDLGNBQWMsQ0FBQyxDQUFDO0tBQzNFO0lBQ0Q7UUFDRSxJQUFJLEVBQUUsZ0JBQWdCO1FBQ3RCLEdBQUcsRUFBRSxDQUFDO1FBQ04sR0FBRyxFQUFFLElBQUk7UUFDVCxRQUFRLEVBQUUsRUFBRTtRQUNaLHdCQUF3QixFQUFFLFVBQUMsSUFBWSxJQUFLLE9BQUEsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQWpCLENBQWlCO1FBQzdELGFBQWEsRUFBRSxDQUFDLElBQUksd0JBQVksQ0FDNUIsdUJBQXVCLEVBQ3ZCLHNCQUFzQixDQUFDLHVCQUF1QixDQUFDLENBQUM7S0FDckQ7SUFDRDtRQUNFLElBQUksRUFBRSwwQkFBMEI7UUFDaEMsR0FBRyxFQUFFLENBQUM7UUFDTixHQUFHLEVBQUUsSUFBSTtRQUNULFFBQVEsRUFBRSxFQUFFO1FBQ1osd0JBQXdCLEVBQUUsVUFBQyxJQUFZLElBQUssT0FBQSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsRUFBakIsQ0FBaUI7UUFDN0QsYUFBYSxFQUFFLENBQUMsSUFBSSx3QkFBWSxDQUM1Qix1QkFBdUIsRUFDdkIsc0JBQXNCLENBQUMsNkJBQTZCLENBQUMsQ0FBQztLQUMzRDtJQUNEO1FBQ0UsSUFBSSxFQUFFLHlCQUF5QjtRQUMvQixHQUFHLEVBQUUsQ0FBQztRQUNOLEdBQUcsRUFBRSxJQUFJO1FBQ1QsUUFBUSxFQUFFLEVBQUU7UUFDWix3QkFBd0IsRUFBRSxVQUFDLElBQVksSUFBSyxPQUFBLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxFQUFqQixDQUFpQjtRQUM3RCxhQUFhLEVBQUUsQ0FBQyxJQUFJLHdCQUFZLENBQzVCLHVCQUF1QixFQUN2QiwrQkFBK0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztLQUNyRDtDQUNGLENBQUM7Ozs7Ozs7Ozs7Ozs7OztBQ3JHRiwwQkFBd0I7QUFDeEIsMEJBQXdCO0FBR3hCLGdEQUFtRTtBQUduRSx5RUFBaUU7QUFHdEQsUUFBQSxvQkFBb0IsR0FBRyw2QkFBYyxDQUM1QyxFQUFDLEVBQUUsRUFBRSxnQkFBZ0IsRUFBRSxVQUFVLEVBQUUsRUFBQyxzQkFBc0IsRUFBRSxLQUFLLEVBQUMsRUFBQyxDQUFDLENBQUM7QUFFekU7SUFBbUMsaUNBQW9CO0lBQXZEOztJQW1NQSxDQUFDO0lBOUxDLDZCQUFLLEdBQUw7UUFBQSxpQkF1QkM7UUFyQkMsSUFBTSxzQkFBc0IsR0FBYSxFQUFFLENBQUM7UUFDNUMsSUFBSSxDQUFDLFlBQVksR0FBRyxFQUFFLENBQUM7UUFDdkIsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxnREFBb0IsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNyRCxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsZ0RBQW9CLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDaEMsQ0FBQztRQUNELElBQUksQ0FBQyxzQkFBc0IsR0FBRyxzQkFBc0IsQ0FBQztRQUdyRCxVQUFVLENBQUM7WUFDVCxJQUFNLFVBQVUsR0FBRyxLQUFJLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDdEQsSUFBTSxXQUFXLEdBQUcsS0FBSSxDQUFDLGdCQUFnQixDQUFDLFdBQVcsQ0FBQyxDQUFDO29DQUM5QyxDQUFDO2dCQUNSLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLEVBQUU7b0JBQ3RDLEtBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUIsQ0FBQyxDQUFDLENBQUM7Z0JBQ0gsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRTtvQkFDdkMsS0FBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUM7Z0JBQzlCLENBQUMsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQVBELEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUU7d0JBQWpDLENBQUM7YUFPVDtRQUNILENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNSLENBQUM7SUFFTyx5Q0FBaUIsR0FBekIsVUFBMEIsc0JBQThCO1FBQ3RELElBQU0saUJBQWlCLEdBQUcsZ0RBQW9CLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUV2RSxJQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLENBQUMsc0JBQXNCLENBQ25ELENBQUM7UUFDdEIsSUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQTZCLENBQUM7UUFFcEUsSUFBTSxRQUFRLEdBQW9CLEVBQUUsQ0FBQztRQUNyQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNoRSxJQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxDQUFDLEdBQUcsaUJBQWlCLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3pFLFFBQVEsQ0FBQyxJQUFJLENBQUM7Z0JBQ1osSUFBSSxFQUFFLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTO2dCQUNsRCxJQUFJLEVBQUUsS0FBSztnQkFDWCxLQUFLLEVBQUUsaUJBQWlCLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUk7Z0JBQzlDLFdBQVcsRUFBRSxNQUFNLEdBQUcsR0FBRyxHQUFHLGNBQWM7Z0JBQzFDLGVBQWUsRUFBRSxNQUFNLEdBQUcsR0FBRyxHQUFHLGNBQWM7Z0JBQzlDLFdBQVcsRUFBRSxDQUFDO2dCQUNkLGNBQWMsRUFBRSxDQUFDO2dCQUNqQixXQUFXLEVBQUUsQ0FBQztnQkFDZCxXQUFXLEVBQUUsQ0FBQzthQUNmLENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxJQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUU7WUFDL0IsSUFBSSxFQUFFLE1BQU07WUFDWixJQUFJLEVBQUUsRUFBQyxRQUFRLFVBQUEsRUFBQztZQUNoQixPQUFPLEVBQUU7Z0JBQ1AsU0FBUyxFQUFFLEVBQUMsUUFBUSxFQUFFLENBQUMsRUFBQztnQkFDeEIsVUFBVSxFQUFFLEtBQUs7Z0JBQ2pCLE1BQU0sRUFBRTtvQkFDTixLQUFLLEVBQUUsQ0FBQzs0QkFDTixJQUFJLEVBQUUsUUFBUTs0QkFDZCxRQUFRLEVBQUUsUUFBUTs0QkFDbEIsS0FBSyxFQUFFO2dDQUNMLEdBQUcsRUFBRSxpQkFBaUIsQ0FBQyxHQUFHO2dDQUMxQixHQUFHLEVBQUUsaUJBQWlCLENBQUMsR0FBRztnQ0FDMUIsUUFBUSxFQUFFLGlCQUFpQixDQUFDLFFBQVE7Z0NBQ3BDLFFBQVEsRUFBRSxVQUFDLEtBQWE7b0NBQ3RCLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyx3QkFBd0IsSUFBSSxJQUFJO3dDQUNyRCxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDLEtBQUssQ0FBQzt3Q0FDbEQsQ0FBQyxLQUFLLENBQUM7Z0NBQ2IsQ0FBQzs2QkFFSzt5QkFDVCxDQUFDO29CQUNGLEtBQUssRUFBRSxDQUFDOzRCQUNOLEtBQUssRUFBRTtnQ0FDTCxRQUFRLEVBQUUsVUFBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU07b0NBQzdCLE1BQU0sQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO2dDQUN0QixDQUFDOzZCQUNGO3lCQUNGLENBQUM7aUJBQ0g7Z0JBQ0QsUUFBUSxFQUFFLEVBQUMsSUFBSSxFQUFFLE9BQU8sRUFBQztnQkFDekIsS0FBSyxFQUFFLEVBQUMsSUFBSSxFQUFFLGlCQUFpQixDQUFDLElBQUksRUFBQzthQUN0QztTQUNGLENBQUMsQ0FBQztRQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztRQUU5QixJQUFNLFVBQVUsR0FDWixJQUFJLENBQUMsZ0JBQWdCLENBQUMsY0FBYyxDQUFDLENBQUMsc0JBQXNCLENBQ2pELENBQUM7UUFDaEIsVUFBVSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBRW5DLElBQU0sZUFBZSxHQUNqQixJQUFJLENBQUMsZ0JBQWdCLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxzQkFBc0IsQ0FDdkQsQ0FBQztRQUNoQixlQUFlLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQztRQUMvQixlQUFlLENBQUMsS0FBSyxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFHdkMsSUFBTSxPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6QixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNoRSxPQUFPLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsZUFBZSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUU5RCxJQUFJLENBQUMsaUJBQWlCLENBQ2xCLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxzQkFBc0IsRUFDaEQsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVPLDBDQUFrQixHQUExQixVQUEyQixNQUFnQjtRQUN6QyxJQUFNLG1CQUFtQixHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDMUQsbUJBQW1CLENBQUMsU0FBUyxHQUFHLGdDQUFnQyxDQUFDO1FBRWpFLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3ZDLElBQU0sb0JBQW9CLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUMzRCxvQkFBb0IsQ0FBQyxTQUFTLEdBQUcsaUNBQWlDLENBQUM7WUFDbkUsb0JBQW9CLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUMzQyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsTUFBTSxDQUFDLG1CQUFtQixDQUFDO0lBQzdCLENBQUM7SUFFTyx5Q0FBaUIsR0FBekIsVUFDSSxLQUFZLEVBQUUsaUJBQW9DLEVBQ2xELHNCQUE4QixFQUFFLElBQVk7UUFGaEQsaUJBcUVDO1FBbEVDLElBQU0sZUFBZSxHQUNqQixJQUFJLENBQUMsZ0JBQWdCLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxzQkFBc0IsQ0FDdkQsQ0FBQztRQUNoQixFQUFFLENBQUMsQ0FBQyxJQUFJLEdBQUcsaUJBQWlCLENBQUMsR0FBRztZQUM1QixJQUFJLENBQUMsWUFBWSxDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzlDLElBQUksQ0FBQyxZQUFZLENBQUMsc0JBQXNCLENBQUMsR0FBRyxLQUFLLENBQUM7WUFFbEQsZUFBZSxDQUFDLEtBQUssQ0FBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBRW5DLElBQU0sTUFBTSxHQUNSLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxXQUFXLENBQUMsQ0FBQyxzQkFBc0IsQ0FDeEMsQ0FBQztZQUN0QixNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7WUFDL0IsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBRWYsSUFBTSxVQUFVLEdBQ1osSUFBSSxDQUFDLGdCQUFnQixDQUFDLGNBQWMsQ0FBQyxDQUFDLHNCQUFzQixDQUNqRCxDQUFDO1lBQ2hCLFVBQVUsQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztZQUVsQyxNQUFNLENBQUM7UUFDVCxDQUFDO1FBRUQsSUFBTSxtQkFBbUIsR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzFELG1CQUFtQixDQUFDLFNBQVMsR0FBRyxnQ0FBZ0MsQ0FBQztRQUVqRSxJQUFNLFNBQVMsR0FBYSxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUN4QyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGlCQUFpQixDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNoRSxJQUFNLFlBQVksR0FBRyxpQkFBaUIsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEQsSUFBTSxhQUFhLEdBQUcsWUFBWSxDQUFDLGFBQWEsQ0FBQztZQUVqRCxJQUFNLElBQUksR0FBRyxpQkFBaUIsQ0FBQyx3QkFBd0IsSUFBSSxJQUFJO2dCQUMzRCxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxJQUFJLENBQUM7Z0JBQ2hELElBQUksQ0FBQztZQUVULElBQUksWUFBWSxTQUFRLENBQUM7WUFDekIsSUFBSSxTQUFTLFNBQVEsQ0FBQztZQUN0QixJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7WUFDYixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFFbkIsSUFBSSxDQUFDO2dCQUNILElBQUksR0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzNCLFlBQVksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDdEMsU0FBUyxHQUFHLFlBQVksQ0FBQztZQUMzQixDQUFDO1lBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDWCxPQUFPLEdBQUcsS0FBSyxDQUFDO2dCQUNoQixZQUFZLEdBQUcsT0FBTyxDQUFDO2dCQUN2QixTQUFTLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQztZQUN4QixDQUFDO1lBRUQsRUFBRSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ2QsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztvQkFDWixZQUFZLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBQyxDQUFDLENBQUM7Z0JBQ2xELENBQUM7Z0JBQ0QsU0FBUyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUMvQixDQUFDO1lBQ0QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsSUFBSSxHQUFHLEdBQUcsR0FBRyxJQUFJLEdBQUcsS0FBSyxHQUFHLFNBQVMsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFDRCxlQUFlLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBRWhFLElBQUksSUFBSSxpQkFBaUIsQ0FBQyxRQUFRLENBQUM7UUFFbkMsVUFBVSxDQUNOLGNBQU0sT0FBQSxLQUFJLENBQUMsaUJBQWlCLENBQ3hCLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxzQkFBc0IsRUFBRSxJQUFJLENBQUMsRUFEckQsQ0FDcUQsRUFDM0QsR0FBRyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBQ0gsb0JBQUM7QUFBRCxDQW5NQSxBQW1NQyxDQW5Na0MsNEJBQW9CLEdBbU10RDtBQW5NWSxzQ0FBYTtBQW9NMUIsUUFBUSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxhQUFhLENBQUMsQ0FBQzs7Ozs7QUNqTnBFLG9EQUFzRDtBQUN0RCxvRUFBZ0U7QUFDaEUsa0ZBQW9GO0FBQ3BGLCtDQUFpRDtBQUNqRCxxQ0FBdUM7QUFJdkMsSUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDO0FBRVAsUUFBQSxjQUFjLEdBQWtCLFVBQUMsSUFBWTtJQUN4RCxJQUFNLFVBQVUsR0FBNkIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdELElBQU0sV0FBVyxHQUFHLENBQUMsQ0FBQztJQUN0QixJQUFNLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDckIsSUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLElBQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxVQUFVLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzNFLElBQU0sY0FBYyxHQUNoQixTQUFTLENBQUMsb0JBQW9CLENBQzFCLFVBQVUsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUU3RCxJQUFNLFlBQVksR0FBRyxTQUFTLENBQUMscUJBQXFCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDakUsSUFBTSxnQkFBZ0IsR0FBRyxTQUFTLENBQUMscUJBQXFCLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFekUsSUFBTSxLQUFLLEdBQUcsSUFBSSw0QkFBWSxFQUFFLENBQUM7SUFDakMsSUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FDL0IscUJBQXFCLENBQUMsZ0NBQWdDLENBQ2xELFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFFakQsSUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5RSxJQUFNLG1CQUFtQixHQUNyQixLQUFLLENBQUMsbUJBQW1CLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLElBQU0sYUFBYSxHQUNmLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRXhFLElBQU0sTUFBTSxHQUNSLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEdBQUcsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzNFLElBQU0sZ0JBQWdCLEdBQUcsSUFBSSxZQUFZLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0lBQzFFLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDakQsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsU0FBUyxHQUFHLFNBQVMsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFFRCxLQUFLLENBQUMscUJBQXFCLENBQ3ZCLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3pELEtBQUssQ0FBQyxxQkFBcUIsQ0FDdkIsbUJBQW1CLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO0lBRTdFLElBQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ2pDLHFCQUFxQixDQUFDLGVBQWUsQ0FDakMsS0FBSyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsbUJBQW1CLEVBQUUsYUFBYSxFQUM3RCxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3hCLENBQUM7SUFFRCxLQUFLLENBQUMseUJBQXlCLENBQzNCLGFBQWEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdELElBQU0sR0FBRyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUU5QixJQUFNLE9BQU8sR0FBRyxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUM7SUFFeEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3JDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0lBQy9DLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN6QyxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzdCLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUVoQixNQUFNLENBQUMsT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQzs7Ozs7QUNsRUYsb0RBQXNEO0FBQ3RELG9FQUFnRTtBQUNoRSxnRUFBa0U7QUFDbEUsK0NBQWlEO0FBSWpELElBQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQztBQUVQLFFBQUEsdUJBQXVCLEdBQWtCLFVBQUMsSUFBWTtJQUNqRSxJQUFNLGFBQWEsR0FBNkIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLElBQU0sV0FBVyxHQUFHLENBQUMsQ0FBQztJQUN0QixJQUFNLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDckIsSUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLElBQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzlFLElBQU0sY0FBYyxHQUNoQixTQUFTLENBQUMsb0JBQW9CLENBQzFCLGFBQWEsRUFBRSxTQUFTLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUVoRSxJQUFNLGVBQWUsR0FBRyxTQUFTLENBQUMscUJBQXFCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDdkUsSUFBTSxnQkFBZ0IsR0FBRyxTQUFTLENBQUMscUJBQXFCLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFekUsSUFBTSxLQUFLLEdBQUcsSUFBSSw0QkFBWSxFQUFFLENBQUM7SUFDakMsSUFBTSxPQUFPLEdBQ1QsS0FBSyxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsOEJBQThCLENBQzNELGFBQWEsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFFcEQsSUFBTSxZQUFZLEdBQ2QsS0FBSyxDQUFDLG1CQUFtQixDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxlQUFlLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RSxJQUFNLGFBQWEsR0FDZixLQUFLLENBQUMsbUJBQW1CLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUV4RSxJQUFNLFNBQVMsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQzFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsR0FBRyxlQUFlLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFFcEQsS0FBSyxDQUFDLHFCQUFxQixDQUN2QixZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUVyRSxJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNqQyxZQUFZLENBQUMsYUFBYSxDQUN0QixLQUFLLEVBQUUsT0FBTyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRUQsS0FBSyxDQUFDLHlCQUF5QixDQUMzQixhQUFhLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFFOUIsSUFBTSxPQUFPLEdBQUcsQ0FBQyxHQUFHLEdBQUcsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDO0lBRXhDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN4QyxLQUFLLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDekMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM3QixLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7SUFFaEIsTUFBTSxDQUFDLE9BQU8sQ0FBQztBQUNqQixDQUFDLENBQUM7QUFFVyxRQUFBLDZCQUE2QixHQUFrQixVQUFDLElBQVk7SUFDdkUsSUFBTSxhQUFhLEdBQTZCLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNoRSxJQUFNLFdBQVcsR0FBRyxDQUFDLENBQUM7SUFDdEIsSUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFDO0lBQ3JCLElBQU0sTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNqQixJQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsaUJBQWlCLENBQUMsYUFBYSxFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM5RSxJQUFNLGNBQWMsR0FDaEIsU0FBUyxDQUFDLG9CQUFvQixDQUMxQixhQUFhLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFFaEUsSUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3ZFLElBQU0sZ0JBQWdCLEdBQUcsU0FBUyxDQUFDLHFCQUFxQixDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBRXpFLElBQU0sS0FBSyxHQUFHLElBQUksNEJBQVksRUFBRSxDQUFDO0lBQ2pDLElBQU0sT0FBTyxHQUNULEtBQUssQ0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDLHVDQUF1QyxDQUNwRSxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBRXBELElBQU0sWUFBWSxHQUNkLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdEUsSUFBTSxhQUFhLEdBQ2YsS0FBSyxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxFQUFFLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFeEUsSUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLGtCQUFrQixDQUMxQyxlQUFlLENBQUMsQ0FBQyxDQUFDLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRXBELEtBQUssQ0FBQyxxQkFBcUIsQ0FDdkIsWUFBWSxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUMsRUFBRSxlQUFlLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFFckUsSUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDakMsWUFBWSxDQUFDLGFBQWEsQ0FDdEIsS0FBSyxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLGdCQUFnQixDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVELEtBQUssQ0FBQyx5QkFBeUIsQ0FDM0IsYUFBYSxFQUFFLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxFQUFFLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDN0QsSUFBTSxHQUFHLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBRTlCLElBQU0sT0FBTyxHQUFHLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQztJQUV4QyxLQUFLLENBQUMsbUJBQW1CLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDeEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3pDLEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBRWhCLE1BQU0sQ0FBQyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDOzs7OztBQ3pHRixvREFBdUQ7QUFDdkQsa0RBQXdEO0FBSXhELElBQU0saUJBQWlCLEdBQUcsRUFBRSxDQUFDO0FBRWhCLFFBQUEsY0FBYyxHQUFrQixVQUFDLElBQVk7SUFDeEQsRUFBRSxDQUFDLENBQUMsSUFBSSxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDWixDQUFDO0lBQ0QsSUFBTSxJQUFJLEdBQUcsSUFBSSx5QkFBYyxFQUFFLENBQUM7SUFDbEMsSUFBTSxDQUFDLEdBQUcsaUJBQU8sQ0FBQyxXQUFXLENBQVUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDNUQsSUFBTSxDQUFDLEdBQUcsaUJBQU8sQ0FBQyxXQUFXLENBQVUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDNUQsSUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLEdBQUcsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDO0lBQ2xELElBQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3BCLENBQUM7SUFDRCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQztBQUM5QixDQUFDLENBQUM7Ozs7O0FDckJGLDRDQUFzRDtBQUN0RCxrREFBK0M7QUFDL0Msb0VBQWdFO0FBQ2hFLDREQUE4RDtBQUM5RCwwRUFBNEU7QUFDNUUsK0NBQWlEO0FBSWpELElBQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQztBQUVQLFFBQUEsY0FBYyxHQUFrQixVQUFDLElBQVk7SUFDeEQsSUFBTSxLQUFLLEdBQUcsSUFBSSw0QkFBWSxFQUFFLENBQUM7SUFDakMsSUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLG1CQUFtQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2RCxJQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsbUJBQW1CLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3ZELElBQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFFNUQsSUFBTSxJQUFJLEdBQUcsSUFBSSxpQkFBTyxDQUNwQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsY0FBYyxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFDLENBQUMsQ0FBQztJQUNyRSxJQUFNLElBQUksR0FBRyxJQUFJLGlCQUFPLENBQ3BCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxFQUFFLEVBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUMsQ0FBQyxDQUFDO0lBQ3JFLElBQU0sTUFBTSxHQUFHLElBQUksaUJBQU8sQ0FDdEIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBQyxPQUFPLEVBQUUsYUFBYSxFQUFFLGNBQWMsRUFBRSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsRUFBQyxDQUFDLENBQUM7SUFDMUUsSUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsaUJBQWlCLENBQzVELElBQUksRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLHdCQUFpQixDQUFDLE9BQU8sRUFDN0Msd0JBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUVoQyxJQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQUMsSUFBSSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzRCxJQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQUMsSUFBSSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzRCxLQUFLLENBQUMscUJBQXFCLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDckQsS0FBSyxDQUFDLHFCQUFxQixDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRXJELElBQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ2pDLFVBQVUsQ0FBQyxjQUFjLENBQ3JCLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQUUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBRUQsSUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLHlCQUF5QixDQUFDLGFBQWEsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDMUUsSUFBTSxPQUFPLEdBQUcsQ0FBQyxXQUFXLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDO0lBRXRELEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNwQyxLQUFLLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDcEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3pDLEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0IsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBRWhCLElBQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzNFLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3JELE1BQU0sQ0FBQyxPQUFPLENBQUM7QUFDakIsQ0FBQyxDQUFDO0FBRVcsUUFBQSxxQkFBcUIsR0FBa0IsVUFBQyxJQUFZO0lBQy9ELElBQU0sS0FBSyxHQUFHLElBQUksNEJBQVksRUFBRSxDQUFDO0lBQ2pDLElBQU0sT0FBTyxHQUNULEtBQUssQ0FBQyxhQUFhLENBQUMsaUJBQWlCLENBQUMsdUJBQXVCLENBQ3pELElBQUksRUFBRSx3QkFBaUIsQ0FBQyxPQUFPLEVBQUUsd0JBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUVyRSxJQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMseUJBQXlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzdELElBQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDN0QsSUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLHlCQUF5QixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUVsRSxJQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQUMsSUFBSSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzRCxJQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsa0JBQWtCLENBQUMsSUFBSSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMzRCxLQUFLLENBQUMsMkJBQTJCLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDM0QsS0FBSyxDQUFDLDJCQUEyQixDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBRTNELElBQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ2pDLGlCQUFpQixDQUFDLG9CQUFvQixDQUNsQyxLQUFLLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsYUFBYSxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUVELElBQU0sTUFBTSxHQUNSLEtBQUssQ0FBQywrQkFBK0IsQ0FBQyxhQUFhLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3JFLElBQU0sT0FBTyxHQUFHLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQztJQUV0RCxLQUFLLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDcEMsS0FBSyxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3BDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN6QyxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzdCLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUVoQixJQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMzRSxTQUFTLENBQUMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNyRCxNQUFNLENBQUMsT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQzs7Ozs7QUNyRkYsd0RBQTBEO0FBQzFELDREQUE4RDtBQUM5RCwrQ0FBaUQ7QUFJakQsSUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDO0FBRVgsUUFBQSx5QkFBeUIsR0FBa0IsVUFBQyxJQUFZO0lBQ25FLElBQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLElBQU0sa0JBQWtCLEdBQUcsVUFBVSxDQUFDLHFCQUFxQixFQUFFLENBQUM7SUFDOUQsSUFBTSxhQUFhLEdBQ2YsSUFBSSxZQUFZLENBQUMsUUFBUSxDQUFDLGtDQUFrQyxDQUN4RCxNQUFNLENBQUMsTUFBTSxFQUFFLGtCQUFrQixDQUFDLENBQUMsQ0FBQztJQUM1QyxJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxXQUFXLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUNyQyxRQUFRLENBQUMsMkJBQTJCLENBQ2hDLE1BQU0sRUFBRSxhQUFhLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBQ0QsSUFBTSxHQUFHLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQzlCLE1BQU0sQ0FBQyxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxXQUFXLENBQUM7QUFDckMsQ0FBQyxDQUFDO0FBRVcsUUFBQSx1QkFBdUIsR0FBa0IsVUFBQyxJQUFZO0lBQ2pFLElBQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLElBQU0sVUFBVSxHQUFHLElBQUksWUFBWSxDQUMvQixRQUFRLENBQUMscUNBQXFDLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDaEUsSUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsV0FBVyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDckMsUUFBUSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFDRCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLFdBQVcsQ0FBQztBQUNyQyxDQUFDLENBQUM7QUFFVyxRQUFBLHlCQUF5QixHQUFrQixVQUFDLElBQVk7SUFDbkUsSUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLGtCQUFrQixDQUFDLElBQUksR0FBRyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDaEUsSUFBTSxrQkFBa0IsR0FBRyxVQUFVLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUM5RCxJQUFNLGFBQWEsR0FDZixJQUFJLFlBQVksQ0FBQyxRQUFRLENBQUMsa0NBQWtDLENBQ3hELE1BQU0sQ0FBQyxNQUFNLEVBQUUsa0JBQWtCLENBQUMsQ0FBQyxDQUFDO0lBQzVDLFFBQVEsQ0FBQywyQkFBMkIsQ0FDaEMsTUFBTSxFQUFFLGFBQWEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0lBQy9DLElBQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFdBQVcsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ3JDLFFBQVEsQ0FBQyw2QkFBNkIsQ0FDbEMsYUFBYSxFQUFFLE1BQU0sRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFDRCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLFdBQVcsQ0FBQztBQUNyQyxDQUFDLENBQUM7QUFFVyxRQUFBLHVCQUF1QixHQUFrQixVQUFDLElBQVk7SUFDakUsSUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLGtCQUFrQixDQUFDLElBQUksR0FBRyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDaEUsSUFBTSxVQUFVLEdBQUcsSUFBSSxZQUFZLENBQy9CLFFBQVEsQ0FBQyxxQ0FBcUMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUNoRSxRQUFRLENBQUMsd0JBQXdCLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDbEUsSUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsV0FBVyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDckMsUUFBUSxDQUFDLDBCQUEwQixDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFDRCxJQUFNLEdBQUcsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDOUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxHQUFHLFdBQVcsQ0FBQztBQUNyQyxDQUFDLENBQUM7OztBQ2pFRixPQUFPLENBQUMsRUFBQyxFQUFFLEVBQUUsYUFBYSxFQUFDLENBQUMsQ0FBQzs7O0FDQTdCLE9BQU8sQ0FBQyxFQUFDLEVBQUUsRUFBRSxhQUFhLEVBQUMsQ0FBQyxDQUFDOzs7OztBQzRDN0Isd0JBQStCLElBQVU7SUFFdkMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBVyxDQUFpQyxDQUFDO0FBQ3BFLENBQUM7QUFIRCx3Q0FHQzs7Ozs7QUM5Q0QsOEJBQWdDO0FBRWhDLG1DQUNJLE9BQWlCLEVBQUUsT0FBaUIsRUFBRSxJQUFZLEVBQ2xELGtCQUF1QjtJQUF2QixtQ0FBQSxFQUFBLHVCQUF1QjtJQUN6QixJQUFJLENBQUMsTUFBTSxDQUNQLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUNwQixrQkFBa0IsR0FBRyx3Q0FBd0MsQ0FBQyxDQUFDO0lBQ25FLElBQUksQ0FBQyxNQUFNLENBQ1AsT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQ3BCLGtCQUFrQixHQUFHLHdDQUF3QyxDQUFDLENBQUM7SUFFbkUsSUFBSSxDQUFDLE1BQU0sQ0FDUCxJQUFJLElBQUksQ0FBQyxJQUFJLElBQUksR0FBRyxDQUFDLEVBQUUsNENBQTRDLENBQUMsQ0FBQztJQUV6RSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLEtBQUssSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQzNDLGtCQUFrQjthQUNkLFlBQVUsT0FBTywwQkFBcUIsT0FBTyxhQUFVLENBQUE7WUFDdkQsd0JBQXdCLENBQUMsQ0FBQztJQUNwQyxDQUFDO0FBQ0gsQ0FBQztBQXBCRCw4REFvQkM7QUFFRCxvQ0FDSSxPQUFpQixFQUFFLE9BQWlCLEVBQ3BDLElBQVk7SUFDZCxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLHdDQUF3QyxDQUFDLENBQUM7SUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSx1Q0FBdUMsQ0FBQyxDQUFDO0lBRTNFLElBQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUNwQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ25DLE1BQU0sQ0FBQyxXQUF1QyxDQUFDO0FBQ2pELENBQUM7QUFURCxnRUFTQzs7Ozs7QUNqQ0QsOEJBQWdDO0FBRWhDLDhCQUNJLHFCQUErQyxFQUFFLFNBQWlCLEVBQ2xFLEtBQWEsRUFBRSxNQUFjLEVBQUUsT0FBZ0I7SUFDakQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDcEIsT0FBTyxHQUFHLGlCQUFpQixDQUFDLHFCQUFxQixFQUFFLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUN4RSxDQUFDO0lBQ0QsSUFBTSxTQUFTLEdBQUcscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0MsSUFBTSxTQUFTLEdBQUcscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0MsSUFBTSxVQUFVLEdBQUcsQ0FBQyxTQUFTLEdBQUcsU0FBUyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsR0FBRyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ3RFLElBQUksQ0FBQyxNQUFNLENBQ1AsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFDdEIsMkJBQXlCLFVBQVUsc0NBQW1DO1FBQ2xFLG1DQUFtQyxDQUFDLENBQUM7SUFFN0MsSUFBTSxVQUFVLEdBQUcsQ0FBQyxTQUFTLEdBQUcsU0FBUyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsR0FBRyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ3RFLElBQUksQ0FBQyxNQUFNLENBQ1AsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFDdEIsOEJBQTRCLFVBQVUsa0NBQStCO1FBQ2pFLHVDQUF1QyxDQUFDLENBQUM7SUFFakQsTUFBTSxDQUFDLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUN6QyxDQUFDO0FBckJELG9EQXFCQztBQUVELDJCQUNJLFVBQW9DLEVBQUUsU0FBaUIsRUFDdkQsTUFBYztJQUNoQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsR0FBRyxNQUFNLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDN0UsQ0FBQztBQUpELDhDQUlDO0FBRUQsK0JBQ0ksZ0JBQTBDO0lBQzVDLE1BQU0sQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxFQUFFLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxHQUFHLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDMUUsQ0FBQztBQUhELHNEQUdDO0FBRUQsK0JBQ0ksVUFBa0IsRUFBRSxXQUFtQixFQUN2QyxLQUFhO0lBQ2YsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsV0FBVyxDQUFDLENBQUM7QUFDakQsQ0FBQztBQUpELHNEQUlDO0FBRUQsZ0NBQ0ksVUFBa0IsRUFBRSxXQUFtQixFQUN2QyxTQUFpQjtJQUNuQixNQUFNLENBQUMsQ0FBQyxTQUFTLEdBQUcsU0FBUyxHQUFHLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUMzRCxDQUFDO0FBSkQsd0RBSUM7QUFFRCwrQkFBc0MsV0FBbUI7SUFDdkQsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsQ0FBQyxDQUFDO0FBQzFCLENBQUM7QUFGRCxzREFFQztBQUVELDBCQUNJLEVBQW9CLEVBQUUsVUFBa0I7SUFDMUMsSUFBTSxXQUFXLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsVUFBVSxHQUFHLENBQUMsQ0FBQztJQUNqRCxJQUFNLFdBQVcsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxVQUFVLEdBQUcsQ0FBQyxDQUFDO0lBQ2pELE1BQU0sQ0FBQyxDQUFDLFdBQVcsRUFBRSxXQUFXLENBQUMsQ0FBQztBQUNwQyxDQUFDO0FBTEQsNENBS0M7Ozs7O0FDekRELHdCQUNJLFVBQTRCLEVBQUUsUUFBMEI7SUFDMUQsSUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM5QyxJQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLE9BQU8sS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ3hCLElBQU0sTUFBTSxHQUFHLEdBQUcsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUM7UUFDaEUsSUFBTSxNQUFNLEdBQUcsR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQztRQUM1RCxNQUFNLElBQUksS0FBSyxDQUNYLG9EQUFvRCxHQUFHLE1BQU07WUFDN0QsU0FBUyxHQUFHLE9BQU8sR0FBRyxlQUFlLEdBQUcsTUFBTSxHQUFHLFNBQVMsR0FBRyxPQUFPLENBQUMsQ0FBQztJQUM1RSxDQUFDO0FBQ0gsQ0FBQztBQVhELHdDQVdDOzs7OztBQ1hELDhCQUFnQztBQUNoQywrQ0FBaUQ7QUFDakQsMkNBQTZDO0FBRTdDLHFDQUE4RTtBQUk5RTtJQVdFLHFCQUFvQixRQUFpQjtRQUFqQixhQUFRLEdBQVIsUUFBUSxDQUFTO1FBVjdCLGtCQUFhLEdBQWdCLEVBQUUsQ0FBQztRQUdoQyxtQkFBYyxHQUFnQixFQUFFLENBQUM7UUFDakMsOEJBQXlCLEdBQWMsRUFBRSxDQUFDO0lBTVYsQ0FBQztJQVV6QywyQkFBSyxHQUFMLFVBQ0ksT0FFeUQ7UUFIN0QsaUJBYUM7UUFUQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFbEIsSUFBTSxNQUFNLEdBQUcsVUFBb0IsT0FBVSxJQUFRLE9BQUEsS0FBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBbEIsQ0FBa0IsQ0FBQztRQUN4RSxJQUFNLE9BQU8sR0FBRyxVQUFvQixPQUFVLElBQVEsT0FBQSxLQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFuQixDQUFtQixDQUFDO1FBQzFFLElBQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFeEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUV0QixNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFNRCxnQ0FBVSxHQUFWO1FBQ0UsSUFBTSxRQUFRLEdBQWMsRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxXQUFXLEdBQUcsUUFBUSxDQUFDO1FBRTVCLElBQU0saUJBQWlCLEdBQWMsRUFBRSxDQUFDO1FBQ3hDLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLHlCQUF5QixHQUFHLGlCQUFpQixDQUFDO0lBQ3JELENBQUM7SUFNRCw4QkFBUSxHQUFSLFVBQVMsTUFBbUI7UUFBNUIsaUJBb0NDO1FBbENDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNqRCxJQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXBDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLHlCQUF5QixDQUFDO2dCQUNqRSxDQUFDLE1BQU0sSUFBSSxJQUFJLElBQUksTUFBTSxZQUFZLGlCQUFPO29CQUMzQyxPQUFPLENBQUMsT0FBTyxFQUFFLEtBQU0sTUFBa0IsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDMUQsUUFBUSxDQUFDO1lBQ1gsQ0FBQztZQUNELE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNwQixDQUFDO1FBR0QsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN6QixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDOUMsSUFBSztZQUNMLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFHdEQsRUFBRSxDQUFDLENBQUMsTUFBTSxZQUFZLGlCQUFPO1lBQ3pCLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMseUJBQXlCLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDdEUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNyQixDQUFDO1FBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2pDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBQSxDQUFDO2dCQUNkLEVBQUUsQ0FBQyxDQUFDLENBQUMsWUFBWSxpQkFBTztvQkFDcEIsQ0FBQyxLQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxFQUFFLEtBQUksQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztvQkFDakUsS0FBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDaEIsQ0FBQztZQUNILENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLHlCQUF5QixHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxLQUFLLENBQUM7WUFDN0QsSUFBSztZQUNMLElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVPLHlDQUFtQixHQUEzQixVQUE0QixPQUFnQixFQUFFLFdBQXNCO1FBQ2xFLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsV0FBVyxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzVDLEVBQUUsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsS0FBSyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUNuRCxNQUFNLENBQUMsSUFBSSxDQUFDO1lBQ2QsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLENBQUMsS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQU1ELDBCQUFJLEdBQUosVUFBd0IsTUFBUztRQUMvQixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDN0IsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7Z0JBQ2xCLE1BQU0sSUFBSSxLQUFLLENBQ1gsK0NBQStDO29CQUMvQyxzQ0FBc0M7b0JBQ3RDLHdEQUF3RDtvQkFDeEQsUUFBUSxDQUFDLENBQUM7WUFDaEIsQ0FBQztZQUNELE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDaEIsQ0FBQztRQUNELElBQUksQ0FBQyx5QkFBeUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBT0QsMkJBQUssR0FBTCxVQUF5QixNQUFTO1FBQ2hDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQztZQUM3QixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztnQkFDbEIsTUFBTSxJQUFJLEtBQUssQ0FDWCwrQ0FBK0M7b0JBQy9DLHNDQUFzQztvQkFDdEMsd0RBQXdEO29CQUN4RCxRQUFRLENBQUMsQ0FBQztZQUNoQixDQUFDO1lBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBQ0QsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDOUIsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBYUQsNEJBQU0sR0FBTixVQUNJLENBQVUsRUFBRSxDQUFVLEVBQUUsWUFBd0MsRUFDaEUsWUFBd0M7UUFEaEIsNkJBQUEsRUFBQSxlQUFlLGlCQUFpQixDQUFDLE9BQU87UUFDaEUsNkJBQUEsRUFBQSxlQUFlLGlCQUFpQixDQUFDLE9BQU87UUFDMUMsSUFBTSxXQUFXLEdBQ2IsQ0FBQyxZQUFZLEtBQUssaUJBQWlCLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzNFLElBQU0sV0FBVyxHQUNiLENBQUMsWUFBWSxLQUFLLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUUzRSxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUM1Qix1REFBcUQsQ0FBQyxDQUFDLElBQU07YUFDekQsU0FBTyxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBRTFCLElBQUksQ0FBQyxNQUFNLENBQ1AsV0FBVyxLQUFLLFdBQVcsRUFDM0Isb0NBQWtDLFdBQVcsWUFBUzthQUMvQyxXQUFXLGtDQUE2QixDQUFDLENBQUMsS0FBSyxVQUFPLENBQUE7YUFDdEQsQ0FBQyxDQUFDLEtBQUssMEJBQXFCLGlCQUFpQixDQUFDLFlBQVksQ0FBRyxDQUFBO2FBQ2hFLFVBQVEsaUJBQWlCLENBQUMsWUFBWSxDQUFDLGlCQUFjLENBQUEsQ0FBQyxDQUFDO1FBRS9ELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxZQUFZLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBVUQsdUNBQWlCLEdBQWpCLFVBQWtCLENBQVUsRUFBRSxNQUFlO1FBQzNDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osa0VBQWtFO2FBQzlELFVBQVEsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMzQixJQUFJLENBQUMsTUFBTSxDQUNQLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNqQixtRUFBbUU7YUFDL0QsVUFBUSxNQUFNLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMxQiw2REFBMkQsQ0FBQyxDQUFDLElBQUksT0FBSTtZQUNqRSw2REFBNkQ7YUFDN0QsVUFBUSxNQUFNLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBRWhDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN2RCxDQUFDO0lBT0QsdUNBQWlCLEdBQWpCLFVBQWtCLE1BQWUsRUFBRSxDQUFVO1FBQzNDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osZ0VBQWdFO2FBQzVELFVBQVEsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMzQixJQUFJLENBQUMsTUFBTSxDQUNQLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNqQixvRUFBb0U7YUFDaEUsVUFBUSxNQUFNLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMxQiw0REFBMEQsQ0FBQyxDQUFDLElBQUksTUFBRztZQUMvRCw2REFBNkQ7YUFDN0QsV0FBUyxNQUFNLENBQUMsS0FBSyxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBRWxDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN2RCxDQUFDO0lBT0QsZ0NBQVUsR0FBVixVQUFXLEVBQVcsRUFBRSxFQUFXO1FBQ2pDLElBQUksQ0FBQyxNQUFNLENBQ1AsRUFBRSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQzlCLDREQUE0RDthQUNyRCxFQUFFLENBQUMsSUFBSSxhQUFRLEVBQUUsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDdEMsSUFBSSxDQUFDLE1BQU0sQ0FDUCxFQUFFLENBQUMsSUFBSSxLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQ25CLDBDQUF3QyxFQUFFLENBQUMsSUFBSSxZQUFTO2FBQ2pELEVBQUUsQ0FBQyxJQUFJLGtCQUFlLENBQUEsQ0FBQyxDQUFDO1FBQ25DLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUMxRSxDQUFDO0lBT0Qsa0NBQVksR0FBWixVQUFhLEVBQVcsRUFBRSxFQUFXO1FBQ25DLElBQUksQ0FBQyxNQUFNLENBQ1AsRUFBRSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQzlCLDhEQUE4RDthQUN2RCxFQUFFLENBQUMsSUFBSSxhQUFRLEVBQUUsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFFdEMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFVRCwyQkFBSyxHQUFMLFVBQXlCLE9BQVU7UUFDakMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ2pELENBQUM7SUFVRCw2QkFBTyxHQUFQLFVBQ0ksT0FBVyxFQUFFLFFBQWtCO1FBQ2pDLElBQUksQ0FBQyxNQUFNLENBQ1AsT0FBTyxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxFQUM3QyxnQ0FBOEIsT0FBTyxDQUFDLElBQUksMEJBQXVCO2FBQzFELElBQUksQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDNUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBUyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBWUQsNkJBQU8sR0FBUCxVQUFRLEtBQWMsRUFBRSxLQUF1QixFQUFFLElBQXNCO1FBRXJFLElBQUksQ0FBQyxNQUFNLENBQ1AsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztZQUNoQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQ3hDLGdEQUE4QyxLQUFLLGVBQVk7YUFDeEQsSUFBSSx1Q0FBa0MsS0FBSyxDQUFDLEtBQUssTUFBRyxDQUFBLENBQUMsQ0FBQztRQUNqRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUM5RCxDQUFDO0lBZUQsNEJBQU0sR0FBTixVQUNJLE1BQWUsRUFBRSxXQUE2QixFQUM5QyxVQUE0QixFQUFFLElBQWEsRUFBRSxTQUEyQixFQUN4RSxRQUEwQjtRQUM1QixJQUFJLENBQUMsTUFBTSxDQUNQLFdBQVcsQ0FBQyxDQUFDLENBQUMsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDN0MsV0FBVyxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUNyRCxzREFBb0QsV0FBVyxNQUFHO2FBQzlELHFCQUFtQixVQUFVLG1DQUFnQyxDQUFBO2FBQzdELGNBQVksTUFBTSxDQUFDLEtBQUssTUFBRyxDQUFBLENBQUMsQ0FBQztRQUNyQyxJQUFJLENBQUMsTUFBTSxDQUNQLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDdkMsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMvQyxvREFBa0QsU0FBUyxNQUFHO2FBQzFELHFCQUFtQixRQUFRLG9DQUFpQyxDQUFBO2FBQzVELFdBQVMsSUFBSSxDQUFDLEtBQUssTUFBRyxDQUFBLENBQUMsQ0FBQztRQUNoQyxXQUFXLENBQUMsY0FBYyxDQUFDLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUVqRCxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FDdEIsTUFBTSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBb0NELDhCQUFRLEdBQVIsVUFBUyxRQUFpQixFQUFFLFFBQWlCLEVBQUUsSUFBWTtRQUN6RCxhQUFhLENBQUMseUJBQXlCLENBQ25DLFFBQVEsQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUscUJBQXFCLENBQUMsQ0FBQztRQUNqRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFZRCwrQkFBUyxHQUFULFVBQVUsT0FBZ0I7UUFDeEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDckQsQ0FBQztJQU9ELHlCQUFHLEdBQUgsVUFBSSxPQUFnQjtRQUNsQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQU9ELDRCQUFNLEdBQU4sVUFBTyxPQUFnQjtRQUNyQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQU9ELDRCQUFNLEdBQU4sVUFBTyxPQUFnQjtRQUNyQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQVFELGtDQUFZLEdBQVosVUFBYSxFQUFXLEVBQUUsRUFBVztRQUNuQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxFQUFFLHlCQUF5QixDQUFDLENBQUM7UUFDdEUsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFRRCwwQkFBSSxHQUFKLFVBQUssT0FBZ0IsRUFBRSxDQUFTO1FBQzlCLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEVBQ2pCLDZCQUEyQixDQUFDLHVDQUFvQzthQUM1RCx3QkFBc0IsT0FBTyxDQUFDLEtBQUssTUFBRyxDQUFBLENBQUMsQ0FBQztRQUNoRCxJQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM3QyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMxQixJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMzQixNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFRRCx5QkFBRyxHQUFILFVBQUksT0FBZ0I7UUFDbEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFPRCx5QkFBRyxHQUFILFVBQUksT0FBZ0I7UUFDbEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFPRCw2QkFBTyxHQUFQLFVBQVEsQ0FBVTtRQUFsQixpQkFRQztRQVBDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDO1lBR2hCLElBQU0sR0FBRyxHQUFHLEtBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDOUIsSUFBTSxTQUFTLEdBQUcsS0FBSSxDQUFDLGdCQUFnQixDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNoRCxNQUFNLENBQUMsS0FBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3QixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFXRCwrQkFBUyxHQUFULFVBQTZCLENBQUksRUFBRSxNQUFnQjtRQUNqRCxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssTUFBTSxDQUFDLE1BQU0sRUFDeEIsK0NBQTZDLENBQUMsQ0FBQyxLQUFLLE1BQUc7YUFDbkQscUNBQW1DLE1BQU0sTUFBRyxDQUFBLENBQUMsQ0FBQztRQUN0RCxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQVNELHFDQUFlLEdBQWYsVUFBbUMsQ0FBUyxFQUFFLENBQUk7UUFDaEQsSUFBSSxDQUFDLE1BQU0sQ0FDUCxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDWixtRUFBbUU7YUFDL0QsVUFBUSxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQzNCLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBU0Qsc0NBQWdCLEdBQWhCLFVBQW9DLENBQVMsRUFBRSxDQUFJO1FBQ2pELElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osb0VBQW9FO2FBQ2hFLFVBQVEsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMzQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekQsQ0FBQztJQVNELHNDQUFnQixHQUFoQixVQUFvQyxDQUFJLEVBQUUsQ0FBUztRQUNqRCxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLGlFQUFpRTthQUM3RCxjQUFZLENBQUMsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDL0IsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLHdCQUF3QixDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFRRCx5QkFBRyxHQUFILFVBQXVCLENBQUk7UUFDekIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7SUFRRCx5QkFBRyxHQUFILFVBQXVCLENBQUksRUFBRSxDQUFJO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUMzRCxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFRRCx5QkFBRyxHQUFILFVBQXVCLENBQUksRUFBRSxDQUFJO1FBQy9CLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUMzRCxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFTRCxvQ0FBYyxHQUFkLFVBQWtDLENBQUksRUFBRSxDQUFJO1FBQzFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxLQUFLLEVBQUUsMkJBQTJCLENBQUMsQ0FBQztRQUN0RSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQVNELDRCQUFNLEdBQU4sVUFBMEIsQ0FBSSxFQUFFLENBQUk7UUFDbEMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO1FBQzlELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQVNELDBDQUFvQixHQUFwQixVQUF3QyxDQUFTLEVBQUUsQ0FBSTtRQUNyRCxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLG9FQUFvRTthQUNoRSx5QkFBdUIsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMxQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsNEJBQTRCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQVVELDBDQUFvQixHQUFwQixVQUF3QyxDQUFJLEVBQUUsQ0FBUztRQUNyRCxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLGlFQUFpRTthQUM3RCw2QkFBMkIsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUM5QyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsNEJBQTRCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQVFELHlCQUFHLEdBQUgsVUFBdUIsT0FBVTtRQUMvQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQU9ELHlCQUFHLEdBQUgsVUFBdUIsT0FBVTtRQUMvQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQU9ELDBCQUFJLEdBQUosVUFBd0IsT0FBVTtRQUNoQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQU9ELDZCQUFPLEdBQVAsVUFBMkIsT0FBVTtRQUNuQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQU9ELDBCQUFJLEdBQUosVUFBd0IsT0FBVTtRQUNoQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQU9ELHlCQUFHLEdBQUgsVUFBdUIsT0FBVTtRQUMvQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQVFELDBCQUFJLEdBQUosVUFBd0IsT0FBVTtRQUNoQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQVVELG9DQUFjLEdBQWQsVUFBa0MsRUFBVSxFQUFFLENBQUksRUFBRSxFQUFVLEVBQUUsQ0FBSTtRQUNsRSxJQUFJLENBQUMsTUFBTSxDQUNQLEVBQUUsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNiLCtEQUErRDthQUMzRCxXQUFTLEVBQUUsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDN0IsSUFBSSxDQUFDLE1BQU0sQ0FDUCxFQUFFLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDYixrRUFBa0U7YUFDOUQscUJBQW1CLEVBQUUsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSwyQkFBMkIsQ0FBQyxDQUFDO1FBRXRFLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQy9ELENBQUM7SUFVRCxzQ0FBZ0IsR0FBaEIsVUFBb0MsQ0FBUyxFQUFFLENBQUk7UUFDakQsSUFBSSxDQUFDLE1BQU0sQ0FDUCxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDWixvRUFBb0U7YUFDaEUsY0FBWSxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQy9CLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBV0QsNkNBQXVCLEdBQXZCLFVBQXdCLENBQVUsRUFBRSxDQUFVO1FBQzVDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osMkRBQTJEO2FBQ3ZELDBCQUF3QixDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osNERBQTREO2FBQ3hELDBCQUF3QixDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNoRSxDQUFDO0lBa0JELDRCQUFNLEdBQU4sVUFDSSxDQUFVLEVBQUUsT0FBZ0IsRUFBRSxNQUFvQixFQUFFLE1BQWMsRUFDbEUsT0FBZTtRQUNqQixJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLHFEQUFtRCxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsTUFBTSxDQUNQLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNsQix3REFBd0Q7YUFDakQsT0FBTyxDQUFDLElBQUksTUFBRyxDQUFBLENBQUMsQ0FBQztRQUM1QixFQUFFLENBQUMsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNuQixJQUFJLENBQUMsTUFBTSxDQUNQLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNqQix1REFBdUQ7aUJBQ2hELE1BQU0sQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDN0IsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMvQixzQ0FBb0MsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsbUJBQWdCO2FBQzFELDZCQUEyQixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBR3hELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQWNELG9DQUFjLEdBQWQsVUFDSSxDQUFVLEVBQUUsRUFBVyxFQUFFLE9BQWdCLEVBQUUsTUFBYyxFQUN6RCxHQUFXO1FBQ2IsSUFBSSxDQUFDLE1BQU0sQ0FDUCxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDWiwyREFBMkQ7YUFDcEQsQ0FBQyxDQUFDLEtBQUssTUFBRyxDQUFBLENBQUMsQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxDQUNQLEVBQUUsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNiLDREQUE0RDthQUNyRCxFQUFFLENBQUMsS0FBSyxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxNQUFNLENBQ1AsT0FBTyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ2xCLGlFQUFpRTthQUMxRCxPQUFPLENBQUMsS0FBSyxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMvQix5Q0FBdUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsWUFBUzthQUN0RCxvQ0FBa0MsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMvRCxJQUFJLENBQUMsTUFBTSxDQUNQLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFDaEMsMkNBQXlDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFlBQVM7YUFDekQscUNBQW1DLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE9BQUksQ0FBQSxDQUFDLENBQUM7UUFFakUsSUFBTSxjQUFjLEdBQ2hCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFOUIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN4QixDQUFDO0lBZ0JELHFDQUFlLEdBQWYsVUFDSSxDQUFVLEVBQUUsT0FBZ0IsRUFBRSxNQUFvQixFQUFFLE1BQWMsRUFDbEUsR0FBVztRQUNiLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osMkRBQTJEO2FBQ3BELENBQUMsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDdEIsSUFBSSxDQUFDLE1BQU0sQ0FDUCxPQUFPLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDbEIsNERBQTREO2FBQ3hELFVBQVEsT0FBTyxDQUFDLElBQU0sQ0FBQSxDQUFDLENBQUM7UUFDaEMsRUFBRSxDQUFDLENBQUMsTUFBTSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDbkIsSUFBSSxDQUFDLE1BQU0sQ0FDUCxNQUFNLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDakIsdUZBQ1ksTUFBTSxDQUFDLElBQUksTUFBRyxDQUFDLENBQUM7UUFDbEMsQ0FBQztRQUNELElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUMvQiwrQ0FBNkMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsWUFBUzthQUM1RCxtQ0FBaUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBRyxDQUFBLENBQUMsQ0FBQztRQUU5RCxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FDYixJQUFJLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQWFELDZCQUFPLEdBQVAsVUFBUSxDQUFVLEVBQUUsS0FBYSxFQUFFLE1BQWMsRUFBRSxHQUFXO1FBQzVELElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osa0RBQWtELEdBQUcsQ0FBQyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztRQUN2RSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDakUsQ0FBQztJQWFELHFDQUFlLEdBQWYsVUFDSSxFQUFXLEVBQUUsQ0FBVSxFQUFFLEtBQWEsRUFBRSxNQUFjLEVBQ3RELEdBQVc7UUFDYixJQUFJLENBQUMsTUFBTSxDQUNQLEVBQUUsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNiLDJEQUEyRDthQUNwRCxFQUFFLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1osMERBQTBEO2FBQ25ELENBQUMsQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFFdEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQzdFLENBQUM7SUFhRCw2QkFBTyxHQUFQLFVBQVEsQ0FBVSxFQUFFLEtBQWEsRUFBRSxNQUFjLEVBQUUsR0FBVztRQUM1RCxJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLHFEQUFtRCxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUMsQ0FBQztRQUNsRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDakUsQ0FBQztJQVlELDZCQUFPLEdBQVAsVUFBUSxDQUFVLEVBQUUsS0FBYSxFQUFFLE1BQWMsRUFBRSxHQUFXO1FBQzVELElBQUksQ0FBQyxNQUFNLENBQ1AsQ0FBQyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ1oscURBQW1ELENBQUMsQ0FBQyxJQUFJLE1BQUcsQ0FBQyxDQUFDO1FBQ2xFLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBY0Qsc0NBQWdCLEdBQWhCLFVBQ0ksQ0FBVSxFQUFFLFVBQTRCLEVBQUUsWUFBb0I7UUFBcEIsNkJBQUEsRUFBQSxvQkFBb0I7UUFDaEUsSUFBSSxDQUFDLE1BQU0sQ0FDUCxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDWiw4REFBNEQsQ0FBQyxDQUFDLElBQUksTUFBRyxDQUFDLENBQUM7UUFDM0UsSUFBSSxDQUFDLE1BQU0sQ0FDUCxVQUFVLENBQUMsTUFBTSxLQUFLLENBQUMsRUFDdkIsOERBQThEO2FBQ3ZELFVBQVUsTUFBRyxDQUFBLENBQUMsQ0FBQztRQUMxQixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FDYixJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFnQkQsMENBQW9CLEdBQXBCLFVBQ0ksQ0FBVSxFQUFFLElBQXFCLEVBQUUsUUFBeUIsRUFDNUQsZUFBc0IsRUFBRSxLQUF1QixFQUMvQyxNQUF3QjtRQUR4QixnQ0FBQSxFQUFBLHNCQUFzQjtRQUV4QixJQUFJLENBQUMsTUFBTSxDQUNQLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUNaLCtEQUErRDthQUN4RCxDQUFDLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQ3RCLElBQUksQ0FBQyxNQUFNLENBQ1AsSUFBSSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ2xDLG1FQUFtRTthQUMvRCxjQUFZLElBQUksQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDbEMsSUFBSSxDQUFDLE1BQU0sQ0FDUCxRQUFRLENBQUMsSUFBSSxLQUFLLENBQUMsSUFBSSxRQUFRLENBQUMsSUFBSSxLQUFLLENBQUMsRUFDMUMsbUVBQW1FO2FBQy9ELGtCQUFnQixRQUFRLENBQUMsSUFBSSxNQUFHLENBQUEsQ0FBQyxDQUFDO1FBQzFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLElBQUksQ0FBQyxNQUFNLENBQ1AsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ3BDLGdFQUFnRTtpQkFDNUQsa0JBQWdCLEtBQU0sQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDMUMsQ0FBQztRQUNELEVBQUUsQ0FBQyxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ25CLElBQUksQ0FBQyxNQUFNLENBQ1AsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQ3RDLGlFQUFpRTtpQkFDN0Qsa0JBQWdCLE1BQU8sQ0FBQyxJQUFJLE1BQUcsQ0FBQSxDQUFDLENBQUM7UUFDM0MsQ0FBQztRQUVELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyw0QkFBNEIsQ0FDL0MsQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQzFELENBQUM7SUFLSCxrQkFBQztBQUFELENBNWdDQSxBQTRnQ0MsSUFBQTtBQTVnQ3FCLGtDQUFXO0FBOGdDakMsSUFBWSxpQkFHWDtBQUhELFdBQVksaUJBQWlCO0lBQzNCLCtEQUFPLENBQUE7SUFDUCxxRUFBVSxDQUFBO0FBQ1osQ0FBQyxFQUhXLGlCQUFpQixHQUFqQix5QkFBaUIsS0FBakIseUJBQWlCLFFBRzVCOzs7Ozs7Ozs7Ozs7Ozs7QUN6aENELDZDQUErQztBQUMvQyw4QkFBZ0M7QUFFaEMsK0NBQWlEO0FBQ2pELDJDQUE2QztBQUM3QywrQkFBc0Q7QUFDdEQscUNBQThFO0FBRTlFO0lBQW9DLGtDQUFXO0lBQzdDLHdCQUFZLFFBQWdCO1FBQWhCLHlCQUFBLEVBQUEsZ0JBQWdCO2VBQzFCLGtCQUFNLFFBQVEsQ0FBQztJQUNqQixDQUFDO0lBRVMsc0NBQWEsR0FBdkIsVUFBMkMsT0FBVTtRQUNuRCxNQUFNLENBQUMsaUJBQU8sQ0FBQyxJQUFJLENBQ2YsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFDLE1BQU0sRUFBRSxJQUFJLFlBQVksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUMsRUFBQyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUVTLHdDQUFlLEdBQXpCLFVBQ0ksT0FBVyxFQUFFLFFBQWtCO1FBQ2pDLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBSyxRQUFRLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRVMsd0NBQWUsR0FBekIsVUFDSSxLQUFjLEVBQUUsV0FBNkIsRUFDN0MsVUFBNEI7UUFDOUIsSUFBTSxNQUFNLEdBQUcsaUJBQU8sQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGNBQWMsQ0FDZixLQUFLLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDaEUsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRVMsdUNBQWMsR0FBeEIsVUFDSSxNQUFlLEVBQUUsaUJBQW1DLEVBQ3BELGdCQUFrQyxFQUFFLElBQWEsRUFDakQsZUFBaUMsRUFDakMsY0FBZ0M7UUFDbEMsV0FBVyxDQUFDLGNBQWMsQ0FBQyxnQkFBZ0IsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUM3RCxJQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDckMsSUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLElBQU0sQ0FBQyxHQUFHLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxHQUFHLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BELEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDM0IsSUFBTSxNQUFNLEdBQUcsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUMxRSxJQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2hFLElBQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQztZQUNqRCxJQUFNLE1BQU0sR0FBRyxlQUFlLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDdEUsSUFBTSxNQUFNLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzVELElBQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQztZQUMvQyxTQUFTLENBQUMsTUFBTSxDQUFDLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3hDLENBQUM7SUFDSCxDQUFDO0lBRVMseUNBQWdCLEdBQTFCLFVBQTJCLEVBQVcsRUFBRSxFQUFXLEVBQUUsSUFBWTtRQUMvRCxJQUFNLFdBQVcsR0FDYixhQUFhLENBQUMsMEJBQTBCLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBRXZFLElBQU0sTUFBTSxHQUFHLGlCQUFPLENBQUMsS0FBSyxDQUFVLFdBQVcsQ0FBQyxDQUFDO1FBRW5ELEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDeEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDeEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztvQkFFeEMsSUFBTSxLQUFLLEdBQTZCLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztvQkFDbEQsSUFBSSxLQUFLLFNBQVEsQ0FBQztvQkFDbEIsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO3dCQUNqQyxLQUFLLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO29CQUMxQixDQUFDO29CQUFDLElBQUksQ0FBQyxDQUFDO3dCQUNOLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO3dCQUN2QixJQUFBLGFBQUUsRUFBRSxhQUFFLEVBQUUsYUFBRSxDQUFVO3dCQUMzQixLQUFLLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO29CQUM3QixDQUFDO29CQUVELE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzdCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVTLGdEQUF1QixHQUFqQyxVQUFxRCxDQUFTLEVBQUUsQ0FBSTtRQUNsRSxJQUFNLFlBQVksR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUMsSUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzlCLElBQU0sSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNyQixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFlBQVksQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUM3QyxZQUFZLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN0QyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsWUFBWSxFQUFDLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBRVMsK0NBQXNCLEdBQWhDLFVBQ0ksRUFBVSxFQUFFLENBQUksRUFBRSxFQUFVLEVBQUUsQ0FBSTtRQUNwQyxJQUFNLE9BQU8sR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekMsSUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzlCLElBQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUM5QixJQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDdkIsSUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxLQUFLLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEtBQUssR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLElBQUksQ0FBSSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUMsTUFBTSxFQUFFLE9BQU8sRUFBQyxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVTLGlEQUF3QixHQUFsQyxVQUFzRCxDQUFTLEVBQUUsQ0FBSTtRQUNuRSxJQUFNLFNBQVMsR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDM0MsSUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzlCLElBQU0sSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNyQixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN4QyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRVMsaURBQXdCLEdBQWxDLFVBQXNELENBQVMsRUFBRSxDQUFJO1FBQ25FLElBQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDakMsSUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLHVCQUF1QixDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUVyRCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFZixNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFUyxpREFBd0IsR0FBbEMsVUFBc0QsQ0FBSSxFQUFFLENBQVM7UUFDbkUsSUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNqQyxJQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBRXJELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUVmLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVTLG9DQUFXLEdBQXJCLFVBQXlDLENBQUk7UUFDM0MsTUFBTSxDQUFDLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxnQkFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBRVMsb0NBQVcsR0FBckIsVUFBeUMsQ0FBSSxFQUFFLENBQUk7UUFDakQsTUFBTSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBSSxnQkFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsZ0JBQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUVTLG9DQUFXLEdBQXJCLFVBQXlDLENBQUksRUFBRSxDQUFJO1FBQ2pELE1BQU0sQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUksZ0JBQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLGdCQUFNLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFFUyx1Q0FBYyxHQUF4QixVQUNJLENBQVUsRUFBRSxDQUFVLEVBQUUsWUFBd0MsRUFDaEUsWUFBd0M7UUFEaEIsNkJBQUEsRUFBQSxlQUFlLHdCQUFpQixDQUFDLE9BQU87UUFDaEUsNkJBQUEsRUFBQSxlQUFlLHdCQUFpQixDQUFDLE9BQU87UUFDMUMsSUFBTSxTQUFTLEdBQ1gsQ0FBQyxZQUFZLEtBQUssd0JBQWlCLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTNFLElBQU0sT0FBTyxHQUNULENBQUMsWUFBWSxLQUFLLHdCQUFpQixDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMzRSxJQUFNLFFBQVEsR0FDVixDQUFDLFlBQVksS0FBSyx3QkFBaUIsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFM0UsSUFBTSxZQUFZLEdBQUcsVUFBQyxNQUFlLEVBQUUsQ0FBUyxFQUFFLENBQVM7WUFDdkQsT0FBQSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFBaEIsQ0FBZ0IsQ0FBQztRQUNyQixJQUFNLGdCQUFnQixHQUFHLFVBQUMsTUFBZSxFQUFFLENBQVMsRUFBRSxDQUFTO1lBQzNELE9BQUEsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQWhCLENBQWdCLENBQUM7UUFFckIsSUFBTSxPQUFPLEdBQUcsQ0FBQyxZQUFZLEtBQUssd0JBQWlCLENBQUMsT0FBTyxDQUFDO1lBQ3hELFlBQVk7WUFDWixnQkFBZ0IsQ0FBQztRQUNyQixJQUFNLE9BQU8sR0FBRyxDQUFDLFlBQVksS0FBSyx3QkFBaUIsQ0FBQyxPQUFPLENBQUM7WUFDeEQsWUFBWTtZQUNaLGdCQUFnQixDQUFDO1FBQ3JCLElBQU0sTUFBTSxHQUFHLElBQUksWUFBWSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQztRQUNwRCxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUM7UUFFZCxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ2pDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7Z0JBQ2xDLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztnQkFDWixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFNBQVMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO29CQUVuQyxHQUFHLElBQUksT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzdDLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDO1lBQ3hCLENBQUM7UUFDSCxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFUywrQ0FBc0IsR0FBaEMsVUFBb0QsQ0FBSSxFQUFFLENBQUk7UUFDNUQsSUFBTSxTQUFTLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNDLElBQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUM5QixJQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDOUIsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDeEMsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekMsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLElBQUksQ0FBSSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUMsTUFBTSxFQUFFLFNBQVMsRUFBQyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQUVTLHdEQUErQixHQUF6QyxVQUEwQyxDQUFVLEVBQUUsQ0FBVTtRQUM5RCxJQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hELElBQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFaEQsSUFBTSxNQUFNLEdBQUcsSUFBSSxZQUFZLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNkLEdBQUcsQ0FBQyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsTUFBTSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUM7WUFDdEMsR0FBRyxDQUFDLENBQUMsSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztnQkFDdEMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztvQkFDdkQsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2hELENBQUM7UUFDSCxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFFUyx1Q0FBYyxHQUF4QixVQUE0QyxDQUFJLEVBQUUsQ0FBSTtRQUNwRCxJQUFNLFNBQVMsR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDM0MsSUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzlCLElBQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUM5QixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN4QyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRVMscURBQTRCLEdBQXRDLFVBQTBELENBQVMsRUFBRSxDQUFJO1FBRXZFLElBQU0sU0FBUyxHQUFHLElBQUksWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQyxJQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDOUIsSUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3hDLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFDRCxNQUFNLENBQUMsaUJBQU8sQ0FBQyxJQUFJLENBQUksQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFDLE1BQU0sRUFBRSxTQUFTLEVBQUMsQ0FBQyxDQUFDO0lBQ3ZELENBQUM7SUFFUyxxREFBNEIsR0FBdEMsVUFBMEQsQ0FBSSxFQUFFLENBQVM7UUFFdkUsSUFBTSxTQUFTLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzNDLElBQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUM5QixJQUFNLE1BQU0sR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDdkIsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDeEMsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUM7UUFDckMsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLElBQUksQ0FBSSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUMsTUFBTSxFQUFFLFNBQVMsRUFBQyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQUVTLG9DQUFXLEdBQXJCLFVBQXNCLE9BQWdCO1FBQ3BDLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztRQUNaLElBQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNuQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxHQUFHLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25CLENBQUM7UUFDRCxNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUVTLHVDQUFjLEdBQXhCLFVBQXlCLE9BQWdCO1FBQ3ZDLElBQUksR0FBRyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDM0IsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDbEIsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLElBQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN4QixFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqQixNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDekIsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNoQixHQUFHLEdBQUcsS0FBSyxDQUFDO2dCQUNaLFFBQVEsR0FBRyxDQUFDLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxnQkFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM5QixDQUFDO0lBRVMsdUNBQWMsR0FBeEIsVUFBeUIsT0FBZ0I7UUFDdkMsSUFBSSxHQUFHLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDO1FBQ25DLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ2xCLElBQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNuQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxJQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEIsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDakIsTUFBTSxDQUFDLGdCQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3pCLENBQUM7WUFDRCxFQUFFLENBQUMsQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDaEIsR0FBRyxHQUFHLEtBQUssQ0FBQztnQkFDWixRQUFRLEdBQUcsQ0FBQyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDOUIsQ0FBQztJQUVTLDZDQUFvQixHQUE5QixVQUErQixFQUFXLEVBQUUsRUFBVztRQUNyRCxJQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQzlDLElBQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDOUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDckMsTUFBTSxDQUFDLGdCQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3pCLENBQUM7UUFDRCxNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFUyxxQ0FBWSxHQUF0QixVQUF1QixPQUFnQixFQUFFLENBQVM7UUFFaEQsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLElBQU0sZ0JBQWdCLEdBQTBDLEVBQUUsQ0FBQztRQUNuRSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN2QyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUMsQ0FBQyxDQUFDO1FBQ3RELENBQUM7UUFDRCxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsVUFBQyxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDO1FBQzNCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBTSxVQUFVLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsSUFBTSxXQUFXLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDeEMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUMzQixVQUFVLENBQUMsQ0FBQyxDQUFDLEdBQUcsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO1lBQzFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsR0FBRyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFDN0MsQ0FBQztRQUNELE1BQU0sQ0FBQyxFQUFDLE1BQU0sRUFBRSxpQkFBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRSxPQUFPLEVBQUUsaUJBQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLEVBQUMsQ0FBQztJQUM5RSxDQUFDO0lBRVMsb0NBQVcsR0FBckIsVUFBc0IsT0FBZ0I7UUFDcEMsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLElBQUksR0FBRyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwQixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxJQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEIsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDakIsTUFBTSxDQUFDLGdCQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3pCLENBQUM7WUFDRCxFQUFFLENBQUMsQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDaEIsR0FBRyxHQUFHLEtBQUssQ0FBQztZQUNkLENBQUM7UUFDSCxDQUFDO1FBQ0QsTUFBTSxDQUFDLGdCQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFFUyxvQ0FBVyxHQUFyQixVQUFzQixPQUFnQjtRQUNwQyxJQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkMsSUFBSSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3BCLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLElBQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN4QixFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqQixNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDekIsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLEtBQUssR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNoQixHQUFHLEdBQUcsS0FBSyxDQUFDO1lBQ2QsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLENBQUMsZ0JBQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUVTLG9DQUFXLEdBQXJCLFVBQXlDLE9BQVU7UUFDakQsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLElBQU0sU0FBUyxHQUFHLElBQUksWUFBWSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNsRCxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNyQyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRVMsb0NBQVcsR0FBckIsVUFBeUMsT0FBVTtRQUNqRCxJQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkMsSUFBTSxTQUFTLEdBQUcsSUFBSSxZQUFZLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2xELEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLElBQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN4QixTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNqQyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRVMsMENBQWlCLEdBQTNCLFVBQTRCLE9BQWdCO1FBQzFDLElBQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0IsSUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMvQyxJQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3RCLElBQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdEIsSUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN0QixJQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztRQUVqQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDZixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFWixNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFUyxxQ0FBWSxHQUF0QixVQUEwQyxPQUFVO1FBQ2xELElBQU0sWUFBWSxHQUFHLElBQUksWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDdkMsWUFBWSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzNDLENBQUM7UUFDRCxNQUFNLENBQUMsaUJBQU8sQ0FBQyxJQUFJLENBQUksT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFFUyx3Q0FBZSxHQUF6QixVQUE2QyxPQUFVO1FBQ3JELElBQU0sWUFBWSxHQUFHLElBQUksWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDdkMsWUFBWSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLE9BQU8sQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsWUFBWSxFQUFDLENBQUMsQ0FBQztJQUNoRSxDQUFDO0lBRVMscUNBQVksR0FBdEIsVUFBMEMsT0FBVTtRQUNsRCxJQUFNLFlBQVksR0FBRyxJQUFJLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDcEQsSUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25DLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLFlBQVksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLENBQUM7UUFDRCxNQUFNLENBQUMsaUJBQU8sQ0FBQyxJQUFJLENBQUksT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUMsQ0FBQyxDQUFDO0lBQ2hFLENBQUM7SUFFUyxvQ0FBVyxHQUFyQixVQUF5QyxPQUFVO1FBQ2pELElBQU0sWUFBWSxHQUFHLElBQUksWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDdkMsWUFBWSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDeEMsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLElBQUksQ0FBSSxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUMsTUFBTSxFQUFFLFlBQVksRUFBQyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVTLHFDQUFZLEdBQXRCLFVBQTBDLE9BQVU7UUFDbEQsSUFBTSxZQUFZLEdBQUcsSUFBSSxZQUFZLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNuQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxJQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEIsWUFBWSxDQUFDLENBQUMsQ0FBQyxHQUFHLEtBQUssR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUM7UUFDNUQsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLElBQUksQ0FBSSxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUMsTUFBTSxFQUFFLFlBQVksRUFBQyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQU1TLHVDQUFjLEdBQXhCLFVBQ0ksQ0FBVSxFQUFFLE9BQWdCLEVBQUUsTUFBb0IsRUFBRSxNQUFjLEVBQ2xFLEdBQVc7UUFDUCxJQUFBLFlBQW9DLEVBQW5DLGFBQUssRUFBRSxhQUFLLEVBQUUsa0JBQVUsQ0FBWTtRQUMzQyxJQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25DLElBQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDckMsSUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLG9CQUFvQixDQUM5QyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsVUFBVSxDQUFDLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDckUsSUFBTSxDQUFDLEdBQUcsaUJBQU8sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDckMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxXQUFXLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztZQUN4QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztnQkFDdkMsSUFBTSxRQUFRLEdBQUcsRUFBRSxHQUFHLE1BQU0sR0FBRyxHQUFHLENBQUM7Z0JBQ25DLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUNwQyxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxTQUFTLEdBQUcsUUFBUSxDQUFDLENBQUM7Z0JBQ3BELEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO29CQUN2QyxJQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQztvQkFDbkMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7b0JBQ3BDLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLFNBQVMsR0FBRyxRQUFRLENBQUMsQ0FBQztvQkFDcEQsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO29CQUNoQixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUN0QyxJQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsUUFBUSxDQUFDO3dCQUN6QixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDOzRCQUN0QyxJQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsUUFBUSxDQUFDOzRCQUN6QixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFVBQVUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2dDQUN2QyxJQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0NBQ2hDLElBQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0NBQzNDLE9BQU8sSUFBSSxLQUFLLEdBQUcsTUFBTSxDQUFDOzRCQUM1QixDQUFDO3dCQUNILENBQUM7b0JBQ0gsQ0FBQztvQkFDRCxJQUFNLElBQUksR0FBRyxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDbkQsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEdBQUcsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQ3BDLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRVMsK0NBQXNCLEdBQWhDLFVBQ0ksQ0FBVSxFQUFFLEVBQVcsRUFBRSxPQUFnQixFQUFFLE1BQWMsRUFDekQsR0FBVztRQUNiLElBQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDL0IsSUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUM1RCxJQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2xDLElBQU0sRUFBRSxHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxFQUFFLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDeEUsTUFBTSxDQUFDLEVBQUMsRUFBRSxJQUFBLEVBQUUsRUFBRSxJQUFBLEVBQUUsRUFBRSxJQUFBLEVBQUMsQ0FBQztJQUN0QixDQUFDO0lBTVMsZ0RBQXVCLEdBQWpDLFVBQ0ksQ0FBVSxFQUFFLE9BQWdCLEVBQUUsTUFBb0IsRUFBRSxVQUFrQixFQUN0RSxPQUFlO1FBQ2pCLElBQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDL0IsSUFBTSxHQUFHLEdBQUcsS0FBSyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUM7UUFDaEMsSUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4QyxJQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25DLElBQUEsWUFBZ0MsRUFBL0IsYUFBSyxFQUFFLGFBQUssRUFBRSxjQUFNLENBQVk7UUFHdkMsSUFBTSxZQUFZLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNsRCxJQUFNLFlBQVksR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsR0FBRyxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBRWxELElBQU0sV0FBVyxHQUFHLFNBQVMsQ0FBQyxvQkFBb0IsQ0FDOUMsQ0FBQyxZQUFZLEVBQUUsWUFBWSxFQUFFLGVBQWUsQ0FBQyxFQUFFLEtBQUssRUFBRSxjQUFjLEVBQUUsQ0FBQyxFQUN2RSxHQUFHLENBQUMsQ0FBQztRQUNULElBQU0sQ0FBQyxHQUFHLGlCQUFPLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3JDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsY0FBYyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7WUFDM0MsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7Z0JBQ3ZDLElBQU0sUUFBUSxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUM7Z0JBQzFCLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUM7Z0JBQzVELElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDO2dCQUUvRCxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztvQkFDdkMsSUFBTSxRQUFRLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQztvQkFDMUIsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQztvQkFDNUQsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLEdBQUcsVUFBVSxDQUFDLENBQUM7b0JBRS9ELElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQztvQkFDaEIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzt3QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFVBQVUsR0FBRyxRQUFRLENBQUM7d0JBRXRDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7NEJBQ3RDLElBQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxVQUFVLEdBQUcsUUFBUSxDQUFDOzRCQUV0QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLGVBQWUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2dDQUM1QyxJQUFNLEtBQUssR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0NBQ2hDLElBQU0sTUFBTSxHQUNSLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsS0FBSyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dDQUN4RCxPQUFPLElBQUksS0FBSyxHQUFHLE1BQU0sQ0FBQzs0QkFDNUIsQ0FBQzt3QkFDSCxDQUFDO29CQUNILENBQUM7b0JBQ0QsSUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLElBQUksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztvQkFDakQsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEdBQUcsSUFBSSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQ3BDLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBTVMsa0RBQXlCLEdBQW5DLFVBQ0ksQ0FBVSxFQUFFLFdBQW9CLEVBQUUsVUFBa0IsRUFDcEQsT0FBZTtRQUNqQixJQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25DLElBQU0sR0FBRyxHQUFHLEtBQUssR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDO1FBQ2hDLElBQU0sY0FBYyxHQUFHLFdBQVcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUMsSUFBTSxlQUFlLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN2QyxJQUFBLFlBQWdDLEVBQS9CLGFBQUssRUFBRSxhQUFLLEVBQUUsY0FBTSxDQUFZO1FBR3ZDLElBQU0sWUFBWSxHQUFHLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxHQUFHLFVBQVUsR0FBRyxDQUFDLENBQUM7UUFDbEQsSUFBTSxZQUFZLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsVUFBVSxHQUFHLENBQUMsQ0FBQztRQUVsRCxJQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsb0JBQW9CLENBQzlDLENBQUMsWUFBWSxFQUFFLFlBQVksRUFBRSxlQUFlLENBQUMsRUFBRSxLQUFLLEVBQUUsY0FBYyxFQUFFLENBQUMsRUFDdkUsR0FBRyxDQUFDLENBQUM7UUFDVCxJQUFNLENBQUMsR0FBRyxpQkFBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUVyQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLGNBQWMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO1lBQzNDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2dCQUN2QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztvQkFFdkMsSUFBTSxRQUFRLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQztvQkFDMUIsSUFBTSxRQUFRLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FBQztvQkFDMUIsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO29CQUNoQixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUNsQyxJQUFNLEVBQUUsR0FBRyxDQUFDLFFBQVEsR0FBRyxFQUFFLENBQUMsR0FBRyxVQUFVLENBQUM7d0JBQ3hDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLEtBQUssSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUM7NEJBQ25ELFFBQVEsQ0FBQzt3QkFDWCxDQUFDO3dCQUNELEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7NEJBQ2xDLElBQU0sRUFBRSxHQUFHLENBQUMsUUFBUSxHQUFHLEVBQUUsQ0FBQyxHQUFHLFVBQVUsQ0FBQzs0QkFDeEMsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBSSxFQUFFLElBQUksS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQztnQ0FDbkQsUUFBUSxDQUFDOzRCQUNYLENBQUM7NEJBQ0QsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxlQUFlLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztnQ0FDNUMsSUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dDQUNoQyxJQUFNLE1BQU0sR0FDUixXQUFXLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLEtBQUssR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztnQ0FDNUQsT0FBTyxJQUFJLEtBQUssR0FBRyxNQUFNLENBQUM7NEJBQzVCLENBQUM7d0JBQ0gsQ0FBQztvQkFDSCxDQUFDO29CQUNELENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7Z0JBQzdCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRUQseUNBQWdCLEdBQWhCLFVBQ0ksQ0FBVSxFQUFFLEVBQVcsRUFBRSxLQUFhLEVBQUUsTUFBYyxFQUN0RCxPQUFlO1FBQ2pCLElBQU0sVUFBVSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDOUIsSUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNoQyxJQUFNLFlBQVksR0FDZCxTQUFTLENBQUMscUJBQXFCLENBQUMsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNwRSxJQUFNLEVBQUUsR0FBRyxpQkFBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUV2QyxJQUFNLFFBQVEsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzdCLElBQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDN0IsSUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1QixJQUFNLFFBQVEsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTVCLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7WUFDbEMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQzlELElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxHQUFHLE9BQU8sR0FBRyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQztZQUVyRSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2dCQUNsQyxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQzlELElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxHQUFHLE9BQU8sR0FBRyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQztnQkFFckUsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxVQUFVLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztvQkFDdkMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxXQUFXLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzt3QkFFeEMsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO3dCQUNoQixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDOzRCQUN0QyxJQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLE1BQU0sR0FBRyxPQUFPLENBQUM7NEJBQ3RDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7Z0NBQ3RDLElBQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLE9BQU8sQ0FBQztnQ0FDdEMsT0FBTyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7NEJBQ3BELENBQUM7d0JBQ0gsQ0FBQzt3QkFDRCxFQUFFLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztvQkFDbEMsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLENBQUMsRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVELHNDQUFhLEdBQWIsVUFBYyxFQUFXO1FBQ3ZCLElBQU0sV0FBVyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDaEMsSUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1QixJQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVCLElBQU0sTUFBTSxHQUFHLElBQUksWUFBWSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzdDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsV0FBVyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7WUFDeEMsSUFBSSxHQUFHLEdBQUcsQ0FBQyxDQUFDO1lBQ1osR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztnQkFDakMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztvQkFDakMsR0FBRyxJQUFJLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDMUIsQ0FBQztZQUNILENBQUM7WUFDRCxNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDO1FBQ25CLENBQUM7UUFDRCxNQUFNLENBQUMsaUJBQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDN0IsQ0FBQztJQUVTLDBDQUFpQixHQUEzQixVQUErQyxDQUFJLEVBQUUsTUFBZ0I7UUFDbkUsSUFBTSxRQUFRLEdBQWEsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ3pDLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ25DLENBQUM7UUFDRCxJQUFNLFlBQVksR0FBRyxJQUFJLFlBQVksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUMsSUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQzdCLElBQU0sTUFBTSxHQUFHLGlCQUFPLENBQUMsSUFBSSxDQUFJLFFBQVEsRUFBRSxFQUFDLE1BQU0sRUFBRSxZQUFZLEVBQUMsQ0FBQyxDQUFDO1FBQ2pFLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ2hDLElBQU0sR0FBRyxHQUFHLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFHNUIsSUFBTSxNQUFNLEdBQWEsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQy9DLEdBQUcsQ0FBQyxDQUFDLElBQUksR0FBQyxHQUFHLENBQUMsRUFBRSxHQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxHQUFDLEVBQUUsRUFBRSxDQUFDO2dCQUN2QyxNQUFNLENBQUMsR0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzdCLENBQUM7WUFFRCxJQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzNDLFlBQVksQ0FBQyxRQUFRLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxD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LEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDM0IsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ3pCLE1BQU0sQ0FBQyxlQUFlLENBQUM7QUFDekIsQ0FBQztBQVBELHNEQU9DIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IGludGVyZmFjZSBCZW5jaG1hcmtSdW5Hcm91cCB7XG4gIG5hbWU6IHN0cmluZztcbiAgLy8gTWluIGFuZCBtYXggc3RlcHMgdG8gcnVuIHRoZSBiZW5jaG1hcmsgdGVzdCBvdmVyLlxuICBtaW46IG51bWJlcjtcbiAgbWF4OiBudW1iZXI7XG4gIC8vIFRoZSBzaXplIG9mIHRoZSBzdGVwIHRvIHRha2UgYmV0d2VlbiBiZW5jaG1hcmsgcnVucy5cbiAgc3RlcFNpemU6IG51bWJlcjtcbiAgLy8gQSB0cmFuc2Zvcm1hdGlvbiBvZiBzdGVwIHRvIHRoZSBzaXplIHBhc3NlZCB0byB0aGUgYmVuY2htYXJrIHRlc3QuXG4gIHN0ZXBUb1NpemVUcmFuc2Zvcm1hdGlvbj86IChzdGVwOiBudW1iZXIpID0+IG51bWJlcjtcbiAgYmVuY2htYXJrUnVuczogQmVuY2htYXJrUnVuW107XG59XG5cbmV4cG9ydCBjbGFzcyBCZW5jaG1hcmtSdW4ge1xuICBuYW1lOiBzdHJpbmc7XG4gIGJlbmNobWFya1Rlc3Q6IEJlbmNobWFya1Rlc3Q7XG5cbiAgY2hhcnREYXRhOiBDaGFydERhdGFbXTtcbiAgY29uc3RydWN0b3IobmFtZTogc3RyaW5nLCBiZW5jaG1hcmtUZXN0OiBCZW5jaG1hcmtUZXN0KSB7XG4gICAgdGhpcy5uYW1lID0gbmFtZTtcbiAgICB0aGlzLmJlbmNobWFya1Rlc3QgPSBiZW5jaG1hcmtUZXN0O1xuICAgIHRoaXMuY2hhcnREYXRhID0gW107XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBCZW5jaG1hcmtUZXN0IHsgKHNpemU6IG51bWJlcik6IG51bWJlcjsgfVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vLi4vc3JjL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIGNvbnZfZ3B1IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2NvbnZfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuLi8uLi9zcmMvbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHRlc3RfdXRpbCBmcm9tICcuLi8uLi9zcmMvdGVzdF91dGlsJztcblxuaW1wb3J0IHtCZW5jaG1hcmtUZXN0fSBmcm9tICcuL2JlbmNobWFyayc7XG5cbmNvbnN0IE9QX1JVTlMgPSAxMDA7XG5cbmV4cG9ydCBjb25zdCBCRU5DSE1BUktfVEVTVDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgaW5wdXRTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW3NpemUsIHNpemUsIDFdO1xuICBjb25zdCBvdXRwdXREZXB0aCA9IDE7XG4gIGNvbnN0IGZpZWxkU2l6ZSA9IDExO1xuICBjb25zdCBzdHJpZGUgPSAxO1xuICBjb25zdCB6ZXJvUGFkID0gY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKGlucHV0U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgc3RyaWRlKTtcbiAgY29uc3Qgb3V0cHV0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgICAgaW5wdXRTaGFwZVJDRCwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcblxuICBjb25zdCBpbnB1dFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGlucHV0U2hhcGVSQ0QpO1xuICBjb25zdCBvdXRwdXRUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChvdXRwdXRTaGFwZVJDRCk7XG4gIGNvbnN0IHdlaWdodHNUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgICBpbnB1dFNoYXBlUkNEWzJdLCBvdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgY29uc3QgYmlhc2VzVGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGgpO1xuXG4gIGNvbnN0IGhhc0JpYXMgPSB0cnVlO1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgcHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oY29udl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBpbnB1dFNoYXBlUkNELCBvdXRwdXREZXB0aCwgZmllbGRTaXplLCBzdHJpZGUsIHplcm9QYWQsIGhhc0JpYXMpKTtcblxuICBjb25zdCBpbnB1dFRleHR1cmUgPVxuICAgICAgZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShpbnB1dFRleFNoYXBlUkNbMF0sIGlucHV0VGV4U2hhcGVSQ1sxXSk7XG4gIGNvbnN0IHdlaWdodHNUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUod2VpZ2h0c1RleFNoYXBlUkNbMF0sIHdlaWdodHNUZXhTaGFwZVJDWzFdKTtcbiAgY29uc3QgYmlhc2VzVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGJpYXNlc1RleFNoYXBlUkNbMF0sIGJpYXNlc1RleFNoYXBlUkNbMV0pO1xuICBjb25zdCBvdXRwdXRUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUob3V0cHV0VGV4U2hhcGVSQ1swXSwgb3V0cHV0VGV4U2hhcGVSQ1sxXSk7XG5cbiAgY29uc3QgaW5wdXREYXRhID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShcbiAgICAgIGlucHV0VGV4U2hhcGVSQ1swXSAqIGlucHV0VGV4U2hhcGVSQ1sxXSwgLTEsIDEpO1xuICBjb25zdCB3ZWlnaHRzRGF0YSA9IHRlc3RfdXRpbC5yYW5kb21BcnJheUluUmFuZ2UoXG4gICAgICB3ZWlnaHRzVGV4U2hhcGVSQ1swXSAqIHdlaWdodHNUZXhTaGFwZVJDWzFdLCAtMSwgMSk7XG4gIGNvbnN0IGJpYXNlc0RhdGEgPSB0ZXN0X3V0aWwucmFuZG9tQXJyYXlJblJhbmdlKFxuICAgICAgYmlhc2VzVGV4U2hhcGVSQ1swXSAqIGJpYXNlc1RleFNoYXBlUkNbMV0sIC0xLCAxKTtcblxuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICBpbnB1dFRleHR1cmUsIGlucHV0VGV4U2hhcGVSQ1swXSwgaW5wdXRUZXhTaGFwZVJDWzFdLCBpbnB1dERhdGEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICB3ZWlnaHRzVGV4dHVyZSwgd2VpZ2h0c1RleFNoYXBlUkNbMF0sIHdlaWdodHNUZXhTaGFwZVJDWzFdLCB3ZWlnaHRzRGF0YSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgIGJpYXNlc1RleHR1cmUsIGJpYXNlc1RleFNoYXBlUkNbMF0sIGJpYXNlc1RleFNoYXBlUkNbMV0sIGJpYXNlc0RhdGEpO1xuXG4gIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgT1BfUlVOUzsgaSsrKSB7XG4gICAgY29udl9ncHUuY29udm9sdmUoXG4gICAgICAgIGdwZ3B1LCBwcm9ncmFtLCBpbnB1dFRleHR1cmUsIHdlaWdodHNUZXh0dXJlLCBiaWFzZXNUZXh0dXJlLFxuICAgICAgICBvdXRwdXRUZXh0dXJlLCBvdXRwdXRUZXhTaGFwZVJDKTtcbiAgfVxuXG4gIGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICBvdXRwdXRUZXh0dXJlLCBvdXRwdXRUZXhTaGFwZVJDWzBdLCBvdXRwdXRUZXhTaGFwZVJDWzFdKTtcbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgY29uc3QgYXZnVGltZSA9IChlbmQgLSBzdGFydCkgLyBPUF9SVU5TO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoaW5wdXRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZSh3ZWlnaHRzVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYmlhc2VzVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUob3V0cHV0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcblxuICByZXR1cm4gYXZnVGltZTtcbn07XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi8uLi9zcmMvbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0ICogYXMgY29udl9iYWNrcHJvcF9ncHUgZnJvbSAnLi4vLi4vc3JjL21hdGgvd2ViZ2wvY29udl9iYWNrcHJvcF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdGVzdF91dGlsIGZyb20gJy4uLy4uL3NyYy90ZXN0X3V0aWwnO1xuXG5pbXBvcnQge0JlbmNobWFya1Rlc3R9IGZyb20gJy4vYmVuY2htYXJrJztcblxuY29uc3QgT1BfUlVOUyA9IDEwMDtcblxuZXhwb3J0IGNvbnN0IEJFTkNITUFSS19URVNUOiBCZW5jaG1hcmtUZXN0ID0gKHNpemU6IG51bWJlcikgPT4ge1xuICBjb25zdCB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9IFtzaXplLCBzaXplLCAxXTtcbiAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gMjtcbiAgY29uc3QgZmllbGRTaXplID0gMTE7XG4gIGNvbnN0IG9yaWdTdHJpZGUgPSAxO1xuICBjb25zdCBvcmlnUGFkID0gMTtcblxuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgZ3BncHUuZW5hYmxlQXV0b21hdGljRGVidWdWYWxpZGF0aW9uKHRydWUpO1xuICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IHhTaGFwZVJDRFsyXTtcbiAgY29uc3Qgc3JjID0gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmaWVsZFNpemUsIG9yaWdJbnB1dERlcHRoLCBvcmlnU3RyaWRlLCBvcmlnUGFkLCBmYWxzZSk7XG4gIGNvbnN0IHByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKHNyYyk7XG5cbiAgLy8gVXBsb2FkIHguXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSQ0QpO1xuICBjb25zdCB4VGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSh4VGV4U2hhcGVSQ1swXSwgeFRleFNoYXBlUkNbMV0pO1xuICBjb25zdCB4RGF0YSA9XG4gICAgICB0ZXN0X3V0aWwucmFuZG9tQXJyYXlJblJhbmdlKHhUZXhTaGFwZVJDWzBdICogeFRleFNoYXBlUkNbMV0sIC0xLCAxKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKHhUZXgsIHhUZXhTaGFwZVJDWzBdLCB4VGV4U2hhcGVSQ1sxXSwgeERhdGEpO1xuXG4gIC8vIFVwbG9hZCB3ZWlnaHRzLlxuICBjb25zdCB3VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKFxuICAgICAgb3JpZ0lucHV0RGVwdGgsIG9yaWdPdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgY29uc3Qgd0RhdGEgPVxuICAgICAgdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZSh3VGV4U2hhcGVSQ1swXSAqIHdUZXhTaGFwZVJDWzFdLCAtMSwgMSk7XG4gIGNvbnN0IHdUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHdUZXhTaGFwZVJDWzBdLCB3VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZSh3VGV4LCB3VGV4U2hhcGVSQ1swXSwgd1RleFNoYXBlUkNbMV0sIHdEYXRhKTtcblxuICAvLyBGaWd1cmUgb3V0IHRoZSBvdXRwdXQgc2hhcGUgYnkgZGlsYXRpbmcgdGhlIGlucHV0LlxuICBjb25zdCBkaWxhdGVkUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVEaWxhdGVkUkMoW3hTaGFwZVJDRFswXSwgeFNoYXBlUkNEWzFdXSwgb3JpZ1N0cmlkZSk7XG4gIGNvbnN0IHBhZCA9IGZpZWxkU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICBjb25zdCByZXN1bHRTaGFwZVJDRCA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgIFtkaWxhdGVkUkNbMF0sIGRpbGF0ZWRSQ1sxXSwgb3JpZ091dHB1dERlcHRoXSwgZmllbGRTaXplLCBvcmlnSW5wdXREZXB0aCxcbiAgICAgIDEsIHBhZCk7XG5cbiAgY29uc3QgcmVzdWx0VGV4UkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHJlc3VsdFNoYXBlUkNEKTtcbiAgY29uc3QgcmVzdWx0VGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXhSQ1swXSwgcmVzdWx0VGV4UkNbMV0pO1xuXG4gIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgT1BfUlVOUzsgaSsrKSB7XG4gICAgY29udl9iYWNrcHJvcF9ncHUuY29udlRyYW5zcG9zZShcbiAgICAgICAgZ3BncHUsIHByb2dyYW0sIHhUZXgsIHdUZXgsIG51bGwsIHJlc3VsdFRleCwgcmVzdWx0VGV4UkMpO1xuICB9XG5cbiAgY29uc3QgeSA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICByZXN1bHRUZXgsIHJlc3VsdFRleFJDWzBdLCByZXN1bHRUZXhSQ1sxXSk7XG5cbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgY29uc3QgYXZnVGltZSA9IChlbmQgLSBzdGFydCkgLyBPUF9SVU5TO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZSh4VGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZSh3VGV4KTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIHJldHVybiBhdmdUaW1lO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5TWF0aENQVX0gZnJvbSAnLi4vLi4vc3JjL21hdGgvbWF0aF9jcHUnO1xuaW1wb3J0IHtBcnJheTJELCBOREFycmF5fSBmcm9tICcuLi8uLi9zcmMvbWF0aC9uZGFycmF5JztcblxuaW1wb3J0IHtCZW5jaG1hcmtUZXN0fSBmcm9tICcuL2JlbmNobWFyayc7XG5cbmNvbnN0IE9QU19QRVJfUlVOID0gMTA7XG5cbmV4cG9ydCBjb25zdCBCRU5DSE1BUktfVEVTVDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgbWF0aCA9IG5ldyBOREFycmF5TWF0aENQVSgpO1xuICBjb25zdCBhID0gTkRBcnJheS5yYW5kVW5pZm9ybTxBcnJheTJEPihbc2l6ZSwgc2l6ZV0sIC0xLCAxKTtcbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUFNfUEVSX1JVTjsgaSsrKSB7XG4gICAgbWF0aC5sb2dTdW1FeHAoYSk7XG4gIH1cbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIHJldHVybiAoZW5kIC0gc3RhcnQpIC8gT1BTX1BFUl9SVU47XG59O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi4vLi4vc3JjL21hdGgvd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBsb2dzdW1leHBfZ3B1IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2xvZ3N1bWV4cF9ncHUnO1xuaW1wb3J0ICogYXMgdGVzdF91dGlsIGZyb20gJy4uLy4uL3NyYy90ZXN0X3V0aWwnO1xuXG5pbXBvcnQge0JlbmNobWFya1Rlc3R9IGZyb20gJy4vYmVuY2htYXJrJztcblxuY29uc3QgT1BfUlVOUyA9IDEwMDtcblxuZXhwb3J0IGNvbnN0IEJFTkNITUFSS19URVNUOiBCZW5jaG1hcmtUZXN0ID0gKHNpemU6IG51bWJlcikgPT4ge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcblxuICBjb25zdCBwcm9ncmFtID1cbiAgICAgIGdwZ3B1LmNyZWF0ZVByb2dyYW0obG9nc3VtZXhwX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShzaXplLCBzaXplKSk7XG5cbiAgY29uc3QgYVRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHNpemUsIHNpemUpO1xuICBjb25zdCByZXN1bHRUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShzaXplLCBzaXplKTtcblxuICBjb25zdCBhID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShzaXplICogc2l6ZSwgLTEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHNpemUsIHNpemUsIGEpO1xuXG4gIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgT1BfUlVOUzsgaSsrKSB7XG4gICAgbG9nc3VtZXhwX2dwdS5sb2dTdW1FeHAoXG4gICAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhVGV4dHVyZSwgc2l6ZSwgc2l6ZSwgcmVzdWx0VGV4dHVyZSk7XG4gIH1cblxuICBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleHR1cmUsIHNpemUsIHNpemUpO1xuICBjb25zdCBhdmdUaW1lID0gKHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQpIC8gT1BfUlVOUztcblxuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIHJldHVybiBhdmdUaW1lO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtCZW5jaG1hcmtSdW4sIEJlbmNobWFya1J1bkdyb3VwfSBmcm9tICcuL2JlbmNobWFyayc7XG5pbXBvcnQgKiBhcyBjb252X2dwdV9iZW5jaG1hcmsgZnJvbSAnLi9jb252X2dwdV9iZW5jaG1hcmsnO1xuaW1wb3J0ICogYXMgY29udl90cmFuc3Bvc2VfZ3B1X2JlbmNobWFyayBmcm9tICcuL2NvbnZfdHJhbnNwb3NlX2dwdV9iZW5jaG1hcmsnO1xuaW1wb3J0ICogYXMgbG9nc3VtZXhwX2NwdV9iZW5jaG1hcmsgZnJvbSAnLi9sb2dzdW1leHBfY3B1X2JlbmNobWFyayc7XG5pbXBvcnQgKiBhcyBsb2dzdW1leHBfZ3B1X2JlbmNobWFyayBmcm9tICcuL2xvZ3N1bWV4cF9ncHVfYmVuY2htYXJrJztcbmltcG9ydCAqIGFzIG1heF9wb29sX2JhY2twcm9wX2dwdV9iZW5jaG1hcmsgZnJvbSAnLi9tYXhfcG9vbF9iYWNrcHJvcF9ncHVfYmVuY2htYXJrJztcbmltcG9ydCAqIGFzIG1heF9wb29sX2dwdV9iZW5jaG1hcmsgZnJvbSAnLi9tYXhfcG9vbF9ncHVfYmVuY2htYXJrJztcbmltcG9ydCAqIGFzIG11bG1hdF9jcHVfYmVuY2htYXJrIGZyb20gJy4vbXVsbWF0X2NwdV9iZW5jaG1hcmsnO1xuaW1wb3J0ICogYXMgbXVsbWF0X2dwdV9iZW5jaG1hcmsgZnJvbSAnLi9tdWxtYXRfZ3B1X2JlbmNobWFyayc7XG5pbXBvcnQgKiBhcyB0ZXhfdXRpbF9iZW5jaG1hcmsgZnJvbSAnLi90ZXhfdXRpbF9iZW5jaG1hcmsnO1xuXG5leHBvcnQgY29uc3QgQkVOQ0hNQVJLX1JVTl9HUk9VUFM6IEJlbmNobWFya1J1bkdyb3VwW10gPSBbXG4gIHtcbiAgICBuYW1lOiAnVGV4dHVyZSBlbmNvZGluZyAvIGRlY29kaW5nICh1bnBhY2tlZCB2cyBwYWNrZWQpJyxcbiAgICBtaW46IDAsXG4gICAgbWF4OiAxMDI0LFxuICAgIHN0ZXBTaXplOiA2NCxcbiAgICBzdGVwVG9TaXplVHJhbnNmb3JtYXRpb246IChzdGVwOiBudW1iZXIpID0+IE1hdGgubWF4KDEsIHN0ZXApLFxuICAgIGJlbmNobWFya1J1bnM6IFtcbiAgICAgIG5ldyBCZW5jaG1hcmtSdW4oXG4gICAgICAgICAgJ2VuY29kZV91bnBhY2tlZCcsIHRleF91dGlsX2JlbmNobWFyay5CRU5DSE1BUktfRU5DT0RFX1VOUEFDS0VEKSxcbiAgICAgIG5ldyBCZW5jaG1hcmtSdW4oXG4gICAgICAgICAgJ2VuY29kZV9wYWNrZWQnLCB0ZXhfdXRpbF9iZW5jaG1hcmsuQkVOQ0hNQVJLX0VOQ09ERV9QQUNLRUQpLFxuICAgICAgbmV3IEJlbmNobWFya1J1bihcbiAgICAgICAgICAnZGVjb2RlX3VucGFja2VkJywgdGV4X3V0aWxfYmVuY2htYXJrLkJFTkNITUFSS19ERUNPREVfVU5QQUNLRUQpLFxuICAgICAgbmV3IEJlbmNobWFya1J1bihcbiAgICAgICAgICAnZGVjb2RlX3BhY2tlZCcsIHRleF91dGlsX2JlbmNobWFyay5CRU5DSE1BUktfREVDT0RFX1BBQ0tFRClcbiAgICBdXG4gIH0sXG4gIHtcbiAgICBuYW1lOiAnTWF0cml4IE11bHRpcGxpY2F0aW9uIChDUFUgdnMgR1BVKScsXG4gICAgbWluOiAwLFxuICAgIG1heDogMTAyNCxcbiAgICBzdGVwU2l6ZTogNjQsXG4gICAgc3RlcFRvU2l6ZVRyYW5zZm9ybWF0aW9uOiAoc3RlcDogbnVtYmVyKSA9PiBNYXRoLm1heCgxLCBzdGVwKSxcbiAgICBiZW5jaG1hcmtSdW5zOiBbXG4gICAgICBuZXcgQmVuY2htYXJrUnVuKCdtdWxtYXRfZ3B1JywgbXVsbWF0X2dwdV9iZW5jaG1hcmsuQkVOQ0hNQVJLX1RFU1QpLFxuICAgICAgbmV3IEJlbmNobWFya1J1bihcbiAgICAgICAgICAnbXVsbWF0X3BhY2tlZF9ncHUnLCBtdWxtYXRfZ3B1X2JlbmNobWFyay5CRU5DSE1BUktfVEVTVF9QQUNLRUQpLFxuICAgICAgbmV3IEJlbmNobWFya1J1bignbXVsbWF0X2NwdScsIG11bG1hdF9jcHVfYmVuY2htYXJrLkJFTkNITUFSS19URVNUKVxuICAgIF0sXG4gIH0sXG4gIHtcbiAgICBuYW1lOiAnTG9nU3VtRXhwIChDUFUgdnMgR1BVKScsXG4gICAgbWluOiAwLFxuICAgIG1heDogMTAyNCxcbiAgICBzdGVwU2l6ZTogNjQsXG4gICAgc3RlcFRvU2l6ZVRyYW5zZm9ybWF0aW9uOiAoc3RlcDogbnVtYmVyKSA9PiBNYXRoLm1heCgxLCBzdGVwKSxcbiAgICBiZW5jaG1hcmtSdW5zOiBbXG4gICAgICBuZXcgQmVuY2htYXJrUnVuKCdsb2dzdW1leHBfZ3B1JywgbG9nc3VtZXhwX2dwdV9iZW5jaG1hcmsuQkVOQ0hNQVJLX1RFU1QpLFxuICAgICAgbmV3IEJlbmNobWFya1J1bignbG9nc3VtZXhwX2NwdScsIGxvZ3N1bWV4cF9jcHVfYmVuY2htYXJrLkJFTkNITUFSS19URVNUKVxuICAgIF0sXG4gIH0sXG4gIHtcbiAgICBuYW1lOiAnQ29udm9sdXRpb24gKEdQVSknLFxuICAgIG1pbjogMCxcbiAgICBtYXg6IDEwMjQsXG4gICAgc3RlcFNpemU6IDY0LFxuICAgIHN0ZXBUb1NpemVUcmFuc2Zvcm1hdGlvbjogKHN0ZXA6IG51bWJlcikgPT4gTWF0aC5tYXgoMSwgc3RlcCksXG4gICAgYmVuY2htYXJrUnVuczogW25ldyBCZW5jaG1hcmtSdW4oXG4gICAgICAgICdkMT0xLCBkMj0xLCBmPTExLCBzPTEnLCBjb252X2dwdV9iZW5jaG1hcmsuQkVOQ0hNQVJLX1RFU1QpXSxcbiAgfSxcbiAge1xuICAgIG5hbWU6ICdDb252b2x1dGlvbiBUcmFuc3Bvc2VkIChHUFUpJyxcbiAgICBtaW46IDAsXG4gICAgbWF4OiAxMDI0LFxuICAgIHN0ZXBTaXplOiA2NCxcbiAgICBzdGVwVG9TaXplVHJhbnNmb3JtYXRpb246IChzdGVwOiBudW1iZXIpID0+IE1hdGgubWF4KDEsIHN0ZXApLFxuICAgIGJlbmNobWFya1J1bnM6IFtuZXcgQmVuY2htYXJrUnVuKFxuICAgICAgICAnZDE9MSwgZDI9MSwgZj0xMSwgcz0xJywgY29udl90cmFuc3Bvc2VfZ3B1X2JlbmNobWFyay5CRU5DSE1BUktfVEVTVCldLFxuICB9LFxuICB7XG4gICAgbmFtZTogJ01heCBwb29sIChHUFUpJyxcbiAgICBtaW46IDAsXG4gICAgbWF4OiAxMDI0LFxuICAgIHN0ZXBTaXplOiA2NCxcbiAgICBzdGVwVG9TaXplVHJhbnNmb3JtYXRpb246IChzdGVwOiBudW1iZXIpID0+IE1hdGgubWF4KDEsIHN0ZXApLFxuICAgIGJlbmNobWFya1J1bnM6IFtuZXcgQmVuY2htYXJrUnVuKFxuICAgICAgICAnZDE9MSwgZDI9MSwgZj0xMSwgcz0xJyxcbiAgICAgICAgbWF4X3Bvb2xfZ3B1X2JlbmNobWFyay5NQVhfUE9PTF9CRU5DSE1BUktfVEVTVCldLFxuICB9LFxuICB7XG4gICAgbmFtZTogJ01heCBwb29sIHBvc2l0aW9ucyAoR1BVKScsXG4gICAgbWluOiAwLFxuICAgIG1heDogMTAyNCxcbiAgICBzdGVwU2l6ZTogNjQsXG4gICAgc3RlcFRvU2l6ZVRyYW5zZm9ybWF0aW9uOiAoc3RlcDogbnVtYmVyKSA9PiBNYXRoLm1heCgxLCBzdGVwKSxcbiAgICBiZW5jaG1hcmtSdW5zOiBbbmV3IEJlbmNobWFya1J1bihcbiAgICAgICAgJ2QxPTEsIGQyPTEsIGY9MTEsIHM9MScsXG4gICAgICAgIG1heF9wb29sX2dwdV9iZW5jaG1hcmsuTUFYX1BPT0xfUE9TTlNfQkVOQ0hNQVJLX1RFU1QpXSxcbiAgfSxcbiAge1xuICAgIG5hbWU6ICdNYXggcG9vbCBiYWNrcHJvcCAoR1BVKScsXG4gICAgbWluOiAwLFxuICAgIG1heDogMTAyNCxcbiAgICBzdGVwU2l6ZTogNjQsXG4gICAgc3RlcFRvU2l6ZVRyYW5zZm9ybWF0aW9uOiAoc3RlcDogbnVtYmVyKSA9PiBNYXRoLm1heCgxLCBzdGVwKSxcbiAgICBiZW5jaG1hcmtSdW5zOiBbbmV3IEJlbmNobWFya1J1bihcbiAgICAgICAgJ2QxPTEsIGQyPTEsIGY9MTEsIHM9MScsXG4gICAgICAgIG1heF9wb29sX2JhY2twcm9wX2dwdV9iZW5jaG1hcmsuQkVOQ0hNQVJLX1RFU1QpXSxcbiAgfVxuXTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICcuLi9kZW1vLWhlYWRlcic7XG5pbXBvcnQgJy4uL2RlbW8tZm9vdGVyJztcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLXVudXNlZC12YXJpYWJsZVxuaW1wb3J0IHtQb2x5bWVyRWxlbWVudCwgUG9seW1lckhUTUxFbGVtZW50fSBmcm9tICcuLi9wb2x5bWVyLXNwZWMnO1xuaW1wb3J0IHtCZW5jaG1hcmtSdW5Hcm91cH0gZnJvbSAnLi9iZW5jaG1hcmsnO1xuXG5pbXBvcnQge0JFTkNITUFSS19SVU5fR1JPVVBTfSBmcm9tICcuL21hdGgtYmVuY2htYXJrLXJ1bi1ncm91cHMnO1xuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6dmFyaWFibGUtbmFtZVxuZXhwb3J0IGxldCBNYXRoQmVuY2htYXJrUG9seW1lciA9IFBvbHltZXJFbGVtZW50KFxuICAgIHtpczogJ21hdGgtYmVuY2htYXJrJywgcHJvcGVydGllczoge2JlbmNobWFya1J1bkdyb3VwTmFtZXM6IEFycmF5fX0pO1xuXG5leHBvcnQgY2xhc3MgTWF0aEJlbmNobWFyayBleHRlbmRzIE1hdGhCZW5jaG1hcmtQb2x5bWVyIHtcbiAgLy8gUG9seW1lciBwcm9wZXJ0aWVzLlxuICBwcml2YXRlIGJlbmNobWFya1J1bkdyb3VwTmFtZXM6IHN0cmluZ1tdO1xuICBwcml2YXRlIHN0b3BNZXNzYWdlczogYm9vbGVhbltdO1xuXG4gIHJlYWR5KCkge1xuICAgIC8vIFNldCB1cCB0aGUgYmVuY2htYXJrcyBVSS5cbiAgICBjb25zdCBiZW5jaG1hcmtSdW5Hcm91cE5hbWVzOiBzdHJpbmdbXSA9IFtdO1xuICAgIHRoaXMuc3RvcE1lc3NhZ2VzID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBCRU5DSE1BUktfUlVOX0dST1VQUy5sZW5ndGg7IGkrKykge1xuICAgICAgYmVuY2htYXJrUnVuR3JvdXBOYW1lcy5wdXNoKEJFTkNITUFSS19SVU5fR1JPVVBTW2ldLm5hbWUpO1xuICAgICAgdGhpcy5zdG9wTWVzc2FnZXMucHVzaChmYWxzZSk7XG4gICAgfVxuICAgIHRoaXMuYmVuY2htYXJrUnVuR3JvdXBOYW1lcyA9IGJlbmNobWFya1J1bkdyb3VwTmFtZXM7XG5cbiAgICAvLyBJbiBhIHNldFRpbWVvdXQgdG8gbGV0IHRoZSBVSSB1cGRhdGUgYmVmb3JlIHdlIGFkZCBldmVudCBsaXN0ZW5lcnMuXG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBjb25zdCBydW5CdXR0b25zID0gdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLXRlc3QnKTtcbiAgICAgIGNvbnN0IHN0b3BCdXR0b25zID0gdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLXN0b3AnKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcnVuQnV0dG9ucy5sZW5ndGg7IGkrKykge1xuICAgICAgICBydW5CdXR0b25zW2ldLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4ge1xuICAgICAgICAgIHRoaXMucnVuQmVuY2htYXJrR3JvdXAoaSk7XG4gICAgICAgIH0pO1xuICAgICAgICBzdG9wQnV0dG9uc1tpXS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcbiAgICAgICAgICB0aGlzLnN0b3BNZXNzYWdlc1tpXSA9IHRydWU7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0sIDApO1xuICB9XG5cbiAgcHJpdmF0ZSBydW5CZW5jaG1hcmtHcm91cChiZW5jaG1hcmtSdW5Hcm91cEluZGV4OiBudW1iZXIpIHtcbiAgICBjb25zdCBiZW5jaG1hcmtSdW5Hcm91cCA9IEJFTkNITUFSS19SVU5fR1JPVVBTW2JlbmNobWFya1J1bkdyb3VwSW5kZXhdO1xuXG4gICAgY29uc3QgY2FudmFzID0gdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLXBsb3QnKVtiZW5jaG1hcmtSdW5Hcm91cEluZGV4XSBhc1xuICAgICAgICBIVE1MQ2FudmFzRWxlbWVudDtcbiAgICBjb25zdCBjb250ZXh0ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykgYXMgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEO1xuXG4gICAgY29uc3QgZGF0YXNldHM6IENoYXJ0RGF0YVNldHNbXSA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYmVuY2htYXJrUnVuR3JvdXAuYmVuY2htYXJrUnVucy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaHVlID0gTWF0aC5mbG9vcigzNjAgKiBpIC8gYmVuY2htYXJrUnVuR3JvdXAuYmVuY2htYXJrUnVucy5sZW5ndGgpO1xuICAgICAgZGF0YXNldHMucHVzaCh7XG4gICAgICAgIGRhdGE6IGJlbmNobWFya1J1bkdyb3VwLmJlbmNobWFya1J1bnNbaV0uY2hhcnREYXRhLFxuICAgICAgICBmaWxsOiBmYWxzZSxcbiAgICAgICAgbGFiZWw6IGJlbmNobWFya1J1bkdyb3VwLmJlbmNobWFya1J1bnNbaV0ubmFtZSxcbiAgICAgICAgYm9yZGVyQ29sb3I6ICdoc2woJyArIGh1ZSArICcsIDEwMCUsIDQwJSknLFxuICAgICAgICBiYWNrZ3JvdW5kQ29sb3I6ICdoc2woJyArIGh1ZSArICcsIDEwMCUsIDcwJSknLFxuICAgICAgICBwb2ludFJhZGl1czogMCxcbiAgICAgICAgcG9pbnRIaXRSYWRpdXM6IDUsXG4gICAgICAgIGJvcmRlcldpZHRoOiAxLFxuICAgICAgICBsaW5lVGVuc2lvbjogMFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgY2hhcnQgPSBuZXcgQ2hhcnQoY29udGV4dCwge1xuICAgICAgdHlwZTogJ2xpbmUnLFxuICAgICAgZGF0YToge2RhdGFzZXRzfSxcbiAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgYW5pbWF0aW9uOiB7ZHVyYXRpb246IDB9LFxuICAgICAgICByZXNwb25zaXZlOiBmYWxzZSxcbiAgICAgICAgc2NhbGVzOiB7XG4gICAgICAgICAgeEF4ZXM6IFt7XG4gICAgICAgICAgICB0eXBlOiAnbGluZWFyJyxcbiAgICAgICAgICAgIHBvc2l0aW9uOiAnYm90dG9tJyxcbiAgICAgICAgICAgIHRpY2tzOiB7XG4gICAgICAgICAgICAgIG1pbjogYmVuY2htYXJrUnVuR3JvdXAubWluLFxuICAgICAgICAgICAgICBtYXg6IGJlbmNobWFya1J1bkdyb3VwLm1heCxcbiAgICAgICAgICAgICAgc3RlcFNpemU6IGJlbmNobWFya1J1bkdyb3VwLnN0ZXBTaXplLFxuICAgICAgICAgICAgICBjYWxsYmFjazogKGxhYmVsOiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gYmVuY2htYXJrUnVuR3JvdXAuc3RlcFRvU2l6ZVRyYW5zZm9ybWF0aW9uICE9IG51bGwgP1xuICAgICAgICAgICAgICAgICAgICBiZW5jaG1hcmtSdW5Hcm91cC5zdGVwVG9TaXplVHJhbnNmb3JtYXRpb24oK2xhYmVsKSA6XG4gICAgICAgICAgICAgICAgICAgICtsYWJlbDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgICAgICB9IGFzIGFueSAgLy8gTm90ZTogdGhlIHR5cGluZ3MgZm9yIHRoaXMgYXJlIGluY29ycmVjdCwgY2FzdCBhcyBhbnkuXG4gICAgICAgICAgfV0sXG4gICAgICAgICAgeUF4ZXM6IFt7XG4gICAgICAgICAgICB0aWNrczoge1xuICAgICAgICAgICAgICBjYWxsYmFjazogKGxhYmVsLCBpbmRleCwgbGFiZWxzKSA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGxhYmVsICsgJ21zJztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9XVxuICAgICAgICB9LFxuICAgICAgICB0b29sdGlwczoge21vZGU6ICdsYWJlbCd9LFxuICAgICAgICB0aXRsZToge3RleHQ6IGJlbmNobWFya1J1bkdyb3VwLm5hbWV9XG4gICAgICB9XG4gICAgfSk7XG4gICAgY2FudmFzLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG5cbiAgICBjb25zdCBydW5NZXNzYWdlID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLW1lc3NhZ2UnKVtiZW5jaG1hcmtSdW5Hcm91cEluZGV4XSBhc1xuICAgICAgICBIVE1MRWxlbWVudDtcbiAgICBydW5NZXNzYWdlLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuXG4gICAgY29uc3QgcnVuTnVtYmVyc1RhYmxlID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLW51bWJlcnMtdGFibGUnKVtiZW5jaG1hcmtSdW5Hcm91cEluZGV4XSBhc1xuICAgICAgICBIVE1MRWxlbWVudDtcbiAgICBydW5OdW1iZXJzVGFibGUuaW5uZXJIVE1MID0gJyc7XG4gICAgcnVuTnVtYmVyc1RhYmxlLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG5cbiAgICAvLyBTZXQgdXAgdGhlIGhlYWRlciBmb3IgdGhlIHRhYmxlLlxuICAgIGNvbnN0IGhlYWRlcnMgPSBbJ3NpemUnXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJlbmNobWFya1J1bkdyb3VwLmJlbmNobWFya1J1bnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGhlYWRlcnMucHVzaChiZW5jaG1hcmtSdW5Hcm91cC5iZW5jaG1hcmtSdW5zW2ldLm5hbWUpO1xuICAgIH1cbiAgICBydW5OdW1iZXJzVGFibGUuYXBwZW5kQ2hpbGQodGhpcy5idWlsZFJ1bk51bWJlcnNSb3coaGVhZGVycykpO1xuXG4gICAgdGhpcy5ydW5CZW5jaG1hcmtTdGVwcyhcbiAgICAgICAgY2hhcnQsIGJlbmNobWFya1J1bkdyb3VwLCBiZW5jaG1hcmtSdW5Hcm91cEluZGV4LFxuICAgICAgICBiZW5jaG1hcmtSdW5Hcm91cC5taW4pO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZFJ1bk51bWJlcnNSb3codmFsdWVzOiBzdHJpbmdbXSkge1xuICAgIGNvbnN0IHJ1bk51bWJlclJvd0VsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBydW5OdW1iZXJSb3dFbGVtZW50LmNsYXNzTmFtZSA9ICdydW4tbnVtYmVycy1yb3cgbWF0aC1iZW5jaG1hcmsnO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHJ1bk51bWJlckNlbGxFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBydW5OdW1iZXJDZWxsRWxlbWVudC5jbGFzc05hbWUgPSAncnVuLW51bWJlcnMtY2VsbCBtYXRoLWJlbmNobWFyayc7XG4gICAgICBydW5OdW1iZXJDZWxsRWxlbWVudC5pbm5lclRleHQgPSB2YWx1ZXNbaV07XG4gICAgICBydW5OdW1iZXJSb3dFbGVtZW50LmFwcGVuZENoaWxkKHJ1bk51bWJlckNlbGxFbGVtZW50KTtcbiAgICB9XG4gICAgcmV0dXJuIHJ1bk51bWJlclJvd0VsZW1lbnQ7XG4gIH1cblxuICBwcml2YXRlIHJ1bkJlbmNobWFya1N0ZXBzKFxuICAgICAgY2hhcnQ6IENoYXJ0LCBiZW5jaG1hcmtSdW5Hcm91cDogQmVuY2htYXJrUnVuR3JvdXAsXG4gICAgICBiZW5jaG1hcmtSdW5Hcm91cEluZGV4OiBudW1iZXIsIHN0ZXA6IG51bWJlcikge1xuICAgIGNvbnN0IHJ1bk51bWJlcnNUYWJsZSA9XG4gICAgICAgIHRoaXMucXVlcnlTZWxlY3RvckFsbCgnLnJ1bi1udW1iZXJzLXRhYmxlJylbYmVuY2htYXJrUnVuR3JvdXBJbmRleF0gYXNcbiAgICAgICAgSFRNTEVsZW1lbnQ7XG4gICAgaWYgKHN0ZXAgPiBiZW5jaG1hcmtSdW5Hcm91cC5tYXggfHxcbiAgICAgICAgdGhpcy5zdG9wTWVzc2FnZXNbYmVuY2htYXJrUnVuR3JvdXBJbmRleF0pIHtcbiAgICAgIHRoaXMuc3RvcE1lc3NhZ2VzW2JlbmNobWFya1J1bkdyb3VwSW5kZXhdID0gZmFsc2U7XG5cbiAgICAgIHJ1bk51bWJlcnNUYWJsZS5zdHlsZS5kaXNwbGF5ID0gJyc7XG5cbiAgICAgIGNvbnN0IGNhbnZhcyA9XG4gICAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yQWxsKCcucnVuLXBsb3QnKVtiZW5jaG1hcmtSdW5Hcm91cEluZGV4XSBhc1xuICAgICAgICAgIEhUTUxDYW52YXNFbGVtZW50O1xuICAgICAgY2FudmFzLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xuICAgICAgY2hhcnQudXBkYXRlKCk7XG5cbiAgICAgIGNvbnN0IHJ1bk1lc3NhZ2UgPVxuICAgICAgICAgIHRoaXMucXVlcnlTZWxlY3RvckFsbCgnLnJ1bi1tZXNzYWdlJylbYmVuY2htYXJrUnVuR3JvdXBJbmRleF0gYXNcbiAgICAgICAgICBIVE1MRWxlbWVudDtcbiAgICAgIHJ1bk1lc3NhZ2Uuc3R5bGUuZGlzcGxheSA9ICdub25lJztcblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHJ1bk51bWJlclJvd0VsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBydW5OdW1iZXJSb3dFbGVtZW50LmNsYXNzTmFtZSA9ICdydW4tbnVtYmVycy1yb3cgbWF0aC1iZW5jaG1hcmsnO1xuXG4gICAgY29uc3Qgcm93VmFsdWVzOiBzdHJpbmdbXSA9IFsnJyArIHN0ZXBdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYmVuY2htYXJrUnVuR3JvdXAuYmVuY2htYXJrUnVucy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgYmVuY2htYXJrUnVuID0gYmVuY2htYXJrUnVuR3JvdXAuYmVuY2htYXJrUnVuc1tpXTtcbiAgICAgIGNvbnN0IGJlbmNobWFya1Rlc3QgPSBiZW5jaG1hcmtSdW4uYmVuY2htYXJrVGVzdDtcblxuICAgICAgY29uc3Qgc2l6ZSA9IGJlbmNobWFya1J1bkdyb3VwLnN0ZXBUb1NpemVUcmFuc2Zvcm1hdGlvbiAhPSBudWxsID9cbiAgICAgICAgICBiZW5jaG1hcmtSdW5Hcm91cC5zdGVwVG9TaXplVHJhbnNmb3JtYXRpb24oc3RlcCkgOlxuICAgICAgICAgIHN0ZXA7XG5cbiAgICAgIGxldCByZXN1bHRTdHJpbmc6IHN0cmluZztcbiAgICAgIGxldCBsb2dTdHJpbmc6IHN0cmluZztcbiAgICAgIGxldCB0aW1lID0gMDtcbiAgICAgIGxldCBzdWNjZXNzID0gdHJ1ZTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgdGltZSA9IGJlbmNobWFya1Rlc3Qoc2l6ZSk7XG4gICAgICAgIHJlc3VsdFN0cmluZyA9IHRpbWUudG9GaXhlZCgzKSArICdtcyc7XG4gICAgICAgIGxvZ1N0cmluZyA9IHJlc3VsdFN0cmluZztcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgc3VjY2VzcyA9IGZhbHNlO1xuICAgICAgICByZXN1bHRTdHJpbmcgPSAnRXJyb3InO1xuICAgICAgICBsb2dTdHJpbmcgPSBlLm1lc3NhZ2U7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aW1lID49IDApIHtcbiAgICAgICAgaWYgKHN1Y2Nlc3MpIHtcbiAgICAgICAgICBiZW5jaG1hcmtSdW4uY2hhcnREYXRhLnB1c2goe3g6IHN0ZXAsIHk6IHRpbWV9KTtcbiAgICAgICAgfVxuICAgICAgICByb3dWYWx1ZXMucHVzaChyZXN1bHRTdHJpbmcpO1xuICAgICAgfVxuICAgICAgY29uc29sZS5sb2coYmVuY2htYXJrUnVuLm5hbWUgKyAnWycgKyBzdGVwICsgJ106ICcgKyBsb2dTdHJpbmcpO1xuICAgIH1cbiAgICBydW5OdW1iZXJzVGFibGUuYXBwZW5kQ2hpbGQodGhpcy5idWlsZFJ1bk51bWJlcnNSb3cocm93VmFsdWVzKSk7XG5cbiAgICBzdGVwICs9IGJlbmNobWFya1J1bkdyb3VwLnN0ZXBTaXplO1xuICAgIC8vIEFsbG93IHRoZSBVSSB0byB1cGRhdGUuXG4gICAgc2V0VGltZW91dChcbiAgICAgICAgKCkgPT4gdGhpcy5ydW5CZW5jaG1hcmtTdGVwcyhcbiAgICAgICAgICAgIGNoYXJ0LCBiZW5jaG1hcmtSdW5Hcm91cCwgYmVuY2htYXJrUnVuR3JvdXBJbmRleCwgc3RlcCksXG4gICAgICAgIDEwMCk7XG4gIH1cbn1cbmRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChNYXRoQmVuY2htYXJrLnByb3RvdHlwZS5pcywgTWF0aEJlbmNobWFyayk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi8uLi9zcmMvbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgbWF4X3Bvb2xfYmFja3Byb3BfZ3B1IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdSc7XG5pbXBvcnQgKiBhcyB0ZXN0X3V0aWwgZnJvbSAnLi4vLi4vc3JjL3Rlc3RfdXRpbCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3NyYy91dGlsJztcblxuaW1wb3J0IHtCZW5jaG1hcmtUZXN0fSBmcm9tICcuL2JlbmNobWFyayc7XG5cbmNvbnN0IE9QX1JVTlMgPSAxMDA7XG5cbmV4cG9ydCBjb25zdCBCRU5DSE1BUktfVEVTVDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgZHlTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW3NpemUsIHNpemUsIDFdO1xuICBjb25zdCBvdXRwdXREZXB0aCA9IDE7XG4gIGNvbnN0IGZpZWxkU2l6ZSA9IDExO1xuICBjb25zdCBzdHJpZGUgPSAxO1xuICBjb25zdCB6ZXJvUGFkID0gY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKGR5U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgc3RyaWRlKTtcbiAgY29uc3Qgb3V0cHV0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgICAgZHlTaGFwZVJDRCwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcblxuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuICBjb25zdCBvdXRwdXRUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChvdXRwdXRTaGFwZVJDRCk7XG5cbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKFxuICAgICAgbWF4X3Bvb2xfYmFja3Byb3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbEJhY2twcm9wKFxuICAgICAgICAgIGR5U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgc3RyaWRlLCB6ZXJvUGFkKSk7XG5cbiAgY29uc3QgZHlUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShkeVRleFNoYXBlUkNbMF0sIGR5VGV4U2hhcGVSQ1sxXSk7XG4gIGNvbnN0IG1heFBvc2l0aW9uc1RleHR1cmUgPVxuICAgICAgZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShkeVRleFNoYXBlUkNbMF0sIGR5VGV4U2hhcGVSQ1sxXSk7XG4gIGNvbnN0IG91dHB1dFRleHR1cmUgPVxuICAgICAgZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShvdXRwdXRUZXhTaGFwZVJDWzBdLCBvdXRwdXRUZXhTaGFwZVJDWzFdKTtcblxuICBjb25zdCBkeURhdGEgPVxuICAgICAgdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShkeVRleFNoYXBlUkNbMF0gKiBkeVRleFNoYXBlUkNbMV0sIC0xLCAxKTtcbiAgY29uc3QgbWF4UG9zaXRpb25zRGF0YSA9IG5ldyBGbG9hdDMyQXJyYXkodXRpbC5zaXplRnJvbVNoYXBlKGR5U2hhcGVSQ0QpKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtYXhQb3NpdGlvbnNEYXRhLmxlbmd0aDsgaSsrKSB7XG4gICAgbWF4UG9zaXRpb25zRGF0YVtpXSA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGZpZWxkU2l6ZSAqIGZpZWxkU2l6ZSk7XG4gIH1cblxuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICBkeVRleHR1cmUsIGR5VGV4U2hhcGVSQ1swXSwgZHlUZXhTaGFwZVJDWzFdLCBkeURhdGEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICBtYXhQb3NpdGlvbnNUZXh0dXJlLCBkeVRleFNoYXBlUkNbMF0sIGR5VGV4U2hhcGVSQ1sxXSwgbWF4UG9zaXRpb25zRGF0YSk7XG5cbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUF9SVU5TOyBpKyspIHtcbiAgICBtYXhfcG9vbF9iYWNrcHJvcF9ncHUubWF4UG9vbEJhY2twcm9wKFxuICAgICAgICBncGdwdSwgcHJvZ3JhbSwgZHlUZXh0dXJlLCBtYXhQb3NpdGlvbnNUZXh0dXJlLCBvdXRwdXRUZXh0dXJlLFxuICAgICAgICBvdXRwdXRUZXhTaGFwZVJDKTtcbiAgfVxuXG4gIGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICBvdXRwdXRUZXh0dXJlLCBvdXRwdXRUZXhTaGFwZVJDWzBdLCBvdXRwdXRUZXhTaGFwZVJDWzFdKTtcbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgY29uc3QgYXZnVGltZSA9IChlbmQgLSBzdGFydCkgLyBPUF9SVU5TO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoZHlUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShtYXhQb3NpdGlvbnNUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShvdXRwdXRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIHJldHVybiBhdmdUaW1lO1xufTsiLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi8uLi9zcmMvbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgbWF4X3Bvb2xfZ3B1IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL21heF9wb29sX2dwdSc7XG5pbXBvcnQgKiBhcyB0ZXN0X3V0aWwgZnJvbSAnLi4vLi4vc3JjL3Rlc3RfdXRpbCc7XG5cbmltcG9ydCB7QmVuY2htYXJrVGVzdH0gZnJvbSAnLi9iZW5jaG1hcmsnO1xuXG5jb25zdCBPUF9SVU5TID0gMTAwO1xuXG5leHBvcnQgY29uc3QgTUFYX1BPT0xfQkVOQ0hNQVJLX1RFU1Q6IEJlbmNobWFya1Rlc3QgPSAoc2l6ZTogbnVtYmVyKSA9PiB7XG4gIGNvbnN0IGlucHV0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9IFtzaXplLCBzaXplLCAxXTtcbiAgY29uc3Qgb3V0cHV0RGVwdGggPSAxO1xuICBjb25zdCBmaWVsZFNpemUgPSAxMTtcbiAgY29uc3Qgc3RyaWRlID0gMTtcbiAgY29uc3QgemVyb1BhZCA9IGNvbnZfdXRpbC5jb21wdXRlRGVmYXVsdFBhZChpbnB1dFNoYXBlUkNELCBmaWVsZFNpemUsIHN0cmlkZSk7XG4gIGNvbnN0IG91dHB1dFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgIGlucHV0U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG5cbiAgY29uc3QgaW5wdXRUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChpbnB1dFNoYXBlUkNEKTtcbiAgY29uc3Qgb3V0cHV0VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0Qob3V0cHV0U2hhcGVSQ0QpO1xuXG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtID1cbiAgICAgIGdwZ3B1LmNyZWF0ZVByb2dyYW0obWF4X3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFNvdXJjZShcbiAgICAgICAgICBpbnB1dFNoYXBlUkNELCBmaWVsZFNpemUsIHN0cmlkZSwgemVyb1BhZCkpO1xuXG4gIGNvbnN0IGlucHV0VGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGlucHV0VGV4U2hhcGVSQ1swXSwgaW5wdXRUZXhTaGFwZVJDWzFdKTtcbiAgY29uc3Qgb3V0cHV0VGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKG91dHB1dFRleFNoYXBlUkNbMF0sIG91dHB1dFRleFNoYXBlUkNbMV0pO1xuXG4gIGNvbnN0IGlucHV0RGF0YSA9IHRlc3RfdXRpbC5yYW5kb21BcnJheUluUmFuZ2UoXG4gICAgICBpbnB1dFRleFNoYXBlUkNbMF0gKiBpbnB1dFRleFNoYXBlUkNbMV0sIC0xLCAxKTtcblxuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICBpbnB1dFRleHR1cmUsIGlucHV0VGV4U2hhcGVSQ1swXSwgaW5wdXRUZXhTaGFwZVJDWzFdLCBpbnB1dERhdGEpO1xuXG4gIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgT1BfUlVOUzsgaSsrKSB7XG4gICAgbWF4X3Bvb2xfZ3B1Lm1heFBvb2xDb21tb24oXG4gICAgICAgIGdwZ3B1LCBwcm9ncmFtLCBpbnB1dFRleHR1cmUsIG91dHB1dFRleHR1cmUsIG91dHB1dFRleFNoYXBlUkMpO1xuICB9XG5cbiAgZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgIG91dHB1dFRleHR1cmUsIG91dHB1dFRleFNoYXBlUkNbMF0sIG91dHB1dFRleFNoYXBlUkNbMV0pO1xuICBjb25zdCBlbmQgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuICBjb25zdCBhdmdUaW1lID0gKGVuZCAtIHN0YXJ0KSAvIE9QX1JVTlM7XG5cbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShpbnB1dFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKG91dHB1dFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG5cbiAgcmV0dXJuIGF2Z1RpbWU7XG59O1xuXG5leHBvcnQgY29uc3QgTUFYX1BPT0xfUE9TTlNfQkVOQ0hNQVJLX1RFU1Q6IEJlbmNobWFya1Rlc3QgPSAoc2l6ZTogbnVtYmVyKSA9PiB7XG4gIGNvbnN0IGlucHV0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9IFtzaXplLCBzaXplLCAxXTtcbiAgY29uc3Qgb3V0cHV0RGVwdGggPSAxO1xuICBjb25zdCBmaWVsZFNpemUgPSAxMTtcbiAgY29uc3Qgc3RyaWRlID0gMTtcbiAgY29uc3QgemVyb1BhZCA9IGNvbnZfdXRpbC5jb21wdXRlRGVmYXVsdFBhZChpbnB1dFNoYXBlUkNELCBmaWVsZFNpemUsIHN0cmlkZSk7XG4gIGNvbnN0IG91dHB1dFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgIGlucHV0U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG5cbiAgY29uc3QgaW5wdXRUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChpbnB1dFNoYXBlUkNEKTtcbiAgY29uc3Qgb3V0cHV0VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0Qob3V0cHV0U2hhcGVSQ0QpO1xuXG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPVxuICAgICAgZ3BncHUuY3JlYXRlUHJvZ3JhbShtYXhfcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sUG9zaXRpb25zU291cmNlKFxuICAgICAgICAgIGlucHV0U2hhcGVSQ0QsIGZpZWxkU2l6ZSwgc3RyaWRlLCB6ZXJvUGFkKSk7XG5cbiAgY29uc3QgaW5wdXRUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoaW5wdXRUZXhTaGFwZVJDWzBdLCBpbnB1dFRleFNoYXBlUkNbMV0pO1xuICBjb25zdCBvdXRwdXRUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUob3V0cHV0VGV4U2hhcGVSQ1swXSwgb3V0cHV0VGV4U2hhcGVSQ1sxXSk7XG5cbiAgY29uc3QgaW5wdXREYXRhID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShcbiAgICAgIGlucHV0VGV4U2hhcGVSQ1swXSAqIGlucHV0VGV4U2hhcGVSQ1sxXSwgLTEsIDEpO1xuXG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgIGlucHV0VGV4dHVyZSwgaW5wdXRUZXhTaGFwZVJDWzBdLCBpbnB1dFRleFNoYXBlUkNbMV0sIGlucHV0RGF0YSk7XG5cbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUF9SVU5TOyBpKyspIHtcbiAgICBtYXhfcG9vbF9ncHUubWF4UG9vbENvbW1vbihcbiAgICAgICAgZ3BncHUsIHByb2dyYW0sIGlucHV0VGV4dHVyZSwgb3V0cHV0VGV4dHVyZSwgb3V0cHV0VGV4U2hhcGVSQyk7XG4gIH1cblxuICBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKFxuICAgICAgb3V0cHV0VGV4dHVyZSwgb3V0cHV0VGV4U2hhcGVSQ1swXSwgb3V0cHV0VGV4U2hhcGVSQ1sxXSk7XG4gIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gIGNvbnN0IGF2Z1RpbWUgPSAoZW5kIC0gc3RhcnQpIC8gT1BfUlVOUztcblxuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGlucHV0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUob3V0cHV0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcblxuICByZXR1cm4gYXZnVGltZTtcbn07IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRoQ1BVfSBmcm9tICcuLi8uLi9zcmMvbWF0aC9tYXRoX2NwdSc7XG5pbXBvcnQge0FycmF5MkQsIE5EQXJyYXl9IGZyb20gJy4uLy4uL3NyYy9tYXRoL25kYXJyYXknO1xuXG5pbXBvcnQge0JlbmNobWFya1Rlc3R9IGZyb20gJy4vYmVuY2htYXJrJztcblxuY29uc3QgT1BTX1BFUl9TTUFMTF9SVU4gPSAxMDtcblxuZXhwb3J0IGNvbnN0IEJFTkNITUFSS19URVNUOiBCZW5jaG1hcmtUZXN0ID0gKHNpemU6IG51bWJlcikgPT4ge1xuICBpZiAoc2l6ZSA+IDUxMikge1xuICAgIHJldHVybiAtMTtcbiAgfVxuICBjb25zdCBtYXRoID0gbmV3IE5EQXJyYXlNYXRoQ1BVKCk7XG4gIGNvbnN0IGEgPSBOREFycmF5LnJhbmRVbmlmb3JtPEFycmF5MkQ+KFtzaXplLCBzaXplXSwgLTEsIDEpO1xuICBjb25zdCBiID0gTkRBcnJheS5yYW5kVW5pZm9ybTxBcnJheTJEPihbc2l6ZSwgc2l6ZV0sIC0xLCAxKTtcbiAgY29uc3QgcnVucyA9IChzaXplIDwgMTkyKSA/IE9QU19QRVJfU01BTExfUlVOIDogMTtcbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBydW5zOyBpKyspIHtcbiAgICBtYXRoLm1hdE11bChhLCBiKTtcbiAgfVxuICBjb25zdCBlbmQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgcmV0dXJuIChlbmQgLSBzdGFydCkgLyBydW5zO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vLi4vc3JjL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MkR9IGZyb20gJy4uLy4uL3NyYy9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgbXVsbWF0X2dwdSBmcm9tICcuLi8uLi9zcmMvbWF0aC93ZWJnbC9tdWxtYXRfZ3B1JztcbmltcG9ydCAqIGFzIG11bG1hdF9wYWNrZWRfZ3B1IGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL211bG1hdF9wYWNrZWRfZ3B1JztcbmltcG9ydCAqIGFzIHRlc3RfdXRpbCBmcm9tICcuLi8uLi9zcmMvdGVzdF91dGlsJztcblxuaW1wb3J0IHtCZW5jaG1hcmtUZXN0fSBmcm9tICcuL2JlbmNobWFyayc7XG5cbmNvbnN0IE9QX1JVTlMgPSAxMDA7XG5cbmV4cG9ydCBjb25zdCBCRU5DSE1BUktfVEVTVDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IGFUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShzaXplLCBzaXplKTtcbiAgY29uc3QgYlRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHNpemUsIHNpemUpO1xuICBjb25zdCByZXN1bHRUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShzaXplLCBzaXplKTtcblxuICBjb25zdCBhQXJyID0gbmV3IEFycmF5MkQoXG4gICAgICBbc2l6ZSwgc2l6ZV0sIHt0ZXh0dXJlOiBhVGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IFtzaXplLCBzaXplXX0pO1xuICBjb25zdCBiQXJyID0gbmV3IEFycmF5MkQoXG4gICAgICBbc2l6ZSwgc2l6ZV0sIHt0ZXh0dXJlOiBiVGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IFtzaXplLCBzaXplXX0pO1xuICBjb25zdCByZXNBcnIgPSBuZXcgQXJyYXkyRChcbiAgICAgIFtzaXplLCBzaXplXSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiBbc2l6ZSwgc2l6ZV19KTtcbiAgY29uc3QgcHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0obXVsbWF0X2dwdS5nZXRGcmFnbWVudFNoYWRlcihcbiAgICAgIGFBcnIsIGJBcnIsIHJlc0FyciwgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICAgIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpKTtcblxuICBjb25zdCBhID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShzaXplICogc2l6ZSwgLTEsIDEpO1xuICBjb25zdCBiID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShzaXplICogc2l6ZSwgLTEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHNpemUsIHNpemUsIGEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYlRleHR1cmUsIHNpemUsIHNpemUsIGIpO1xuXG4gIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgT1BfUlVOUzsgaSsrKSB7XG4gICAgbXVsbWF0X2dwdS5tdWx0aXBseU1hdHJpeChcbiAgICAgICAgZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCBiVGV4dHVyZSwgcmVzdWx0VGV4dHVyZSwgW3NpemUsIHNpemVdKTtcbiAgfVxuXG4gIGNvbnN0IGFjdHVhbCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUocmVzdWx0VGV4dHVyZSwgc2l6ZSwgc2l6ZSk7XG4gIGNvbnN0IGF2Z1RpbWUgPSAocGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydCkgLyBPUF9SVU5TO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGJUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIGNvbnN0IGV4cGVjdGVkID0gdGVzdF91dGlsLmNwdU11bHRpcGx5TWF0cml4KGEsIHNpemUsIHNpemUsIGIsIHNpemUsIHNpemUpO1xuICB0ZXN0X3V0aWwuZXhwZWN0QXJyYXlzQ2xvc2UoYWN0dWFsLCBleHBlY3RlZCwgMC4wMDEpO1xuICByZXR1cm4gYXZnVGltZTtcbn07XG5cbmV4cG9ydCBjb25zdCBCRU5DSE1BUktfVEVTVF9QQUNLRUQ6IEJlbmNobWFya1Rlc3QgPSAoc2l6ZTogbnVtYmVyKSA9PiB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPVxuICAgICAgZ3BncHUuY3JlYXRlUHJvZ3JhbShtdWxtYXRfcGFja2VkX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICBzaXplLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSk7XG5cbiAgY29uc3QgYVRleHR1cmUgPSBncGdwdS5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHNpemUsIHNpemUpO1xuICBjb25zdCBiVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZVBhY2tlZE1hdHJpeFRleHR1cmUoc2l6ZSwgc2l6ZSk7XG4gIGNvbnN0IHJlc3VsdFRleHR1cmUgPSBncGdwdS5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHNpemUsIHNpemUpO1xuXG4gIGNvbnN0IGEgPSB0ZXN0X3V0aWwucmFuZG9tQXJyYXlJblJhbmdlKHNpemUgKiBzaXplLCAtMSwgMSk7XG4gIGNvbnN0IGIgPSB0ZXN0X3V0aWwucmFuZG9tQXJyYXlJblJhbmdlKHNpemUgKiBzaXplLCAtMSwgMSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShhVGV4dHVyZSwgc2l6ZSwgc2l6ZSwgYSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShiVGV4dHVyZSwgc2l6ZSwgc2l6ZSwgYik7XG5cbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUF9SVU5TOyBpKyspIHtcbiAgICBtdWxtYXRfcGFja2VkX2dwdS5tdWx0aXBseU1hdHJpeFBhY2tlZChcbiAgICAgICAgZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCBiVGV4dHVyZSwgcmVzdWx0VGV4dHVyZSwgW3NpemUsIHNpemVdKTtcbiAgfVxuXG4gIGNvbnN0IGFjdHVhbCA9XG4gICAgICBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21QYWNrZWRUZXh0dXJlKHJlc3VsdFRleHR1cmUsIHNpemUsIHNpemUpO1xuICBjb25zdCBhdmdUaW1lID0gKHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQpIC8gT1BfUlVOUztcblxuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcblxuICBjb25zdCBleHBlY3RlZCA9IHRlc3RfdXRpbC5jcHVNdWx0aXBseU1hdHJpeChhLCBzaXplLCBzaXplLCBiLCBzaXplLCBzaXplKTtcbiAgdGVzdF91dGlsLmV4cGVjdEFycmF5c0Nsb3NlKGFjdHVhbCwgZXhwZWN0ZWQsIDAuMDAxKTtcbiAgcmV0dXJuIGF2Z1RpbWU7XG59O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBncGdwdV91dGlsIGZyb20gJy4uLy4uL3NyYy9tYXRoL3dlYmdsL2dwZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgdGV4X3V0aWwgZnJvbSAnLi4vLi4vc3JjL21hdGgvd2ViZ2wvdGV4X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuLi8uLi9zcmMvbWF0aC93ZWJnbC93ZWJnbF91dGlsJztcbmltcG9ydCAqIGFzIHRlc3RfdXRpbCBmcm9tICcuLi8uLi9zcmMvdGVzdF91dGlsJztcblxuaW1wb3J0IHtCZW5jaG1hcmtUZXN0fSBmcm9tICcuL2JlbmNobWFyayc7XG5cbmNvbnN0IE9QU19QRVJfUlVOID0gMTAwO1xuXG5leHBvcnQgY29uc3QgQkVOQ0hNQVJLX0VOQ09ERV9VTlBBQ0tFRDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgbWF0cml4ID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShzaXplICogc2l6ZSwgLTEsIDEpO1xuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPSB3ZWJnbF91dGlsLmdldENoYW5uZWxzUGVyVGV4dHVyZSgpO1xuICBjb25zdCB1bnBhY2tlZEFycmF5ID1cbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkodGV4X3V0aWwuZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICAgICAgICBtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpKTtcbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUFNfUEVSX1JVTjsgKytpKSB7XG4gICAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9VbnBhY2tlZEFycmF5KFxuICAgICAgICBtYXRyaXgsIHVucGFja2VkQXJyYXksIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIH1cbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIHJldHVybiAoZW5kIC0gc3RhcnQpIC8gT1BTX1BFUl9SVU47XG59O1xuXG5leHBvcnQgY29uc3QgQkVOQ0hNQVJLX0VOQ09ERV9QQUNLRUQ6IEJlbmNobWFya1Rlc3QgPSAoc2l6ZTogbnVtYmVyKSA9PiB7XG4gIGNvbnN0IG1hdHJpeCA9IHRlc3RfdXRpbC5yYW5kb21BcnJheUluUmFuZ2Uoc2l6ZSAqIHNpemUsIC0xLCAxKTtcbiAgY29uc3QgcGFja2VkUkdCQSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRSR0JBQXJyYXlTaXplRnJvbU1hdHJpeFNoYXBlKHNpemUsIHNpemUpKTtcbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBPUFNfUEVSX1JVTjsgKytpKSB7XG4gICAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9QYWNrZWRSR0JBKG1hdHJpeCwgc2l6ZSwgc2l6ZSwgcGFja2VkUkdCQSk7XG4gIH1cbiAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gIHJldHVybiAoZW5kIC0gc3RhcnQpIC8gT1BTX1BFUl9SVU47XG59O1xuXG5leHBvcnQgY29uc3QgQkVOQ0hNQVJLX0RFQ09ERV9VTlBBQ0tFRDogQmVuY2htYXJrVGVzdCA9IChzaXplOiBudW1iZXIpID0+IHtcbiAgY29uc3QgbWF0cml4ID0gdGVzdF91dGlsLnJhbmRvbUFycmF5SW5SYW5nZShzaXplICogc2l6ZSwgLTEsIDEpO1xuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPSB3ZWJnbF91dGlsLmdldENoYW5uZWxzUGVyVGV4dHVyZSgpO1xuICBjb25zdCB1bnBhY2tlZEFycmF5ID1cbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkodGV4X3V0aWwuZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICAgICAgICBtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpKTtcbiAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9VbnBhY2tlZEFycmF5KFxuICAgICAgbWF0cml4LCB1bnBhY2tlZEFycmF5LCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IE9QU19QRVJfUlVOOyArK2kpIHtcbiAgICB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICAgICAgdW5wYWNrZWRBcnJheSwgbWF0cml4LCBjaGFubmVsc1BlclRleHR1cmUpO1xuICB9XG4gIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICByZXR1cm4gKGVuZCAtIHN0YXJ0KSAvIE9QU19QRVJfUlVOO1xufTtcblxuZXhwb3J0IGNvbnN0IEJFTkNITUFSS19ERUNPREVfUEFDS0VEOiBCZW5jaG1hcmtUZXN0ID0gKHNpemU6IG51bWJlcikgPT4ge1xuICBjb25zdCBtYXRyaXggPSB0ZXN0X3V0aWwucmFuZG9tQXJyYXlJblJhbmdlKHNpemUgKiBzaXplLCAtMSwgMSk7XG4gIGNvbnN0IHBhY2tlZFJHQkEgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShzaXplLCBzaXplKSk7XG4gIHRleF91dGlsLmVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShtYXRyaXgsIHNpemUsIHNpemUsIHBhY2tlZFJHQkEpO1xuICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IE9QU19QRVJfUlVOOyArK2kpIHtcbiAgICB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tUGFja2VkUkdCQShwYWNrZWRSR0JBLCBzaXplLCBzaXplLCBtYXRyaXgpO1xuICB9XG4gIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICByZXR1cm4gKGVuZCAtIHN0YXJ0KSAvIE9QU19QRVJfUlVOO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblBvbHltZXIoe2lzOiAnZGVtby1mb290ZXInfSk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5Qb2x5bWVyKHtpczogJ2RlbW8taGVhZGVyJ30pO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vKipcbiAqIEBmaWxlb3ZlcnZpZXdcbiAqXG4gKiBEZWZpbmVzIGFuIGludGVyZmFjZSBmb3IgY3JlYXRpbmcgUG9seW1lciBlbGVtZW50cyBpbiBUeXBlc2NyaXB0IHdpdGggdGhlXG4gKiBjb3JyZWN0IHR5cGluZ3MuIEEgUG9seW1lciBlbGVtZW50IHNob3VsZCBiZSBkZWZpbmVkIGxpa2UgdGhpczpcbiAqXG4gKiBgYGBcbiAqIGxldCBNeUVsZW1lbnRQb2x5bWVyID0gUG9seW1lckVsZW1lbnQoe1xuICogICBpczogJ215LXBvbHltZXItZWxlbWVudCcsXG4gKiAgIHByb3BlcnRpZXM6IHtcbiAqICAgICBmb286IHN0cmluZyxcbiAqICAgICBiYXI6IEFycmF5XG4gKiAgIH1cbiAqIH0pO1xuICpcbiAqIGNsYXNzIE15RWxlbWVudCBleHRlbmRzIE15RWxlbWVudFBvbHltZXIge1xuICogICBmb286IHN0cmluZztcbiAqICAgYmFyOiBudW1iZXJbXTtcbiAqXG4gKiAgIHJlYWR5KCkge1xuICogICAgIGNvbnNvbGUubG9nKCdNeUVsZW1lbnQgaW5pdGlhbGl6ZWQhJyk7XG4gKiAgIH1cbiAqIH1cbiAqXG4gKiBkb2N1bWVudC5yZWdpc3RlckVsZW1lbnQoTXlFbGVtZW50LnByb3RvdHlwZS5pcywgTXlFbGVtZW50KTtcbiAqIGBgYFxuICovXG5cbmV4cG9ydCB0eXBlIFNwZWMgPSB7XG4gIGlzOiBzdHJpbmc7IHByb3BlcnRpZXM6IHtcbiAgICBba2V5OiBzdHJpbmddOiAoRnVuY3Rpb258e1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgdHlwZTogRnVuY3Rpb24sIHZhbHVlPzogYW55O1xuICAgICAgcmVmbGVjdFRvQXR0cmlidXRlPzogYm9vbGVhbjtcbiAgICAgIHJlYWRvbmx5PzogYm9vbGVhbjtcbiAgICAgIG5vdGlmeT86IGJvb2xlYW47XG4gICAgICBjb21wdXRlZD86IHN0cmluZztcbiAgICAgIG9ic2VydmVyPzogc3RyaW5nO1xuICAgIH0pXG4gIH07XG4gIG9ic2VydmVycz86IHN0cmluZ1tdO1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIFBvbHltZXJFbGVtZW50KHNwZWM6IFNwZWMpIHtcbiAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICByZXR1cm4gUG9seW1lci5DbGFzcyhzcGVjIGFzIGFueSkgYXMge25ldyAoKTogUG9seW1lckhUTUxFbGVtZW50fTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQb2x5bWVySFRNTEVsZW1lbnQgZXh0ZW5kcyBIVE1MRWxlbWVudCwgcG9seW1lci5CYXNlIHt9XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRDb25jYXQzRFNoYXBlc01hdGNoKFxuICAgIHgxU2hhcGU6IG51bWJlcltdLCB4MlNoYXBlOiBudW1iZXJbXSwgYXhpczogbnVtYmVyLFxuICAgIGVycm9yTWVzc2FnZVByZWZpeCA9ICcnKSB7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgeDFTaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICBlcnJvck1lc3NhZ2VQcmVmaXggKyAnQ29uY2F0M0QgeDEgc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB4MlNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArICdDb25jYXQzRCB4MiBzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuXG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgYXhpcyA+PSAwICYmIGF4aXMgPCAzLCAnQXhpcyBmb3IgY29uY2F0M0QgbXVzdCBiZSBiZXR3ZWVuIDAgYW5kIDIuJyk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCAzOyBpKyspIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgKGkgPT09IGF4aXMpIHx8ICh4MVNoYXBlW2ldID09PSB4MlNoYXBlW2ldKSxcbiAgICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICtcbiAgICAgICAgICAgIGBTaGFwZSAoJHt4MVNoYXBlfSkgZG9lcyBub3QgbWF0Y2ggKCR7eDJTaGFwZX0pIGFsb25nIGAgK1xuICAgICAgICAgICAgYG5vbi1jb25jYXRlbmF0ZWQgYXhpcy5gKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoXG4gICAgeDFTaGFwZTogbnVtYmVyW10sIHgyU2hhcGU6IG51bWJlcltdLFxuICAgIGF4aXM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gIHV0aWwuYXNzZXJ0KHgxU2hhcGUubGVuZ3RoID09PSAzLCAnQ29uY2F0M0QgeDEgc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcbiAgdXRpbC5hc3NlcnQoeDJTaGFwZS5sZW5ndGggPT09IDMsICdDb25jYXQzRCB4MnNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG5cbiAgY29uc3Qgb3V0cHV0U2hhcGUgPSB4MVNoYXBlLnNsaWNlKCk7XG4gIG91dHB1dFNoYXBlW2F4aXNdICs9IHgyU2hhcGVbYXhpc107XG4gIHJldHVybiBvdXRwdXRTaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgaW5wdXRTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgIGRlcHRoOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCB6ZXJvUGFkPzogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgaWYgKHplcm9QYWQgPT0gbnVsbCkge1xuICAgIHplcm9QYWQgPSBjb21wdXRlRGVmYXVsdFBhZChpbnB1dFNoYXBlUm93Q29sRGVwdGgsIGZpZWxkU2l6ZSwgc3RyaWRlKTtcbiAgfVxuICBjb25zdCBpbnB1dFJvd3MgPSBpbnB1dFNoYXBlUm93Q29sRGVwdGhbMF07XG4gIGNvbnN0IGlucHV0Q29scyA9IGlucHV0U2hhcGVSb3dDb2xEZXB0aFsxXTtcbiAgY29uc3Qgb3V0cHV0Um93cyA9IChpbnB1dFJvd3MgLSBmaWVsZFNpemUgKyAyICogemVyb1BhZCkgLyBzdHJpZGUgKyAxO1xuICB1dGlsLmFzc2VydChcbiAgICAgIHV0aWwuaXNJbnQob3V0cHV0Um93cyksXG4gICAgICBgVGhlIG91dHB1dCAjIG9mIHJvd3MgKCR7b3V0cHV0Um93c30pIG11c3QgYmUgYW4gaW50ZWdlci4gQ2hhbmdlIHRoZSBgICtcbiAgICAgICAgICBgc3RyaWRlIGFuZC9vciB6ZXJvIHBhZCBwYXJhbWV0ZXJzYCk7XG5cbiAgY29uc3Qgb3V0cHV0Q29scyA9IChpbnB1dENvbHMgLSBmaWVsZFNpemUgKyAyICogemVyb1BhZCkgLyBzdHJpZGUgKyAxO1xuICB1dGlsLmFzc2VydChcbiAgICAgIHV0aWwuaXNJbnQob3V0cHV0Q29scyksXG4gICAgICBgVGhlIG91dHB1dCAjIG9mIGNvbHVtbnMgKCR7b3V0cHV0Q29sc30pIG11c3QgYmUgYW4gaW50ZWdlci4gQ2hhbmdlIGAgK1xuICAgICAgICAgIGB0aGUgc3RyaWRlIGFuZC9vciB6ZXJvIHBhZCBwYXJhbWV0ZXJzYCk7XG5cbiAgcmV0dXJuIFtvdXRwdXRSb3dzLCBvdXRwdXRDb2xzLCBkZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlRGVmYXVsdFBhZChcbiAgICBpbnB1dFNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgIHN0cmlkZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoKGlucHV0U2hhcGVbMF0gKiAoc3RyaWRlIC0gMSkgLSBzdHJpZGUgKyBmaWVsZFNpemUpIC8gMik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlVGV4U2hhcGVGcm9tM0QoXG4gICAgc2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbc2hhcGVSb3dDb2xEZXB0aFswXSwgc2hhcGVSb3dDb2xEZXB0aFsxXSAqIHNoYXBlUm93Q29sRGVwdGhbMl1dO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZVdlaWdodHNTaGFwZTREKFxuICAgIGlucHV0RGVwdGg6IG51bWJlciwgb3V0cHV0RGVwdGg6IG51bWJlcixcbiAgICBmU2l6ZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2ZTaXplLCBmU2l6ZSwgaW5wdXREZXB0aCwgb3V0cHV0RGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZVdlaWdodHNUZXhTaGFwZShcbiAgICBpbnB1dERlcHRoOiBudW1iZXIsIG91dHB1dERlcHRoOiBudW1iZXIsXG4gICAgZmllbGRTaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtmaWVsZFNpemUgKiBmaWVsZFNpemUgKiBpbnB1dERlcHRoLCBvdXRwdXREZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGg6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gWzEsIG91dHB1dERlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVEaWxhdGVkUkMoXG4gICAgcmM6IFtudW1iZXIsIG51bWJlcl0sIG9yaWdTdHJpZGU6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCByb3dzRGlsYXRlZCA9IChyY1swXSAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gIGNvbnN0IGNvbHNEaWxhdGVkID0gKHJjWzFdIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgcmV0dXJuIFtyb3dzRGlsYXRlZCwgY29sc0RpbGF0ZWRdO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVTaGFwZXMoXG4gICAgc291cmNlU2l6ZTogW251bWJlciwgbnVtYmVyXSwgZGVzdFNpemU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgY29uc3Qgc3JjQXJlYSA9IHNvdXJjZVNpemVbMF0gKiBzb3VyY2VTaXplWzFdO1xuICBjb25zdCBkc3RBcmVhID0gZGVzdFNpemVbMF0gKiBkZXN0U2l6ZVsxXTtcbiAgaWYgKHNyY0FyZWEgIT09IGRzdEFyZWEpIHtcbiAgICBjb25zdCBzcmNTdHIgPSAnWycgKyBzb3VyY2VTaXplWzBdICsgJywgJyArIHNvdXJjZVNpemVbMV0gKyAnXSc7XG4gICAgY29uc3QgZHN0U3RyID0gJ1snICsgZGVzdFNpemVbMF0gKyAnLCAnICsgZGVzdFNpemVbMV0gKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnY29weTJEIHNoYXBlcyBoYXZlIGRpZmZlcmVudCBhcmVhczpcXG4gIHNvdXJjZVNpemUgJyArIHNyY1N0ciArXG4gICAgICAgICcsIGFyZWEgJyArIHNyY0FyZWEgKyAnXFxuICBkZXN0U2l6ZSAnICsgZHN0U3RyICsgJywgYXJlYSAnICsgZHN0QXJlYSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyZF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuXG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IHR5cGUgU2NvcGVSZXN1bHQgPSBOREFycmF5W118TkRBcnJheXx2b2lkO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTkRBcnJheU1hdGgge1xuICBwcml2YXRlIG5kYXJyYXlTY29wZXM6IE5EQXJyYXlbXVtdID0gW107XG4gIHByaXZhdGUgYWN0aXZlU2NvcGU6IE5EQXJyYXlbXTtcblxuICBwcml2YXRlIG5kYXJyYXlzVG9LZWVwOiBOREFycmF5W11bXSA9IFtdO1xuICBwcml2YXRlIGFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXA6IE5EQXJyYXlbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gc2FmZU1vZGUgSW4gc2FmZSBtb2RlLCB5b3UgbXVzdCB1c2UgbWF0aCBvcGVyYXRpb25zIGluc2lkZVxuICAgKiBhIG1hdGguc2NvcGUoKSB3aGljaCB3aWxsIGF1dG9tYXRpY2FsbHkgY2xlYW4gdXAgaW50ZXJtZWRpYXRlIE5EQXJyYXlzLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBzYWZlTW9kZTogYm9vbGVhbikge31cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IG1hdGggc2NvcGUuIFB1dCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucyBpbnNpZGUgYSBzY29wZVxuICAgKiBmdW5jdGlvbiBjbG9zdXJlIHNvIHRoYXQgdGhlIGxpYnJhcnkgYXV0b21hdGljYWxseSBjbGVhbnMgdXAgTkRBcnJheXNcbiAgICogZnJvbSBpbnRlcm1lZGlhdGUgbWF0aCBvcGVyYXRpb25zLiBZb3UgbXVzdCBjcmVhdGUgYSBzY29wZSBpbiBzYWZlIG1vZGVcbiAgICogdG8gY2FsbCBtYXRoIG9wZXJhdGlvbnMuIElmIGEgcmVzdWx0IGlzIHJldHVybmVkIGZyb20gdGhlIHNjb3BlLCBpdCB3aWxsXG4gICAqIGFsc28gYmUgdHJhY2tlZCwgd2hpY2ggbWVhbnMgdGhlcmUgbXVzdCBiZSB5ZXQgYW5vdGhlciB3cmFwcGluZyBzY29wZS5cbiAgICogQHBhcmFtIHNjb3BlRm4gVGhlIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgd2l0aCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucy5cbiAgICovXG4gIHNjb3BlPFQgZXh0ZW5kcyBTY29wZVJlc3VsdD4oXG4gICAgICBzY29wZUZuOlxuICAgICAgICAgIChrZWVwOiA8VDEgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMSkgPT4gVDEsXG4gICAgICAgICAgIHRyYWNrOiA8VDIgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMikgPT4gVDIpID0+IFQpIHtcbiAgICB0aGlzLnN0YXJ0U2NvcGUoKTtcblxuICAgIGNvbnN0IGtlZXBGbiA9IDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQgPT4gdGhpcy5rZWVwKG5kYXJyYXkpO1xuICAgIGNvbnN0IHRyYWNrRm4gPSA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUID0+IHRoaXMudHJhY2sobmRhcnJheSk7XG4gICAgY29uc3QgcmVzdWx0ID0gc2NvcGVGbihrZWVwRm4sIHRyYWNrRm4pO1xuXG4gICAgdGhpcy5lbmRTY29wZShyZXN1bHQpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIGVuZFNjb3BlKCkgdG8gYWNoaWV2ZSB0aGUgc2FtZSBmdW5jdGlvbmFsaXR5XG4gICAqIGFzIHNjb3BlKCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgYSBmdW5jdGlvbiBjbG9zdXJlLlxuICAgKi9cbiAgc3RhcnRTY29wZSgpIHtcbiAgICBjb25zdCBuZXdTY29wZTogTkRBcnJheVtdID0gW107XG4gICAgdGhpcy5uZGFycmF5U2NvcGVzLnB1c2gobmV3U2NvcGUpO1xuICAgIHRoaXMuYWN0aXZlU2NvcGUgPSBuZXdTY29wZTtcblxuICAgIGNvbnN0IG5ld05EQXJyYXlzVG9LZWVwOiBOREFycmF5W10gPSBbXTtcbiAgICB0aGlzLm5kYXJyYXlzVG9LZWVwLnB1c2gobmV3TkRBcnJheXNUb0tlZXApO1xuICAgIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCA9IG5ld05EQXJyYXlzVG9LZWVwO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIHN0YXJ0U2NvcGUoKSB0byBhY2hpZXZlIHRoZSBzYW1lIGZ1bmN0aW9uYWxpdHlcbiAgICogYXMgc2NvcGUoKSB3aXRob3V0IHRoZSBuZWVkIGZvciBhIGZ1bmN0aW9uIGNsb3N1cmUuXG4gICAqL1xuICBlbmRTY29wZShyZXN1bHQ6IFNjb3BlUmVzdWx0KSB7XG4gICAgLy8gRGlzcG9zZSB0aGUgY3VycmVudCBzY29wZS5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuYWN0aXZlU2NvcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG5kYXJyYXkgPSB0aGlzLmFjdGl2ZVNjb3BlW2ldO1xuXG4gICAgICBpZiAodGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KG5kYXJyYXksIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCkgfHxcbiAgICAgICAgICAocmVzdWx0ICE9IG51bGwgJiYgcmVzdWx0IGluc3RhbmNlb2YgTkRBcnJheSAmJlxuICAgICAgICAgICBuZGFycmF5LmdldERhdGEoKSA9PT0gKHJlc3VsdCBhcyBOREFycmF5KS5nZXREYXRhKCkpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgbmRhcnJheS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgLy8gUG9wIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgIHRoaXMubmRhcnJheVNjb3Blcy5wb3AoKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlID0gdGhpcy5uZGFycmF5U2NvcGVzLmxlbmd0aCA9PT0gMCA/XG4gICAgICAgIG51bGwhIDpcbiAgICAgICAgdGhpcy5uZGFycmF5U2NvcGVzW3RoaXMubmRhcnJheVNjb3Blcy5sZW5ndGggLSAxXTtcblxuICAgIC8vIFRyYWNrIHRoZSBjdXJyZW50IHJlc3VsdCBpbiB0aGUgcGFyZW50IHNjb3BlLlxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBOREFycmF5ICYmXG4gICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QocmVzdWx0LCB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXApKSB7XG4gICAgICB0aGlzLnRyYWNrKHJlc3VsdCk7XG4gICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkpIHtcbiAgICAgIHJlc3VsdC5mb3JFYWNoKHIgPT4ge1xuICAgICAgICBpZiAociBpbnN0YW5jZW9mIE5EQXJyYXkgJiZcbiAgICAgICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QociwgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwKSkge1xuICAgICAgICAgIHRoaXMudHJhY2socik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMubmRhcnJheXNUb0tlZXAucG9wKCk7XG4gICAgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwID0gdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggPT09IDAgP1xuICAgICAgICBudWxsISA6XG4gICAgICAgIHRoaXMubmRhcnJheXNUb0tlZXBbdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHByaXZhdGUgaXNOREFycmF5RGF0YUluTGlzdChuZGFycmF5OiBOREFycmF5LCBuZGFycmF5TGlzdDogTkRBcnJheVtdKSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZGFycmF5TGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKG5kYXJyYXlMaXN0W2ldLmdldERhdGEoKSA9PT0gbmRhcnJheS5nZXREYXRhKCkpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBLZWVwcyBhbiBOREFycmF5IGluIHRoZSBjdXJyZW50IHNjb3BlIGZyb20gYmVpbmcgZGlzcG9zZWQgYXV0b21hdGljYWxseS5cbiAgICogQHBhcmFtIHJlc3VsdCBUaGUgTkRBcnJheSB0byBrZWVwIGZyb20gYmVpbmcgZGlzcG9zZWQuXG4gICAqL1xuICBrZWVwPFQgZXh0ZW5kcyBOREFycmF5PihyZXN1bHQ6IFQpOiBUIHtcbiAgICBpZiAodGhpcy5hY3RpdmVTY29wZSA9PSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5zYWZlTW9kZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnWW91IGFyZSB1c2luZyBtYXRoIGluIHNhZmUgbW9kZS4gRW5jbG9zZSBhbGwgJyArXG4gICAgICAgICAgICAnbWF0aC5tZXRob2QoKSBjYWxscyBpbnNpZGUgYSBzY29wZTogJyArXG4gICAgICAgICAgICAnbWF0aC5zY29wZSgoKSA9PiB7bWF0aC5tZXRob2QoKTsuLi59KSB0byBhdm9pZCBtZW1vcnkgJyArXG4gICAgICAgICAgICAnbGVha3MuJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXAucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogVHJhY2tzIGFuIE5EQXJyYXkgaW4gdGhlIGN1cnJlbnQgc2NvcGUgdG8gYmUgYXV0b21hdGljYWxseSBjbGVhbmVkIHVwIHdoZW5cbiAgICogdGhlIGN1cnJlbnQgc2NvcGUgZW5kcywgYW5kIHJldHVybnMgdGhlIHZhbHVlLlxuICAgKiBAcGFyYW0gcmVzdWx0IFRoZSBOREFycmF5IHRvIHRyYWNrIGluIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgKi9cbiAgdHJhY2s8VCBleHRlbmRzIE5EQXJyYXk+KHJlc3VsdDogVCk6IFQge1xuICAgIGlmICh0aGlzLmFjdGl2ZVNjb3BlID09IG51bGwpIHtcbiAgICAgIGlmICh0aGlzLnNhZmVNb2RlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdZb3UgYXJlIHVzaW5nIG1hdGggaW4gc2FmZSBtb2RlLiBFbmNsb3NlIGFsbCAnICtcbiAgICAgICAgICAgICdtYXRoLm1ldGhvZCgpIGNhbGxzIGluc2lkZSBhIHNjb3BlOiAnICtcbiAgICAgICAgICAgICdtYXRoLnNjb3BlKCgpID0+IHttYXRoLm1ldGhvZCgpOy4uLn0pIHRvIGF2b2lkIG1lbW9yeSAnICtcbiAgICAgICAgICAgICdsZWFrcy4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHRoaXMuYWN0aXZlU2NvcGUucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byBtYXRyaWNlcywgQSAqIEIuIFRoZXNlIG11c3QgYmUgbWF0cmljZXMsXG4gICAqIHVzZSBtYXRyaXhUaW1lc1ZlY3RvciBhbmQgdmVjdG9yVGltZXNNYXRyaXgsIGRvdFByb2R1Y3QsIGFuZCBvdXRlclByb2R1Y3RcbiAgICogaW4gb3RoZXIgY2FzZXMuXG4gICAqIEBwYXJhbSBhIEZpcnN0IG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBiIFNlY29uZCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYU9yaWVudGF0aW9uIFRoZSBNYXRyaXhPcmllbnRhdGlvbiBvZiBBLiBJZiB1c2luZyBUUkFOU1BPU0VELCB3aWxsXG4gICAqIGNvbXB1dGUgQV5UICogQi5cbiAgICogQHBhcmFtIGJPcmllbnRhdGlvbiBUaGUgTWF0cml4T3JpZW50YXRpb24gb2YgQi4gSWYgdXNpbmcgVFJBTlNQT1NFRCwgd2lsbFxuICAgKiBjb21wdXRlIEEgKiBCXlQuXG4gICAqL1xuICBtYXRNdWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEFycmF5MkQge1xuICAgIGNvbnN0IGlubmVyU2hhcGVBID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcbiAgICBjb25zdCBpbm5lclNoYXBlQiA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVswXSA6IGIuc2hhcGVbMV07XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYS5yYW5rID09PSAyICYmIGIucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIG1hdE11bDogaW5wdXRzIG11c3QgYmUgcmFuayAyLCBnb3QgcmFua3MgJHthLnJhbmt9YCArXG4gICAgICAgICAgICBgYW5kICR7Yi5yYW5rfS5gKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBpbm5lclNoYXBlQSA9PT0gaW5uZXJTaGFwZUIsXG4gICAgICAgIGBFcnJvciBpbiBtYXRNdWw6IGlubmVyIHNoYXBlcyAoJHtpbm5lclNoYXBlQX0pIGFuZCAoYCArXG4gICAgICAgICAgICBgJHtpbm5lclNoYXBlQn0pIG9mIE5EQXJyYXlzIHdpdGggc2hhcGVzICR7YS5zaGFwZX0gYW5kIGAgK1xuICAgICAgICAgICAgYCR7Yi5zaGFwZX0gYW5kIG9yaWVudGF0aW9ucyAke01hdHJpeE9yaWVudGF0aW9uW2FPcmllbnRhdGlvbl19YCArXG4gICAgICAgICAgICBgIGFuZCAke01hdHJpeE9yaWVudGF0aW9uW2JPcmllbnRhdGlvbl19IG11c3QgbWF0Y2guYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1hdE11bEludGVybmFsKGEsIGIsIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1hdE11bEludGVybmFsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbixcbiAgICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBBcnJheTJEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSB2ZWN0b3IgYW5kIGEgbWF0cml4LCB2ICogQi5cbiAgICogQHBhcmFtIHYgVGhlIHZlY3RvciBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqL1xuICB2ZWN0b3JUaW1lc01hdHJpeCh2OiBBcnJheTFELCBtYXRyaXg6IEFycmF5MkQpOiBBcnJheTFEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgcmFuayAxLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHt2LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtYXRyaXgucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCBiZSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMF0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgKCR7di5zaXplfSkgYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBpbm5lciBkaW1lbnNpb24gb2Ygc2Vjb25kIHJhbmsgMiBpbnB1dCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7bWF0cml4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYuYXMyRCgxLCB2LnNpemUpLCBtYXRyaXgpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSBtYXRyaXggYW5kIHZlY3RvciwgQSAqIHYuXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2IFRoZSB2ZWN0b3IgaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWF0cml4VGltZXNWZWN0b3IobWF0cml4OiBBcnJheTJELCB2OiBBcnJheTFEKTogQXJyYXkxRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCByYW5rIDEsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke3YucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1hdHJpeC5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgYSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgJHt2LnNpemV9IGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggaW5uZXIgZGltZW5zaW9uIG9mIHNlY29uZCByYW5rIDIgaW5wdXQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgc2hhcGUgJHttYXRyaXguc2hhcGV9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKG1hdHJpeCwgdi5hczJEKHYuc2l6ZSwgMSkpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxICogdjIuXG4gICAqIEBwYXJhbSB2MSBUaGUgZmlyc3QgdmVjdG9yIGluIHRoZSBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2MiBUaGUgc2Vjb25kIHZlY3RvciBpbiB0aGUgZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgZG90UHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB2MS5yYW5rID09PSAxICYmIHYyLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBkb3RQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEuc2l6ZSA9PT0gdjIuc2l6ZSxcbiAgICAgICAgYEVycm9yIGluIGRvdFByb2R1Y3Q6IHNpemUgb2YgaW5wdXRzICgke3YxLnNpemV9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7djIuc2l6ZX0pIG11c3QgbWF0Y2guYCk7XG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYxLmFzMkQoMSwgdjEuc2l6ZSksIHYyLmFzMkQodjIuc2l6ZSwgMSkpLmFzU2NhbGFyKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG91dGVyIHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxIGFuZCB2Mi5cbiAgICogQHBhcmFtIHYxIFRoZSBmaXJzdCB2ZWN0b3IgaW4gdGhlIG91dGVyIHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdjIgVGhlIHNlY29uZCB2ZWN0b3IgaW4gdGhlIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIG91dGVyUHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEucmFuayA9PT0gMSAmJiB2Mi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gb3V0ZXJQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bCh2MS5hczJEKHYxLnNpemUsIDEpLCB2Mi5hczJEKDEsIHYyLnNpemUpKTtcbiAgfVxuXG4gIC8vLy8vLy8vLy8vLy8vL1xuICAvLyBTaGFwZSBvcHMgLy9cbiAgLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIENsb25lcyBhbiBOREFycmF5IG9mIGFueSBzaGFwZS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIE5EQXJyYXkgdG8gY2xvbmUuXG4gICAqL1xuICBjbG9uZTxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBSZXNoYXBlcyBhbiBOREFycmF5IHRvIGEgbmV3IHNoYXBlLiBUaGUgc2l6ZSBvZiB0aGUgaW5wdXQgTkRBcnJheSBtdXN0XG4gICAqIG1hdGNoIHRoZSBzaXplIG9mIHRoZSByZXF1ZXN0ZWQgc2hhcGUuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmV3U2hhcGUgVGhlIG5ldyBzaGFwZSB0byByZXNoYXBlIHRoZSBOREFycmF5IHRvLiBNdXN0IGJlIHRoZSBzYW1lXG4gICAqIHNpemUgYXMgdGhlIE5EQXJyYXkuXG4gICAqL1xuICByZXNoYXBlPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG5kYXJyYXkuc2l6ZSA9PT0gdXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKSxcbiAgICAgICAgYEVycm9yIGluIHJlc2hhcGU6IG9sZCBzaXplICR7bmRhcnJheS5zaXplfSBtdXN0IG1hdGNoIG5ldyBzaXplIGAgK1xuICAgICAgICAgICAgYCR7dXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZXNoYXBlSW50ZXJuYWw8VDEsIFQyPihuZGFycmF5LCBuZXdTaGFwZSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNoYXBlSW50ZXJuYWw8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgbmRhcnJheTogVDEsIG5ld1NoYXBlOiBudW1iZXJbXSk6IFQyO1xuXG4gIC8qKlxuICAgKiBFeHRyYWN0cyBhIHNsaWNlIGZyb20gYSBtYXRyaXguIFRoZSBvcGVyYXRpb24gZXh0cmFjZXMgYSBzbGljZSBmcm9tIGlucHV0XG4gICAqIHRoYXQgc3RhcnRzIGF0IGNvb3JkaW5hdGVzIGBiZWdpbmAgYW5kIGlzIG9mIHNpemUgYHNpemVgLlxuICAgKiBAcGFyYW0gaW5wdXQgVGhlIGlucHV0IG1hdHJpeCB0byBzbGljZSBmcm9tLlxuICAgKiBAcGFyYW0gYmVnaW4gVGhlIDJEIGNvb3JkaW5hdGVzIGluIHRoZSBpbnB1dCBtYXRyaXggdG8gc3RhcnQgdGhlIHNsaWNlXG4gICAqIGZyb20uXG4gICAqIEBwYXJhbSBzaXplIFRoZSBzaWNlIG9mIHRoZSAyRCB3aW5kb3cgdG8gc2xpY2UuXG4gICAqL1xuICBzbGljZTJEKGlucHV0OiBBcnJheTJELCBiZWdpbjogW251bWJlciwgbnVtYmVyXSwgc2l6ZTogW251bWJlciwgbnVtYmVyXSk6XG4gICAgICBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYmVnaW5bMF0gKyBzaXplWzBdIDw9IGlucHV0LnNoYXBlWzBdICYmXG4gICAgICAgICAgICBiZWdpblsxXSArIHNpemVbMV0gPD0gaW5wdXQuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBzbGljZTJEOiByZXF1ZXN0ZWQgc3RhcnQgcG9zaXRpb24gJHtiZWdpbn0gYW5kIHNpemUgYCArXG4gICAgICAgICAgICBgJHtzaXplfSB3b3VsZCBvdmVyZmxvdyBpbnB1dCBvZiBzaGFwZSAke2lucHV0LnNoYXBlfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNsaWNlMkRJbnRlcm5hbChpbnB1dCwgYmVnaW4sIHNpemUpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luOiBbbnVtYmVyLCBudW1iZXJdLCBzaXplOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRDtcblxuICAvKipcbiAgICogQ29waWVzIGEgd2luZG93IGZyb20gdGhlIGBzb3VyY2VgIG1hdHJpeCBzdGFydGluZyBhdCBgc291cmNlQmVnaW5gIGFuZCBpc1xuICAgKiBvZiBzaXplIGBzb3VyY2VTaXplYCB0byBhIHdpbmRvdyBpbiB0aGUgYGRlc3RgIG1hdHJpeCBzdGFydGluZyBhdFxuICAgKiBgZGVzdEJlZ2luYCBhbmQgaXMgb2Ygc2l6ZSBgZGVzdFNpemVgL1xuICAgKiBAcGFyYW0gc291cmNlIFRoZSBzb3VyY2UgbWF0cml4IHRvIGNvcHkgZnJvbS5cbiAgICogQHBhcmFtIHNvdXJjZUJlZ2luIFRoZSBjb29yZGluYXRlcyB0byBzdGFydCB0aGUgY29weSBmcm9tLlxuICAgKiBAcGFyYW0gc291cmNlU2l6ZSBUaGUgc2l6ZSBvZiB0aGUgY29weSB3aW5kb3cuXG4gICAqIEBwYXJhbSBkZXN0IFRoZSBkZXN0aW5hdGlvbiBtYXRyaXggdG8gY29weSB0by5cbiAgICogQHBhcmFtIGRlc3RCZWdpbiBUaGUgY29vcmRpbmF0ZXMgaW4gYGRlc3RgIHRvIGNvcHkgdG8uXG4gICAqIEBwYXJhbSBkZXN0U2l6ZSBUaGUgc2l6ZSBvZiB0aGUgZGVzdGluYXRpb24gd2luZG93LlxuICAgKi9cbiAgY29weTJEKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHNvdXJjZUJlZ2luWzBdICsgc291cmNlU2l6ZVswXSA8PSBzb3VyY2Uuc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIHNvdXJjZUJlZ2luWzFdICsgc291cmNlU2l6ZVsxXSA8PSBzb3VyY2Uuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBzb3VyY2Ugc3RhcnQgcG9zaXRpb24gJHtzb3VyY2VCZWdpbn0gYCArXG4gICAgICAgICAgICBgYW5kIHNvdXJjZSBzaXplICR7c291cmNlU2l6ZX0gd291bGQgb3ZlcmZsb3cgc291cmNlIE5EQXJyYXlgICtcbiAgICAgICAgICAgIGBvZiBzaGFwZSAke3NvdXJjZS5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRlc3RCZWdpblswXSArIGRlc3RTaXplWzBdIDw9IGRlc3Quc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIGRlc3RCZWdpblsxXSArIGRlc3RTaXplWzFdIDw9IGRlc3Quc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBkZXN0IHN0YXJ0IHBvc2l0aW9uICR7ZGVzdEJlZ2lufSBgICtcbiAgICAgICAgICAgIGBhbmQgc291cmNlIHNpemUgJHtkZXN0U2l6ZX0gd291bGQgb3ZlcmZsb3cgZGVzdCBOREFycmF5IG9mYCArXG4gICAgICAgICAgICBgc2hhcGUgJHtkZXN0LnNoYXBlfS5gKTtcbiAgICBjb3B5MmRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplLCBkZXN0U2l6ZSk7XG5cbiAgICByZXR1cm4gdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgc291cmNlLCBzb3VyY2VCZWdpbiwgc291cmNlU2l6ZSwgZGVzdCwgZGVzdEJlZ2luLCBkZXN0U2l6ZSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvcHkyREludGVybmFsKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZDtcblxuICAvKipcbiAgICogQ29uY2F0ZW5hdGVzIHR3byAzRCBuZGFycmF5cyBhbG9uZyBhIGdpdmVuIGF4aXMuXG4gICAqXG4gICAqIEZvciBleGFtcGxlLCBpZjpcbiAgICogQTogc2hhcGUoMiwgMSwgMykgPSB8IHIxLCBnMSwgYjEgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjIsIGcyLCBiMiB8XG4gICAqXG4gICAqIEI6IHNoYXBlKDIsIDEsIDMpID0gfCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBDID0gY29uY2F0M0QoQSwgQiwgYXhpcylcbiAgICpcbiAgICogaWYgYXhpcyA9IDA6XG4gICAqIEM6IHNoYXBlKDQsIDEsIDMpID0gfCByMSwgZzEsIGIxIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogaWYgYXhpcyA9IDE6XG4gICAqIEM6IHNoYXBlKDIsIDIsIDMpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIsIHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBpZiBheGlzID0gMjpcbiAgICogQyA9IHNoYXBlKDIsIDEsIDYpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyLCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogQHBhcmFtIG5kYXJyYXkxIFRoZSBmaXJzdCBhcnJheSB0byBjb25jYXQuXG4gICAqIEBwYXJhbSBuZGFycmF5MiBUaGUgc2Vjb25kIGFycmF5IHRvIGNvbmF0LlxuICAgKiBAcGFyYW0gYXhpcyBUaGUgYXhpcyB0byBjb25jYXRlIGFsb25nLlxuICAgKi9cbiAgY29uY2F0M0QobmRhcnJheTE6IEFycmF5M0QsIG5kYXJyYXkyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIG5kYXJyYXkxLnNoYXBlLCBuZGFycmF5Mi5zaGFwZSwgYXhpcywgJ0Vycm9yIGluIGNvbmNhdDNkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbmNhdDNESW50ZXJuYWwobmRhcnJheTEsIG5kYXJyYXkyLCBheGlzKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbmNhdDNESW50ZXJuYWwoXG4gICAgICBuZGFycmF5MTogQXJyYXkzRCwgbmRhcnJheTI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBSZWR1Y3Rpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRoZSBsb2coc3VtKGUgXiB4KSkgZm9yIGVhY2ggeCBpbiB0aGUgaW5wdXQgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkgdG8gY29tcHV0ZSB0aGUgbG9nU3VtRXhwIG92ZXIuXG4gICAqL1xuICBsb2dTdW1FeHAobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzdW0gb2YgYWxsIHRoZSBlbnRyaWVzIGluIHRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheSB0byBjb21wdXRlIHRoZSBzdW0gb3Zlci5cbiAgICovXG4gIHN1bShuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN1bUludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3VtSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWluaW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01pbihuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01pbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWF4aW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01heChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01heEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogUmV0dXJucyBhIDEgaWYgdGhlIGFyZ01heCBvZiB4MSBhbmQgeDIgYXJlIHRoZSBzYW1lLCBvdGhlcndpc2UgMC5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgYXJnTWF4RXF1YWxzKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaCh4MS5zaGFwZSwgeDIuc2hhcGUsICdFcnJvciBpbiBhcmdNYXhFcXVhbHM6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJnTWF4RXF1YWxzSW50ZXJuYWwoeDEsIHgyKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRvcCBLIHZhbHVlcyBhbmQgZmxhdHRlbmVkIGluZGljZXMuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gayBIb3cgbWFueSB0b3AgdmFsdWVzIHRvIGNvbXB1dGUuXG4gICAqL1xuICB0b3BLKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6IHt2YWx1ZXM6IEFycmF5MUQsIGluZGljZXM6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgayA8PSBuZGFycmF5LnNpemUsXG4gICAgICAgIGBFcnJvciBpbiB0b3BLOiBrIHZhbHVlICgke2t9KSBtdXN0IGJlIGxlc3MgdGhhbiBzaXplIG9mIGlucHV0IGAgK1xuICAgICAgICAgICAgYG5kYXJyYXksIGdvdCBzaGFwZSAke25kYXJyYXkuc2hhcGV9LmApO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMudG9wS0ludGVybmFsKG5kYXJyYXksIGspO1xuICAgIHRoaXMudHJhY2socmVzdWx0LnZhbHVlcyk7XG4gICAgdGhpcy50cmFjayhyZXN1bHQuaW5kaWNlcyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdG9wS0ludGVybmFsKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6XG4gICAgICB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfTtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG1pbmltdW0gdmFsdWUgZnJvbSB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbWluKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWluSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgbWF4aW11bSB2YWx1ZSBmcm9tIHRoZSBpbnB1dC5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBtYXgobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzb2Z0bWF4IG5vcm1hbGl6ZWQgdmVjdG9yIGZyb20gdGhlIGlucHV0IHZlY3Rvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHZlY3Rvci5cbiAgICovXG4gIHNvZnRtYXgoeDogQXJyYXkxRCk6IEFycmF5MUQge1xuICAgIHJldHVybiB0aGlzLnNjb3BlKCgpID0+IHtcbiAgICAgIC8vIERvIGl0IGluIGxvZyBzcGFjZSBmb3IgbnVtZXJpY2FsIHN0YWJpbGl0eS5cbiAgICAgIC8vIGV4cChYIC0gbG9nU3VtRXhwKFgpKVxuICAgICAgY29uc3QgbHNlID0gdGhpcy5sb2dTdW1FeHAoeCk7XG4gICAgICBjb25zdCBsb2dSZXN1bHQgPSB0aGlzLmFycmF5TWludXNTY2FsYXIoeCwgbHNlKTtcbiAgICAgIHJldHVybiB0aGlzLmV4cChsb2dSZXN1bHQpO1xuICAgIH0pO1xuICB9XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBFbGVtZW50LXdpc2Ugb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogU3dpdGNoZXMgZGltZW5zaW9ucyBvZiB0aGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBuZXdEaW0gVGhlIG5ldyBpbmRpY2VzIHRoYXQgZGVmaW5lIHdoaWNoIHNoYXBlcyB2YWx1ZXMgdG8gc3dpdGNoLlxuICAgKi9cbiAgc3dpdGNoRGltPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBuZXdEaW06IG51bWJlcltdKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gbmV3RGltLmxlbmd0aCxcbiAgICAgICAgYEVycm9yIGluIHN3aXRjaERpbTogbGVuZ3RoIG9mIGlucHV0IHNoYXBlICR7YS5zaGFwZX0gYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBzaXplIG9mIG5ld0RpbSBhcnJheSAke25ld0RpbX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zd2l0Y2hEaW1JbnRlcm5hbChhLCBuZXdEaW0pKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIHBsdXMgTkRBcnJheSwgYyArIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBjICsgQS5cbiAgICogQHBhcmFtIGEgVGhlIE5EQXJyYXkgQSBpbiBjICsgQS5cbiAgICovXG4gIHNjYWxhclBsdXNBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxhclBsdXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJQbHVzQXJyYXlJbnRlcm5hbChjLCBhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIG1pbnVzIE5EQXJyYXksIGMgLSBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gYyAtIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gYyAtIEEuXG4gICAqL1xuICBzY2FsYXJNaW51c0FycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGFyTWludXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJNaW51c0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBzY2FsYXIgbWludXMgTkRBcnJheSwgQSAtIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gQSAtIGMuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBBIC0gYy5cbiAgICovXG4gIGFycmF5TWludXNTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheU1pbnVzU2NhbGFyOiBzZWNvbmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheU1pbnVzU2NhbGFySW50ZXJuYWwoYSwgYykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhcnJheU1pbnVzU2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgYzogU2NhbGFyKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgLTEgKiBBIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IGFycmF5LlxuICAgKi9cbiAgbmVnPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5uZWdJbnRlcm5hbChhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVDtcblxuICAvKipcbiAgICogQWRkcyB0d28gTkRBcnJheXMgZWxlbWVudC13aXNlLCBBICsgQi4gSW5wdXRzIG11c3QgYmUgdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIGFkZCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBhZGQgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgYWRkPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gYWRkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFkZEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBTdWJ0cmFjdHMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSwgQSAtIEIuIElucHV0cyBtdXN0IGJlIHRoZSBzYW1lIHNoYXBlLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBzdWI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBzdWI6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc3ViSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzdWJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIE11bHRpcGxpZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgKiBCLiBJbnB1dHMgbXVzdFxuICAgKiBiZSB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgZWxlbWVudFdpc2VNdWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBlbGVtZW50V2lzZU11bDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5lbGVtZW50V2lzZU11bEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZWxlbWVudFdpc2VNdWxJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIERpdmlkZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgLyBCLiBJbnB1dHMgbXVzdCBiZVxuICAgKiB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gZGl2aWRlIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGIgVGhlIHNlY29uZCBOREFycmF5IHRvIGRpdmlkZSBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBkaXZpZGU8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBkaXZpZGU6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZGl2aWRlSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBkaXZpZGVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIGRpdmlkZWQgYnkgYW4gTkRBcnJheSwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAvXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgdmFsdWUgaW4gYyAvIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIGMgLyBBLlxuICAgKi9cbiAgc2NhbGFyRGl2aWRlZEJ5QXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsYXJEaXZpZGVkQnlBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgTkRBcnJheSBvZiByYW5rICR7Yy5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJEaXZpZGVkQnlBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGFuIE5EQXJyYXkgZGl2aWRlZCBieSBhIHNjYWxhciwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgQSAvXG4gICAqIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIEEgLyBjLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIHZhbHVlIGluIEEgLyBjLlxuICAgKi9cbiAgYXJyYXlEaXZpZGVkQnlTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogc2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBgICtcbiAgICAgICAgICAgIGBidXQgZ290IE5EQXJyYXkgb2YgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsKGEsIGMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBhOiBULCBjOiBTY2FsYXIpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBleHBvbmVudGlhbCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuIHkgPSBlIF4geFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGV4cDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZXhwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBleHBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIG5hdHVyYWwgbG9nYXJpdGhtIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZS4geSA9IGxuKHgpXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbG9nPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgcmVjdGlmaWVkIGxpbmVhciBlbGVtZW50LXdpc2UsIG1heCh4LCAwKS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICByZWx1PFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZWx1SW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzaWdtb2lkIGVsZW1lbnQtd2lzZSwgeSA9IDEgLyAoMSArIGV4cCgteCkpLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHNpZ21vaWQ8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpZ21vaWRJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGh5cGVyYm9saWMgdGFuZ2VudCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgdGFuaDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMudGFuaEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc2luIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IHNpbih4KS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzaW48VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2luSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzdGVwIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IDEgaWYgeCA+IDAgfCAwIGlmIHggPD1cbiAgICogMFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHN0ZXA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN0ZXBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN0ZXBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGVkIGFycmF5IGFkZCBvcGVyYXRpb24sIGMxICogQSArIGMyICogQi5cbiAgICogQHBhcmFtIGMxIFRoZSBmaXJzdCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gYzIgVGhlIHNlY29uZCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBjYiBUaGUgc2Vjb25kIE5EQXJyYXkgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqL1xuICBzY2FsZWRBcnJheUFkZDxUIGV4dGVuZHMgTkRBcnJheT4oYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjMS5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6IGZpcnN0IGFyZ3VtZW50IG11c3QgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYCByYW5rICR7YzEucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMyLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsZWRBcnJheUFkZDogdGhpcmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgTkRBcnJheSBvZiByYW5rICR7YzIucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6ICcpO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsKGMxLCBhLCBjMiwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciB0aW1lcyBhcnJheSBvcGVyYXRpb24gYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAqXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgaW4gdGhlIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIEEgdGhlIE5EQXJyYXkgaW4gdGhlIG9wZXJhdGlvbiB0aGF0IHdpbGwgYmUgYnJvYWRjYXN0ZWQgb3Zlci5cbiAgICovXG4gIHNjYWxhclRpbWVzQXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJUaW1lc0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJUaW1lc0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYW4gZWxlbWVudC13aXNlIGJyb2FkY2FzdGVkIG11bHRpcGxpY2F0aW9uIG9mIHR3byBtYXRyaWNlcyBBIGFuZFxuICAgKiBCLiBXaWxsIHJldHVybiBhIG5ldyBtYXRyaXggdGhhdCBpcyB0aGUgbWF4IG9mIEEgYW5kIEIsIHdoZXJlIHRoZSBzbWFsbGVyXG4gICAqIG1hdHJpeCB3aWxsIGJyb2FkY2FzdCBvdmVyIHRoZSBsYXJnZXIgbWF0cml4LlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGluIHRoZSBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBBIHRoZSBOREFycmF5IGluIHRoZSBvcGVyYXRpb24gdGhhdCB3aWxsIGJlIGJyb2FkY2FzdGVkIG92ZXIuXG4gICAqL1xuICBlbGVtZW50V2lzZU11bEJyb2FkY2FzdChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIGVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0OiBmaXJzdCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7YS5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYi5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gZWxlbWVudFdpc2VNdWxCcm9hZGNhc3Q6IHNlY29uZCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7Yi5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0SW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOlxuICAgICAgQXJyYXkyRDtcblxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gQ29udm9sdXRpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIDJEIGNvbnZvbHV0aW9uIG92ZXIgdGhlIGlucHV0IHguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFtyb3dzLCBjb2xzLCBkZXB0aDFdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIGJpYXNlcyBPcHRpb25hbCBiaWFzZXMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDEgb2Ygc2hhcGUgW2RlcHRoMl0uXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSB6ZXJvIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZFxuICAgKiBlcXVhbGx5IG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIGNvbnYyZChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZDogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7d2VpZ2h0cy5yYW5rfS5gKTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGJpYXNlcy5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGJpYXNlcyBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgICBgJHtiaWFzZXMucmFua30uYCk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbMl0sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IG1hdGNoICBgICtcbiAgICAgICAgICAgIGBpbnB1dCBkZXB0aCBmb3Igd2VpZ2h0cyAke3dlaWdodHMuc2hhcGVbMl19LmApO1xuXG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbnYyZEludGVybmFsKHgsIHdlaWdodHMsIGJpYXNlcywgc3RyaWRlLCB6ZXJvUGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGJhY2twcm9wIG9mIGEgMkQgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFt4cm93cywgeGNvbHMsIGRlcHRoMV0uXG4gICAqIEBwYXJhbSBkeSBUaGUgZHkgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbeXJvd3MsIHljb2xzLCBkZXB0aDJdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBvcmlnaW5hbCBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiB0aGUgb3JpZ2luYWwgY29udm9sdXRpb24uXG4gICAqL1xuICBjb252MmRCYWNrUHJvcChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt4LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkeSBtdXN0IGJlIHJhbmsgMywgYnV0IGdvdCBzaGFwZSBgICtcbiAgICAgICAgICAgIGAke2R5LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgd2VpZ2h0cy5yYW5rID09PSA0LFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt3ZWlnaHRzLnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5zaGFwZVsyXSA9PT0gd2VpZ2h0cy5zaGFwZVsyXSxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkZXB0aCBvZiB4ICR7eC5zaGFwZVsyXX0pIG11c3QgYCArXG4gICAgICAgICAgICBgbWF0Y2ggaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVsyXX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzNdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IGRlcHRoIG9mIGR5ICgke2R5LnNoYXBlWzJdfSkgbXVzdCBgICtcbiAgICAgICAgICAgIGBtYXRjaCBvdXRwdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVszXX0pLmApO1xuXG4gICAgY29uc3QgYmFja3Byb3BSZXN1bHQgPVxuICAgICAgICB0aGlzLmNvbnYyZEJhY2tQcm9wSW50ZXJuYWwoeCwgZHksIHdlaWdodHMsIHN0cmlkZSwgcGFkKTtcblxuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZGIpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHcpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHgpO1xuXG4gICAgcmV0dXJuIGJhY2twcm9wUmVzdWx0O1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH07XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0cmFuc3Bvc2VkIDJEIGNvbnZvbHV0aW9uIG9mIGFuIGltYWdlLCBhbHNvIGtub3duIGFzIGFcbiAgICogZGVjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMywgb2Ygc2hhcGUgW3hyb3dzLCB4Y29scywgZGVwdGgxXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBiaWFzZXMgT3B0aW9uYWwgYmlhc2VzIE5EQXJyYXksIG11c3QgYmUgcmFuayAxIG9mIHNoYXBlIFtkZXB0aDJdLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBjb252MmRUcmFuc3Bvc2UoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7d2VpZ2h0cy5yYW5rfWApO1xuICAgIGlmIChiaWFzZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgYmlhc2VzLnJhbmsgPT09IDEsXG4gICAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogYmlhc2VzIG11c3QgYmUgcmFuayAxLCBidXQgZ290ICcgK1xuICAgICAgICAgICAgICAncmFuayAke2JpYXNlcy5yYW5rfS5gKTtcbiAgICB9XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbM10sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IGAgK1xuICAgICAgICAgICAgYG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICR7d2VpZ2h0cy5zaGFwZVszXX0uYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayhcbiAgICAgICAgdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWF4IHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1heFBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGluIG1heFBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICcgKyB4LnJhbmsgKyAnLicpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4UG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgYmFja3Byb3Agb2YgYSBtYXggcG9vbC5cbiAgICogQHBhcmFtIGR5IFRoZSBkeSBlcnJvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgbWF4UG9vbEJhY2twcm9wKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogZHkgbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7ZHkucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhQb29sQmFja3Byb3BJbnRlcm5hbChkeSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heFBvb2xCYWNrcHJvcEludGVybmFsKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWluIHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1pblBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1pblBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1pblBvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWluUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIGF2ZXJhZ2UgcG9vbGluZyBvZiBhbiBpbWFnZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgYXZnUG9vbCh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gYXZnUG9vbDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXZnUG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhdmdQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qXG4gICAqIEJpbGluZWFyIHJlc2l6ZSBhIDNEIGFycmF5IHBlciBlYWNoIGNoYW5uZWwgdG8gYSBuZXcgMkQgc2hhcGUuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBBcnJheTNELlxuICAgKiBAcGFyYW0gbmV3U2hhcGUyRCBUaGUgbmV3IHNoYXBlIHRvIHJlc2l6ZSB0aGUgQXJyYXkzRCB0by4gRWFjaCBjaGFubmVsIGlzXG4gICAqIHJlc2l6ZWQgaW5kaXZpZHVhbGx5LlxuICAgKiBAcGFyYW0gYWxpZ25Db3JuZXJzIEFuIG9wdGlvbmFsIGJvb2wuIERlZmF1bHRzIHRvIEZhbHNlLiBJZiB0cnVlLCByZXNjYWxlXG4gICAqIGlucHV0IGJ5IChuZXdfaGVpZ2h0IC0gMSkgLyAoaGVpZ2h0IC0gMSksIHdoaWNoIGV4YWN0bHkgYWxpZ25zIHRoZSA0XG4gICAqIGNvcm5lcnMgb2YgaW1hZ2VzIGFuZCByZXNpemVkIGltYWdlcy4gSWYgZmFsc2UsIHJlc2NhbGUgYnkgbmV3X2hlaWdodCAvXG4gICAqIGhlaWdodC4gVHJlYXQgc2ltaWxhcmx5IHRoZSB3aWR0aCBkaW1lbnNpb24uXG4gICAqL1xuICByZXNpemVCaWxpbmVhcjNEKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzID0gZmFsc2UpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gcmVzaXplQmlsaW5lYXIzRDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBuZXdTaGFwZTJELmxlbmd0aCA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHJlc2l6ZUJpbGluZWFyM0Q6IG5ldyBzaGFwZSBtdXN0IDJELCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7bmV3U2hhcGUyRH0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2soXG4gICAgICAgIHRoaXMucmVzaXplQmlsaW5lYXIzREludGVybmFsKHgsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVycykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLCBhbGlnbkNvcm5lcnM6IGJvb2xlYW4pOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBCYXRjaCBub3JtYWxpemF0aW9uIDNELiBNZWFuLCB2YXJpYW5jZSwgc2NhbGUsIGFuZCBvZmZzZXQgY2FuIGJlIG9mIHR3b1xuICAgKiBzaGFwZXM6IDEpIFRoZSBzYW1lIHNoYXBlIGFzIHRoZSBpbnB1dDogYW4gQXJyYXkzRC4gMikgSW4gdGhlIGNvbW1vbiBjYXNlLFxuICAgKiB0aGUgZGVwdGggZGltZW5zaW9uIGlzIHRoZSBsYXN0IGRpbWVuc2lvbiBvZiB4LCBzbyB0aGUgdmFsdWVzIHdvdWxkIGJlIGFuXG4gICAqIEFycmF5MUQgb2Ygc2hhcGUgW2RlcHRoXS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBtZWFuIEEgbWVhbiBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2UgQSB2YXJpYW5jZSBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2VFcHNpbG9uIEEgc21hbGwgZmxvYXQgbnVtYmVyIHRvIGF2b2lkIGRpdmlkaW5nIGJ5IDAuXG4gICAqIEBwYXJhbSBzY2FsZSBBIHNjYWxlIE5EQXJyYXkuXG4gICAqIEBwYXJhbSBvZmZzZXQgQW4gb2Zmc2V0IE5EQXJyYXkuXG4gICAqL1xuICBiYXRjaE5vcm1hbGl6YXRpb24zRChcbiAgICAgIHg6IEFycmF5M0QsIG1lYW46IEFycmF5M0R8QXJyYXkxRCwgdmFyaWFuY2U6IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIHZhcmlhbmNlRXBzaWxvbiA9IC4wMDEsIHNjYWxlPzogQXJyYXkzRHxBcnJheTFELFxuICAgICAgb2Zmc2V0PzogQXJyYXkzRHxBcnJheTFEKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGJhdGNoTm9ybWFsaXphdGlvbjNEOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1lYW4ucmFuayA9PT0gMyB8fCBtZWFuLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogbWVhbiBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYnV0IGAgK1xuICAgICAgICAgICAgYGdvdCByYW5rICR7bWVhbi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdmFyaWFuY2UucmFuayA9PT0gMyB8fCB2YXJpYW5jZS5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IHZhcmlhbmNlIG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgIGBidXQgZ290IHJhbmsgJHt2YXJpYW5jZS5yYW5rfS5gKTtcbiAgICBpZiAoc2NhbGUgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgc2NhbGUucmFuayA9PT0gMyB8fCBzY2FsZS5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogc2NhbGUgbXVzdCBiZSByYW5rIDMgb3IgcmFuayAxIGAgK1xuICAgICAgICAgICAgICBgYnV0IGdvdCByYW5rICR7c2NhbGUhLnJhbmt9LmApO1xuICAgIH1cbiAgICBpZiAob2Zmc2V0ICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIG9mZnNldC5yYW5rID09PSAzIHx8IG9mZnNldC5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogb2Zmc2V0IG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgICAgYGJ1dCBnb3QgcmFuayAke29mZnNldCEucmFua30uYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5iYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgICB4LCBtZWFuLCB2YXJpYW5jZSwgdmFyaWFuY2VFcHNpbG9uLCBzY2FsZSwgb2Zmc2V0KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb246IG51bWJlciwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEO1xufVxuXG5leHBvcnQgZW51bSBNYXRyaXhPcmllbnRhdGlvbiB7XG4gIFJFR1VMQVIsXG4gIFRSQU5TUE9TRURcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyRF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbiwgTkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlNYXRoQ1BVIGV4dGVuZHMgTkRBcnJheU1hdGgge1xuICBjb25zdHJ1Y3RvcihzYWZlTW9kZSA9IGZhbHNlKSB7XG4gICAgc3VwZXIoc2FmZU1vZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuZ2V0VmFsdWVzKCkpfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaGFwZUludGVybmFsPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgcmV0dXJuIHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KS5yZXNoYXBlPFQyPihuZXdTaGFwZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IEFycmF5MkQge1xuICAgIGNvbnN0IHJlc3VsdCA9IEFycmF5MkQuemVyb3Moc2l6ZVJvd0NvbCk7XG4gICAgdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgaW5wdXQsIGJlZ2luUm93Q29sLCBzaXplUm93Q29sLCByZXN1bHQsIFswLCAwXSwgc2l6ZVJvd0NvbCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELFxuICAgICAgZGVzdEJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb3B5MkRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCk7XG4gICAgY29uc3Qgc3JjVmFsdWVzID0gc291cmNlLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGRzdFZhbHVlcyA9IGRlc3QuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgbiA9IHNvdXJjZVNpemVSb3dDb2xbMF0gKiBzb3VyY2VTaXplUm93Q29sWzFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgICBjb25zdCBzcmNSb3cgPSBzb3VyY2VCZWdpblJvd0NvbFswXSArIE1hdGguZmxvb3IoaSAvIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjQ29sID0gc291cmNlQmVnaW5Sb3dDb2xbMV0gKyAoaSAlIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjT2ZmID0gc3JjUm93ICogc291cmNlLnNoYXBlWzFdICsgc3JjQ29sO1xuICAgICAgY29uc3QgZHN0Um93ID0gZGVzdEJlZ2luUm93Q29sWzBdICsgTWF0aC5mbG9vcihpIC8gZGVzdFNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3QgZHN0Q29sID0gZGVzdEJlZ2luUm93Q29sWzFdICsgKGkgJSBkZXN0U2l6ZVJvd0NvbFsxXSk7XG4gICAgICBjb25zdCBkc3RPZmYgPSBkc3RSb3cgKiBkZXN0LnNoYXBlWzFdICsgZHN0Q29sO1xuICAgICAgZHN0VmFsdWVzW2RzdE9mZl0gPSBzcmNWYWx1ZXNbc3JjT2ZmXTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgY29uY2F0M0RJbnRlcm5hbCh4MTogQXJyYXkzRCwgeDI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZSh4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpO1xuXG4gICAgY29uc3QgdmFsdWVzID0gTkRBcnJheS56ZXJvczxBcnJheTNEPihvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dFNoYXBlWzBdOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgb3V0cHV0U2hhcGVbMV07IGorKykge1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IG91dHB1dFNoYXBlWzJdOyBrKyspIHtcbiAgICAgICAgICAvLyBTaGFkZXIgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IGluZGV4OiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbaSwgaiwga107XG4gICAgICAgICAgbGV0IHZhbHVlOiBudW1iZXI7XG4gICAgICAgICAgaWYgKGluZGV4W2F4aXNdIDwgeDEuc2hhcGVbYXhpc10pIHtcbiAgICAgICAgICAgIHZhbHVlID0geDEuZ2V0KGksIGosIGspO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpbmRleFtheGlzXSAtPSB4MS5zaGFwZVtheGlzXTtcbiAgICAgICAgICAgIGNvbnN0IFtpMiwgajIsIGsyXSA9IGluZGV4O1xuICAgICAgICAgICAgdmFsdWUgPSB4Mi5nZXQoaTIsIGoyLCBrMik7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdmFsdWVzLnNldCh2YWx1ZSwgaSwgaiwgayk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWVzO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXN1bHRWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IGNWYWwgKyBhVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCkge1xuICAgIGNvbnN0IGNWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYzFWYWwgPSBjMS5nZXQoKTtcbiAgICBjb25zdCBjMlZhbCA9IGMyLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY1ZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY1ZhbHVlc1tpXSA9IGMxVmFsICogYVZhbHVlc1tpXSArIGMyVmFsICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBjVmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBjVmFsICogYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIGNvbnN0IG5lZ0EgPSB0aGlzLm5lZ0ludGVybmFsKGEpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuc2NhbGFyUGx1c0FycmF5SW50ZXJuYWwoYywgbmVnQSk7XG5cbiAgICBuZWdBLmRpc3Bvc2UoKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICBjb25zdCBuZWdDID0gdGhpcy5uZWdJbnRlcm5hbChjKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnNjYWxhclBsdXNBcnJheUludGVybmFsKG5lZ0MsIGEpO1xuXG4gICAgbmVnQy5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuc2NhbGFyVGltZXNBcnJheUludGVybmFsKFNjYWxhci5ORUdfT05FLCBhKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VD4oU2NhbGFyLk9ORSwgYSwgU2NhbGFyLk9ORSwgYik7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsPFQ+KFNjYWxhci5PTkUsIGEsIFNjYWxhci5ORUdfT05FLCBiKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXRNdWxJbnRlcm5hbChcbiAgICAgIGE6IEFycmF5MkQsIGI6IEFycmF5MkQsIGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsXG4gICAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogQXJyYXkyRCB7XG4gICAgY29uc3Qgc2hhcmVkRGltID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcblxuICAgIGNvbnN0IGxlZnREaW0gPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMF0gOiBhLnNoYXBlWzFdO1xuICAgIGNvbnN0IHJpZ2h0RGltID1cbiAgICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBiLnNoYXBlWzFdIDogYi5zaGFwZVswXTtcblxuICAgIGNvbnN0IG5vcm1hbEdldHRlciA9IChtYXRyaXg6IEFycmF5MkQsIGk6IG51bWJlciwgajogbnVtYmVyKSA9PlxuICAgICAgICBtYXRyaXguZ2V0KGksIGopO1xuICAgIGNvbnN0IHRyYW5zcG9zZWRHZXR0ZXIgPSAobWF0cml4OiBBcnJheTJELCBpOiBudW1iZXIsIGo6IG51bWJlcikgPT5cbiAgICAgICAgbWF0cml4LmdldChqLCBpKTtcblxuICAgIGNvbnN0IGFHZXR0ZXIgPSAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/XG4gICAgICAgIG5vcm1hbEdldHRlciA6XG4gICAgICAgIHRyYW5zcG9zZWRHZXR0ZXI7XG4gICAgY29uc3QgYkdldHRlciA9IChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgICAgbm9ybWFsR2V0dGVyIDpcbiAgICAgICAgdHJhbnNwb3NlZEdldHRlcjtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGxlZnREaW0gKiByaWdodERpbSk7XG4gICAgbGV0IGluZGV4ID0gMDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVmdERpbTsgKytpKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHJpZ2h0RGltOyArK2opIHtcbiAgICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgc2hhcmVkRGltOyArK2spIHtcbiAgICAgICAgICAvLyBUT0RPOiBvcHRpbWl6ZSBDUFUgbWF0bXVsLlxuICAgICAgICAgIHN1bSArPSBhR2V0dGVyKGEsIGksIGspICogYkdldHRlcihiLCBrLCBqKTtcbiAgICAgICAgfVxuICAgICAgICB2YWx1ZXNbaW5kZXgrK10gPSBzdW07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbGVmdERpbSwgcmlnaHREaW1dLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOiBBcnJheTJEIHtcbiAgICBjb25zdCBtYXhSb3cgPSBNYXRoLm1heChhLnNoYXBlWzBdLCBiLnNoYXBlWzBdKTtcbiAgICBjb25zdCBtYXhDb2wgPSBNYXRoLm1heChhLnNoYXBlWzFdLCBiLnNoYXBlWzFdKTtcblxuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobWF4Um93ICogbWF4Q29sKTtcbiAgICBsZXQgaW5kZXggPSAwO1xuICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IG1heFJvdzsgcm93KyspIHtcbiAgICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IG1heENvbDsgY29sKyspIHtcbiAgICAgICAgdmFsdWVzW2luZGV4KytdID0gYS5nZXQocm93ICUgYS5zaGFwZVswXSwgY29sICUgYS5zaGFwZVsxXSkgKlxuICAgICAgICAgICAgYi5nZXQocm93ICUgYi5zaGFwZVswXSwgY29sICUgYi5zaGFwZVsxXSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbWF4Um93LCBtYXhDb2xdLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRpdmlkZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAvIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBjVmFsdWUgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gY1ZhbHVlIC8gYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOlxuICAgICAgVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWx1ZSA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldIC8gY1ZhbHVlO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBzdW0gPSAwO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHN1bSArPSB2YWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KHN1bSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgbGV0IG1pbiA9IE51bWJlci5NQVhfVkFMVUU7XG4gICAgbGV0IG1pbkluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPCBtaW4pIHtcbiAgICAgICAgbWluID0gdmFsdWU7XG4gICAgICAgIG1pbkluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWluSW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBtYXggPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IG1heEluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICAgIG1heEluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWF4SW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgYXJnTWF4MSA9IHRoaXMuYXJnTWF4SW50ZXJuYWwoeDEpLmdldCgpO1xuICAgIGNvbnN0IGFyZ01heDIgPSB0aGlzLmFyZ01heEludGVybmFsKHgyKS5nZXQoKTtcbiAgICBpZiAoaXNOYU4oYXJnTWF4MSkgfHwgaXNOYU4oYXJnTWF4MikpIHtcbiAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KCsoYXJnTWF4MSA9PT0gYXJnTWF4MikpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH0ge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgdmFsdWVzQW5kSW5kaWNlczogQXJyYXk8e3ZhbHVlOiBudW1iZXIsIGluZGV4OiBudW1iZXJ9PiA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YWx1ZXNBbmRJbmRpY2VzLnB1c2goe3ZhbHVlOiB2YWx1ZXNbaV0sIGluZGV4OiBpfSk7XG4gICAgfVxuICAgIHZhbHVlc0FuZEluZGljZXMuc29ydCgoYSwgYikgPT4ge1xuICAgICAgcmV0dXJuIGIudmFsdWUgLSBhLnZhbHVlO1xuICAgIH0pO1xuICAgIGNvbnN0IHRvcGtWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGspO1xuICAgIGNvbnN0IHRvcGtJbmRpY2VzID0gbmV3IEZsb2F0MzJBcnJheShrKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGs7IGkrKykge1xuICAgICAgdG9wa1ZhbHVlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0udmFsdWU7XG4gICAgICB0b3BrSW5kaWNlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0uaW5kZXg7XG4gICAgfVxuICAgIHJldHVybiB7dmFsdWVzOiBBcnJheTFELm5ldyh0b3BrVmFsdWVzKSwgaW5kaWNlczogQXJyYXkxRC5uZXcodG9wa0luZGljZXMpfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGxldCBtaW4gPSB2YWx1ZXNbMF07XG4gICAgZm9yIChsZXQgaSA9IDE7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIDwgbWluKSB7XG4gICAgICAgIG1pbiA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhtaW4pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgbGV0IG1heCA9IHZhbHVlc1swXTtcbiAgICBmb3IgKGxldCBpID0gMTsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1heCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IE1hdGguZXhwKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgbmV3VmFsdWVzW2ldID0gTWF0aC5sb2codmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHhNYXggPSB0aGlzLm1heChuZGFycmF5KTtcbiAgICBjb25zdCBhID0gdGhpcy5hcnJheU1pbnVzU2NhbGFyKG5kYXJyYXksIHhNYXgpO1xuICAgIGNvbnN0IGIgPSB0aGlzLmV4cChhKTtcbiAgICBjb25zdCBjID0gdGhpcy5zdW0oYik7XG4gICAgY29uc3QgZCA9IHRoaXMubG9nKGMpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuYWRkKHhNYXgsIGQpO1xuXG4gICAgeE1heC5kaXNwb3NlKCk7XG4gICAgYS5kaXNwb3NlKCk7XG4gICAgYi5kaXNwb3NlKCk7XG4gICAgYy5kaXNwb3NlKCk7XG4gICAgZC5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlbHVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBNYXRoLm1heCgwLCB2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSAxIC8gKDEgKyBNYXRoLmV4cCgtdmFsdWVzW2ldKSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IHV0aWwudGFuaCh2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IE1hdGguc2luKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RlcEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gdmFsdWUgPiAwID8gMSA6ICh2YWx1ZSA8IDAgPyAwIDogdmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgLyoqXG4gICAqIGltYWdlIGlzIG9mIHNoYXBlIFtyLCBjLCBkMV0uXG4gICAqIHdlaWdodHMgaXMgb2Ygc2hhcGUgW0YsIEYsIGQxLCBkMl0uXG4gICAqL1xuICBwcm90ZWN0ZWQgY29udjJkSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdID0geC5zaGFwZTtcbiAgICBjb25zdCBmaWVsZFNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzLCB4Q29scywgaW5wdXREZXB0aF0sIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG4gICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICBmb3IgKGxldCB5UiA9IDA7IHlSIDwgeS5zaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSICogc3RyaWRlIC0gcGFkO1xuICAgICAgICBjb25zdCB4Uk1pbiA9IE1hdGgubWF4KDAsIHhSQ29ybmVyKTtcbiAgICAgICAgY29uc3QgeFJNYXggPSBNYXRoLm1pbih4Um93cywgZmllbGRTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZmllbGRTaXplICsgeENDb3JuZXIpO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSIC0geFJDb3JuZXI7XG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgLSB4Q0Nvcm5lcjtcbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IGlucHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9IHdlaWdodHMuZ2V0KHdSLCB3QywgZDEsIGQyKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJpYXMgPSAoYmlhc2VzICE9IG51bGwpID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH0ge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBkdyA9IHRoaXMuY29udjJkRGVyV2VpZ2h0cyh4LCBkeSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCBkYiA9IHRoaXMuY29udjJkRGVyQmlhcyhkeSk7XG4gICAgY29uc3QgZHggPSB0aGlzLmNvbnYyZFRyYW5zcG9zZUludGVybmFsKGR5LCB3ZWlnaHRzLCBudWxsLCBzdHJpZGUsIHBhZCk7XG4gICAgcmV0dXJuIHtkeCwgZGIsIGR3fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvcmlnT3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIHhEZXB0aF0gPSB4LnNoYXBlO1xuXG4gICAgLy8gRGlsYXRlIHRoZSBpbnB1dC5cbiAgICBjb25zdCB4Um93c0RpbGF0ZWQgPSAoeFJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IHhDb2xzRGlsYXRlZCA9ICh4Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzRGlsYXRlZCwgeENvbHNEaWxhdGVkLCBvcmlnT3V0cHV0RGVwdGhdLCBmU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIDEsXG4gICAgICAgIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvcmlnSW5wdXREZXB0aDsgKytkMikge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCBNYXRoLmNlaWwoeFJDb3JuZXIgLyBvcmlnU3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIChmU2l6ZSArIHhSQ29ybmVyKSAvIG9yaWdTdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCh4Q0Nvcm5lciAvIG9yaWdTdHJpZGUpKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCAoZlNpemUgKyB4Q0Nvcm5lcikgLyBvcmlnU3RyaWRlKTtcblxuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSICogb3JpZ1N0cmlkZSAtIHhSQ29ybmVyO1xuXG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgKiBvcmlnU3RyaWRlIC0geENDb3JuZXI7XG5cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cy5nZXQoZlNpemUgLSAxIC0gd1IsIGZTaXplIC0gMSAtIHdDLCBkMiwgZDEpO1xuICAgICAgICAgICAgICAgIGRvdFByb2QgKz0gcGl4ZWwgKiB3ZWlnaHQ7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgYmlhcyA9IGJpYXNlcyAhPSBudWxsID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZVNoYWRlckxpa2UoXG4gICAgICB4OiBBcnJheTNELCBvcmlnV2VpZ2h0czogQXJyYXk0RCwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgZlNpemUgPSBvcmlnV2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgW3hSb3dzLCB4Q29scywgeERlcHRoXSA9IHguc2hhcGU7XG5cbiAgICAvLyBEaWxhdGUgdGhlIGlucHV0LlxuICAgIGNvbnN0IHhSb3dzRGlsYXRlZCA9ICh4Um93cyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gICAgY29uc3QgeENvbHNEaWxhdGVkID0gKHhDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbeFJvd3NEaWxhdGVkLCB4Q29sc0RpbGF0ZWQsIG9yaWdPdXRwdXREZXB0aF0sIGZTaXplLCBvcmlnSW5wdXREZXB0aCwgMSxcbiAgICAgICAgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3JpZ0lucHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgLy8gU2hhZGVyIGNvZGUgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICAgICAgICBjb25zdCB4UiA9ICh4UkNvcm5lciArIHdSKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICBpZiAoeFIgPCAwIHx8IHhSID49IHhSb3dzIHx8IE1hdGguZmxvb3IoeFIpICE9PSB4Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCB4QyA9ICh4Q0Nvcm5lciArIHdDKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICAgIGlmICh4QyA8IDAgfHwgeEMgPj0geENvbHMgfHwgTWF0aC5mbG9vcih4QykgIT09IHhDKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgb3JpZ1dlaWdodHMuZ2V0KGZTaXplIC0gMSAtIHdSLCBmU2l6ZSAtIDEgLSB3QywgZDIsIGQxKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHkuc2V0KGRvdFByb2QsIHlSLCB5QywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB5O1xuICB9XG5cbiAgY29udjJkRGVyV2VpZ2h0cyhcbiAgICAgIHg6IEFycmF5M0QsIGRZOiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5NEQge1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB4LnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3Qgd2VpZ2h0c1NoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzU2hhcGU0RChpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuICAgIGNvbnN0IGRXID0gQXJyYXk0RC56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuXG4gICAgY29uc3QgeU51bVJvd3MgPSBkWS5zaGFwZVswXTtcbiAgICBjb25zdCB5TnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHhOdW1Sb3dzID0geC5zaGFwZVswXTtcbiAgICBjb25zdCB4TnVtQ29scyA9IHguc2hhcGVbMV07XG5cbiAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgIGNvbnN0IHlSTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKSk7XG4gICAgICBjb25zdCB5Uk1heCA9IE1hdGgubWluKHlOdW1Sb3dzLCAoeE51bVJvd3MgKyB6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKTtcblxuICAgICAgZm9yIChsZXQgd0MgPSAwOyB3QyA8IGZTaXplOyArK3dDKSB7XG4gICAgICAgIGNvbnN0IHlDTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd0MpIC8gc3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHlDTWF4ID0gTWF0aC5taW4oeU51bUNvbHMsICh4TnVtQ29scyArIHplcm9QYWQgLSB3QykgLyBzdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBpbnB1dERlcHRoOyArK2QxKSB7XG4gICAgICAgICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICAgICAgICAvLyBOZWVkIHRvIGNvbnZvbHZlLlxuICAgICAgICAgICAgbGV0IGRvdFByb2QgPSAwO1xuICAgICAgICAgICAgZm9yIChsZXQgeVIgPSB5Uk1pbjsgeVIgPCB5Uk1heDsgKyt5Uikge1xuICAgICAgICAgICAgICBjb25zdCB4UiA9IHdSICsgeVIgKiBzdHJpZGUgLSB6ZXJvUGFkO1xuICAgICAgICAgICAgICBmb3IgKGxldCB5QyA9IHlDTWluOyB5QyA8IHlDTWF4OyArK3lDKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgeEMgPSB3QyArIHlDICogc3RyaWRlIC0gemVyb1BhZDtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHguZ2V0KHhSLCB4QywgZDEpICogZFkuZ2V0KHlSLCB5QywgZDIpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkVy5zZXQoZG90UHJvZCwgd1IsIHdDLCBkMSwgZDIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZFc7XG4gIH1cblxuICBjb252MmREZXJCaWFzKGRZOiBBcnJheTNEKTogQXJyYXkxRCB7XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSBkWS5zaGFwZVsyXTtcbiAgICBjb25zdCBudW1Sb3dzID0gZFkuc2hhcGVbMF07XG4gICAgY29uc3QgbnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkob3V0cHV0RGVwdGgpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICBmb3IgKGxldCByID0gMDsgciA8IG51bVJvd3M7ICsrcikge1xuICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG51bUNvbHM7ICsrYykge1xuICAgICAgICAgIHN1bSArPSBkWS5nZXQociwgYywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB2YWx1ZXNbZDJdID0gc3VtO1xuICAgIH1cbiAgICByZXR1cm4gQXJyYXkxRC5uZXcodmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzd2l0Y2hEaW1JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4odDogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQge1xuICAgIGNvbnN0IG5ld1NoYXBlOiBudW1iZXJbXSA9IG5ldyBBcnJheSh0LnJhbmspO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3U2hhcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIG5ld1NoYXBlW2ldID0gdC5zaGFwZVtuZXdEaW1baV1dO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHQuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gdC5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCByZXN1bHQgPSBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdC5zaXplOyArK2kpIHtcbiAgICAgIGNvbnN0IGxvYyA9IHQuaW5kZXhUb0xvYyhpKTtcblxuICAgICAgLy8gUGVybXV0ZSBsb2NhdGlvbi5cbiAgICAgIGNvbnN0IG5ld0xvYzogbnVtYmVyW10gPSBuZXcgQXJyYXkobG9jLmxlbmd0aCk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld0xvYy5sZW5ndGg7IGkrKykge1xuICAgICAgICBuZXdMb2NbaV0gPSBsb2NbbmV3RGltW2ldXTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbmV3SW5kZXggPSByZXN1bHQubG9jVG9JbmRleChuZXdMb2MpO1xuICAgICAgcmVzdWx0VmFsdWVzW25ld0luZGV4XSA9IHZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgcG9vbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcixcbiAgICAgIHBvb2xUeXBlOiAnbWF4J3wnbWluJ3wnYXZnJykge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGRlcHRoXSA9IHguc2hhcGU7XG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93cywgeENvbHMsIGRlcHRoXSwgZlNpemUsIGRlcHRoLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCB4UkNvcm5lcik7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIGZTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZlNpemUgKyB4Q0Nvcm5lcik7XG5cblxuICAgICAgICAgIGxldCBtaW5NYXhWYWx1ZSA9XG4gICAgICAgICAgICAgIChwb29sVHlwZSA9PT0gJ21heCcgPyBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFkgOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgICAgICAgICBsZXQgYXZnVmFsdWUgPSAwO1xuXG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKGlzTmFOKHBpeGVsKSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGF2Z1ZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmICgocG9vbFR5cGUgPT09ICdtYXgnICYmIHBpeGVsID4gbWluTWF4VmFsdWUpIHx8XG4gICAgICAgICAgICAgICAgICAocG9vbFR5cGUgPT09ICdtaW4nICYmIHBpeGVsIDwgbWluTWF4VmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgbWluTWF4VmFsdWUgPSBwaXhlbDtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycpIHtcbiAgICAgICAgICAgICAgICBhdmdWYWx1ZSArPSBwaXhlbCAvIChmU2l6ZSAqIGZTaXplKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlzTmFOKG1pbk1heFZhbHVlKSkge1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgeS5zZXQocG9vbFR5cGUgPT09ICdhdmcnID8gYXZnVmFsdWUgOiBtaW5NYXhWYWx1ZSwgeVIsIHlDLCBkKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5wb29sKHgsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21heCcpO1xuICB9XG5cbiAgbWF4UG9vbFBvc2l0aW9ucyh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBkZXB0aF0gPSB4LnNoYXBlO1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKHguc2hhcGUsIGZTaXplLCBkZXB0aCwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IG1heFBvc2l0aW9ucyA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IG91dHB1dFNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgeFJDb3JuZXIpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCBmU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IG91dHB1dFNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIHhDQ29ybmVyKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCBmU2l6ZSArIHhDQ29ybmVyKTtcbiAgICAgICAgICBsZXQgbWF4VmFsdWUgPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgICAgICAgbGV0IG1heFBvc2l0aW9uID0gLTE7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKHBpeGVsID4gbWF4VmFsdWUpIHtcbiAgICAgICAgICAgICAgICBtYXhWYWx1ZSA9IHBpeGVsO1xuICAgICAgICAgICAgICAgIG1heFBvc2l0aW9uID0gd1IgKiBmU2l6ZSArIHdDO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIG1heFBvc2l0aW9ucy5zZXQobWF4UG9zaXRpb24sIHlSLCB5QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1heFBvc2l0aW9ucztcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sQmFja3Byb3BJbnRlcm5hbChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgICBvcmlnUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBtYXhQb3NpdGlvbnMgPSB0aGlzLm1heFBvb2xQb3NpdGlvbnMoeCwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgW2R5Um93cywgZHlDb2xzLCBkZXB0aF0gPSBkeS5zaGFwZTtcblxuICAgIC8vIERpbGF0ZSB0aGUgaW5wdXQuXG4gICAgY29uc3QgZHlSb3dzRGlsYXRlZCA9IChkeVJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IGR4Q29sc0RpbGF0ZWQgPSAoZHlDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbZHlSb3dzRGlsYXRlZCwgZHhDb2xzRGlsYXRlZCwgZGVwdGhdLCBmU2l6ZSwgZGVwdGgsIDEsIHBhZCk7XG4gICAgY29uc3QgZHggPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcblxuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgZHhSID0gMDsgZHhSIDwgZHguc2hhcGVbMF07ICsrZHhSKSB7XG4gICAgICAgIGZvciAobGV0IGR4QyA9IDA7IGR4QyA8IGR4LnNoYXBlWzFdOyArK2R4Qykge1xuICAgICAgICAgIC8vIFNoYWRlciBjb2RlIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCBkeVJDb3JuZXIgPSBkeFIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgZHlDQ29ybmVyID0gZHhDIC0gcGFkO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgICAgICAgIGNvbnN0IGR5UiA9IChkeVJDb3JuZXIgKyB3UikgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgaWYgKGR5UiA8IDAgfHwgZHlSID49IGR5Um93cyB8fCBNYXRoLmZsb29yKGR5UikgIT09IGR5Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCBkeUMgPSAoZHlDQ29ybmVyICsgd0MpIC8gb3JpZ1N0cmlkZTtcbiAgICAgICAgICAgICAgaWYgKGR5QyA8IDAgfHwgZHlDID49IGR5Q29scyB8fCBNYXRoLmZsb29yKGR5QykgIT09IGR5Qykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IG1heFBvcyA9IGZTaXplICogZlNpemUgLSAxIC0gbWF4UG9zaXRpb25zLmdldChkeVIsIGR5QywgZCk7XG4gICAgICAgICAgICAgIGNvbnN0IGN1clBvcyA9IHdSICogZlNpemUgKyB3QztcblxuICAgICAgICAgICAgICBjb25zdCBtYXNrID0gbWF4UG9zID09PSBjdXJQb3MgPyAxIDogMDtcbiAgICAgICAgICAgICAgaWYgKG1hc2sgPT09IDApIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0gZHkuZ2V0KGR5UiwgZHlDLCBkKTtcbiAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIG1hc2s7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGR4LnNldChkb3RQcm9kLCBkeFIsIGR4QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGR4O1xuICB9XG5cbiAgcHJvdGVjdGVkIG1pblBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnBvb2woeCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnbWluJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXZnUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIHRoaXMucG9vbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdhdmcnKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRCB7XG4gICAgY29uc3Qgb3V0cHV0ID0gQXJyYXkzRC56ZXJvcyhbbmV3U2hhcGUyRFswXSwgbmV3U2hhcGUyRFsxXSwgeC5zaGFwZVsyXV0pO1xuXG4gICAgY29uc3QgZWZmZWN0aXZlSW5wdXRTaXplID1cbiAgICAgICAgYWxpZ25Db3JuZXJzID8gW3guc2hhcGVbMF0gLSAxLCB4LnNoYXBlWzFdIC0gMSwgeC5zaGFwZVsyXV0gOiB4LnNoYXBlO1xuICAgIGNvbnN0IGVmZmVjdGl2ZU91dHB1dFNpemUgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgICBbb3V0cHV0LnNoYXBlWzBdIC0gMSwgb3V0cHV0LnNoYXBlWzFdIC0gMSwgb3V0cHV0LnNoYXBlWzJdXSA6XG4gICAgICAgIG91dHB1dC5zaGFwZTtcbiAgICBmb3IgKGxldCByID0gMDsgciA8IG91dHB1dC5zaGFwZVswXTsgcisrKSB7XG4gICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG91dHB1dC5zaGFwZVsxXTsgYysrKSB7XG4gICAgICAgIGZvciAobGV0IGQgPSAwOyBkIDwgb3V0cHV0LnNoYXBlWzJdOyBkKyspIHtcbiAgICAgICAgICAvLyBCZWdpbiBzaGFkZXIuXG5cbiAgICAgICAgICAvLyBDb21wdXRlIHRoZSBmcmFjdGlvbmFsIGluZGV4IG9mIHRoZSBzb3VyY2UuXG4gICAgICAgICAgY29uc3Qgc291cmNlRnJhY1JvdyA9XG4gICAgICAgICAgICAgIChlZmZlY3RpdmVJbnB1dFNpemVbMF0pICogciAvIChlZmZlY3RpdmVPdXRwdXRTaXplWzBdKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VGcmFjQ29sID1cbiAgICAgICAgICAgICAgKGVmZmVjdGl2ZUlucHV0U2l6ZVsxXSkgKiBjIC8gKGVmZmVjdGl2ZU91dHB1dFNpemVbMV0pO1xuXG4gICAgICAgICAgY29uc3Qgc291cmNlUm93Rmxvb3IgPSBNYXRoLmZsb29yKHNvdXJjZUZyYWNSb3cpO1xuICAgICAgICAgIGNvbnN0IHNvdXJjZVJvd0NlaWwgPVxuICAgICAgICAgICAgICBNYXRoLm1pbih4LnNoYXBlWzBdIC0gMSwgTWF0aC5jZWlsKHNvdXJjZUZyYWNSb3cpKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VDb2xGbG9vciA9IE1hdGguZmxvb3Ioc291cmNlRnJhY0NvbCk7XG4gICAgICAgICAgY29uc3Qgc291cmNlQ29sQ2VpbCA9XG4gICAgICAgICAgICAgIE1hdGgubWluKHguc2hhcGVbMV0gLSAxLCBNYXRoLmNlaWwoc291cmNlRnJhY0NvbCkpO1xuXG4gICAgICAgICAgY29uc3QgdG9wTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xGbG9vciwgZCk7XG4gICAgICAgICAgY29uc3QgYm90dG9tTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbEZsb29yLCBkKTtcbiAgICAgICAgICBjb25zdCB0b3BSaWdodCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xDZWlsLCBkKTtcbiAgICAgICAgICBjb25zdCBib3R0b21SaWdodCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbENlaWwsIGQpO1xuXG4gICAgICAgICAgY29uc3Qgcm93RnJhYyA9IHNvdXJjZUZyYWNSb3cgLSBzb3VyY2VSb3dGbG9vcjtcbiAgICAgICAgICBjb25zdCBjb2xGcmFjID0gc291cmNlRnJhY0NvbCAtIHNvdXJjZUNvbEZsb29yO1xuXG4gICAgICAgICAgY29uc3QgdG9wID0gdG9wTGVmdCArICh0b3BSaWdodCAtIHRvcExlZnQpICogY29sRnJhYztcbiAgICAgICAgICBjb25zdCBib3R0b20gPSBib3R0b21MZWZ0ICsgKGJvdHRvbVJpZ2h0IC0gYm90dG9tTGVmdCkgKiBjb2xGcmFjO1xuICAgICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gdG9wICsgKGJvdHRvbSAtIHRvcCkgKiByb3dGcmFjO1xuXG4gICAgICAgICAgb3V0cHV0LnNldChuZXdWYWx1ZSwgciwgYywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb24gPSAuMDAxLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhWYWx1ZXMgPSB4LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG1lYW5WYWx1ZXMgPSBtZWFuLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHZhcmlhbmNlVmFsdWVzID0gdmFyaWFuY2UuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3Qgc2NhbGVWYWx1ZXMgPSBzY2FsZSA/IHNjYWxlLmdldFZhbHVlcygpIDogbmV3IEZsb2F0MzJBcnJheShbMV0pO1xuICAgIGNvbnN0IG9mZnNldFZhbHVlcyA9IG9mZnNldCA/IG9mZnNldC5nZXRWYWx1ZXMoKSA6IG5ldyBGbG9hdDMyQXJyYXkoWzBdKTtcbiAgICBjb25zdCBvdXRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHhWYWx1ZXMubGVuZ3RoKTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgeFZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgb3V0VmFsdWVzW2ldID0gb2Zmc2V0VmFsdWVzW2kgJSBvZmZzZXRWYWx1ZXMubGVuZ3RoXSArXG4gICAgICAgICAgKHhWYWx1ZXNbaV0gLSBtZWFuVmFsdWVzW2kgJSBtZWFuVmFsdWVzLmxlbmd0aF0pICpcbiAgICAgICAgICAgICAgc2NhbGVWYWx1ZXNbaSAlIHNjYWxlVmFsdWVzLmxlbmd0aF0gL1xuICAgICAgICAgICAgICBNYXRoLnNxcnQoXG4gICAgICAgICAgICAgICAgICB2YXJpYW5jZVZhbHVlc1tpICUgdmFyaWFuY2VWYWx1ZXMubGVuZ3RoXSArIHZhcmlhbmNlRXBzaWxvbik7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oeC5zaGFwZSwge3ZhbHVlczogb3V0VmFsdWVzfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge1RleHR1cmVNYW5hZ2VyfSBmcm9tICcuL3dlYmdsL3RleHR1cmVfbWFuYWdlcic7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbi8vIFRoZXNlIGdsb2JhbCB2YXJpYWJsZXMgbmVlZCB0byBiZSBpbml0aWFsaXplZCB0byBudWxsIHNvIHRoYXQgY2xvc3VyZSBrbm93c1xuLy8gbm90IHRvIHNlYWwgdGhlbS5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IEdQR1BVOiBHUEdQVUNvbnRleHQgPSBudWxsITtcbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IFRFWFRVUkVfTUFOQUdFUjogVGV4dHVyZU1hbmFnZXIgPSBudWxsITtcblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTkRBcnJheURhdGEge1xuICB2YWx1ZXM/OiBGbG9hdDMyQXJyYXk7XG4gIHRleHR1cmU/OiBXZWJHTFRleHR1cmU7XG4gIC8qKiBbcm93cywgY29sdW1uc10gc2hhcGUgb2YgdGhlIHRleHR1cmUuICovXG4gIHRleHR1cmVTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXTtcbn1cblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplR1BVKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHRleHR1cmVNYW5hZ2VyOiBUZXh0dXJlTWFuYWdlcikge1xuICBHUEdQVSA9IGdwZ3B1O1xuICBURVhUVVJFX01BTkFHRVIgPSB0ZXh0dXJlTWFuYWdlcjtcbn1cblxuZnVuY3Rpb24gdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCkge1xuICBpZiAoR1BHUFUgPT0gbnVsbCB8fCBURVhUVVJFX01BTkFHRVIgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignR1BVIG5vdCBpbnRpYWxpemVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBOREFycmF5IHtcbiAgLyoqIFRoZSBzaGFwZSBvZiB0aGUgbmRhcnJheS4gKi9cbiAgc2hhcGU6IG51bWJlcltdO1xuICAvKiogTnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBuZGFycmF5LiAqL1xuICBzaXplOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE51bWJlciBvZiBlbGVtZW50cyB0byBza2lwIGluIGVhY2ggZGltZW5zaW9uIHdoZW4gaW5kZXhpbmcuIFNlZVxuICAgKiBodHRwczovL2RvY3Muc2NpcHkub3JnL2RvYy9udW1weS9yZWZlcmVuY2UvZ2VuZXJhdGVkL251bXB5Lm5kYXJyYXkuc3RyaWRlcy5odG1sXG4gICAqL1xuICBwcm90ZWN0ZWQgc3RyaWRlczogbnVtYmVyW107XG5cbiAgcHJpdmF0ZSBkYXRhOiBOREFycmF5RGF0YTtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3Ioc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIC8vIFNhbml0eSBjaGVja3MuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudmFsdWVzICE9IG51bGwgfHwgZGF0YS50ZXh0dXJlICE9IG51bGwsXG4gICAgICAgICdFaXRoZXIgYHZhbHVlc2Agb3IgYHRleHR1cmVgIG11c3QgYmUgZGVmaW5lZCcpO1xuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudGV4dHVyZSA9PSBudWxsIHx8IChkYXRhLnRleHR1cmVTaGFwZVJDICE9IG51bGwpLFxuICAgICAgICAnYHRleHR1cmVTaGFwZWAgbXVzdCBiZSBkZWZpbmVkIHdoZW4gYHRleHR1cmVgIGlzIGRlZmluZWQnKTtcblxuICAgIHRoaXMuc2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShzaGFwZSk7XG5cbiAgICBpZiAoZGF0YS52YWx1ZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy5zaXplID09PSBkYXRhLnZhbHVlcy5sZW5ndGgsXG4gICAgICAgICAgJ0NvbnN0cnVjdGluZyBuZGFycmF5IG9mIHNoYXBlICgnICsgdGhpcy5zaXplICsgJykgc2hvdWxkIG1hdGNoIHRoZScgK1xuICAgICAgICAgICAgICAnIGxlbmd0aCBvZiB2YWx1ZXMgKCcgKyBkYXRhLnZhbHVlcy5sZW5ndGggKyAnKScpO1xuICAgIH1cblxuICAgIHRoaXMuc2hhcGUgPSBzaGFwZTtcbiAgICB0aGlzLmRhdGEgPSBkYXRhO1xuICAgIGNvbnN0IGRpbSA9IHRoaXMuc2hhcGUubGVuZ3RoO1xuXG4gICAgaWYgKGRpbSA8IDIpIHtcbiAgICAgIHRoaXMuc3RyaWRlcyA9IFtdO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBMYXN0IGRpbWVuc2lvbiBoYXMgaW1wbGljaXQgc3RyaWRlIG9mIDEsIHRodXMgaGF2aW5nIEQtMSAoaW5zdGVhZCBvZiBEKVxuICAgICAgLy8gc3RyaWRlcy5cbiAgICAgIHRoaXMuc3RyaWRlcyA9IG5ldyBBcnJheShkaW0gLSAxKTtcbiAgICAgIHRoaXMuc3RyaWRlc1tkaW0gLSAyXSA9IHRoaXMuc2hhcGVbZGltIC0gMV07XG4gICAgICBmb3IgKGxldCBpID0gZGltIC0gMzsgaSA+PSAwOyAtLWkpIHtcbiAgICAgICAgdGhpcy5zdHJpZGVzW2ldID0gdGhpcy5zdHJpZGVzW2kgKyAxXSAqIHRoaXMuc2hhcGVbaSArIDFdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSBvZiB6ZXJvcyB3aXRoIHRoZSBzcGVjaWZpZWQgc2hhcGUuICovXG4gIHN0YXRpYyB6ZXJvczxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KHNoYXBlLCB7dmFsdWVzfSk7XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgb2YgemVyb3Mgd2l0aCB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgemVyb3NMaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3MoYW5vdGhlci5zaGFwZSkgYXMgVDtcbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSB3aXRoIHRoZSBzYW1lIHZhbHVlcy9zaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuICovXG4gIHN0YXRpYyBsaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gYW5vdGhlci5nZXRWYWx1ZXMoKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGFub3RoZXIuc2hhcGUsIHt2YWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ha2VzIGEgbmV3IG5kYXJyYXkgd2l0aCB0aGUgcHJvdmlkZWQgc2hhcGUgYW5kIHZhbHVlcy4gVmFsdWVzIHNob3VsZCBiZSBpblxuICAgKiBhIGZsYXQgYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgbWFrZTxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSk6IFQge1xuICAgIHN3aXRjaCAoc2hhcGUubGVuZ3RoKSB7XG4gICAgICBjYXNlIDA6XG4gICAgICAgIHJldHVybiBuZXcgU2NhbGFyKGRhdGEpIGFzIFQ7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTFEKGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IEFycmF5MkQoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgY2FzZSAzOlxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXkzRChzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgNDpcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTREKFxuICAgICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgICAgICAgICAgICBzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IE5EQXJyYXkoc2hhcGUsIGRhdGEpIGFzIGFueTtcbiAgICB9XG4gIH1cblxuICAvKiogUmVzaGFwZXMgdGhlIGN1cnJlbnQgbmRhcnJheSBpbnRvIHRoZSBwcm92aWRlZCBzaGFwZS4gKi9cbiAgcmVzaGFwZTxUIGV4dGVuZHMgTkRBcnJheT4obmV3U2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgaWYgKHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgbmV3U2hhcGUpKSB7XG4gICAgICAvLyBOby1vcC5cbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHJldHVybiB0aGlzIGFzIGFueTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5zaXplID09PSB1dGlsLnNpemVGcm9tU2hhcGUobmV3U2hhcGUpLFxuICAgICAgICAnbmV3IHNoYXBlIGFuZCBvbGQgc2hhcGUgbXVzdCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBlbGVtZW50cy4nKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHRoaXMuZGF0YSk7XG4gIH1cblxuICBhc1NjYWxhcigpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KHRoaXMuc2l6ZSA9PT0gMSwgJ1RoZSBhcnJheSBtdXN0IGhhdmUgb25seSAxIGVsZW1lbnQuJyk7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxTY2FsYXI+KFtdKTtcbiAgfVxuXG4gIGFzMUQoKTogQXJyYXkxRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTFEPihbdGhpcy5zaXplXSk7XG4gIH1cblxuICBhczJEKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogQXJyYXkyRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTJEPihbcm93cywgY29sdW1uc10pO1xuICB9XG5cbiAgYXMzRChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgZGVwdGg6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkzRD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoXSk7XG4gIH1cblxuICBhczREKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBkZXB0aDogbnVtYmVyLCBkZXB0aDI6IG51bWJlcik6IEFycmF5NEQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXk0RD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoLCBkZXB0aDJdKTtcbiAgfVxuXG4gIGdldCByYW5rKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc2hhcGUubGVuZ3RoO1xuICB9XG5cbiAgZ2V0KC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpW2luZGV4XTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCAuLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIHRoaXMuc2V0KHRoaXMuZ2V0KC4uLmxvY3MpICsgdmFsdWUsIC4uLmxvY3MpO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHRoaXMuZ2V0VmFsdWVzKClbaW5kZXhdID0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IG51bWJlcltdKTogbnVtYmVyIHtcbiAgICBsZXQgaW5kZXggPSBsb2NzW2xvY3MubGVuZ3RoIC0gMV07XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsb2NzLmxlbmd0aCAtIDE7ICsraSkge1xuICAgICAgaW5kZXggKz0gdGhpcy5zdHJpZGVzW2ldICogbG9jc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4O1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogbnVtYmVyW10ge1xuICAgIGNvbnN0IGxvY3M6IG51bWJlcltdID0gbmV3IEFycmF5KHRoaXMuc2hhcGUubGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBsb2NzW2ldID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlc1tpXSk7XG4gICAgICBpbmRleCAtPSBsb2NzW2ldICogdGhpcy5zdHJpZGVzW2ldO1xuICAgIH1cbiAgICBsb2NzW2xvY3MubGVuZ3RoIC0gMV0gPSBpbmRleDtcbiAgICByZXR1cm4gbG9jcztcbiAgfVxuXG4gIGZpbGwodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKCkuZmlsbCh2YWx1ZSk7XG4gIH1cblxuICBnZXREYXRhKCk6IE5EQXJyYXlEYXRhIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhO1xuICB9XG5cbiAgZ2V0VmFsdWVzKCk6IEZsb2F0MzJBcnJheSB7XG4gICAgaWYgKHRoaXMuZGF0YS52YWx1ZXMgPT0gbnVsbCkge1xuICAgICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgICB0aGlzLmRhdGEudmFsdWVzID0gR1BHUFUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyFbMF0sXG4gICAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDIVsxXSk7XG4gICAgICB0aGlzLmRpc3Bvc2VUZXh0dXJlKCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRhdGEudmFsdWVzO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGxvYWRUb0dQVShwcmVmZXJyZWRUZXhTaGFwZT86IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgICB0aHJvd0lmR1BVTm90SW5pdGlhbGl6ZWQoKTtcbiAgICB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMgPSB3ZWJnbF91dGlsLmdldFRleHR1cmVTaGFwZUZyb21Mb2dpY2FsU2hhcGUoXG4gICAgICAgIEdQR1BVLmdsLCB0aGlzLnNoYXBlLCBwcmVmZXJyZWRUZXhTaGFwZSk7XG4gICAgdGhpcy5kYXRhLnRleHR1cmUgPVxuICAgICAgICBURVhUVVJFX01BTkFHRVIuYWNxdWlyZVRleHR1cmUodGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDKTtcblxuICAgIEdQR1BVLnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDWzFdLCB0aGlzLmRhdGEudmFsdWVzISk7XG5cbiAgICB0aGlzLmRhdGEudmFsdWVzID0gbnVsbCE7XG4gIH1cblxuICBnZXRUZXh0dXJlKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogV2ViR0xUZXh0dXJlIHtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgPT0gbnVsbCkge1xuICAgICAgdGhpcy51cGxvYWRUb0dQVShwcmVmZXJyZWRTaGFwZVJDKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlITtcbiAgfVxuXG4gIGdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyA9PSBudWxsKSB7XG4gICAgICB0aGlzLnVwbG9hZFRvR1BVKHByZWZlcnJlZFNoYXBlUkMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDITtcbiAgfVxuXG4gIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5kYXRhLnZhbHVlcyA9IG51bGwhO1xuICAgIHRoaXMuc2hhcGUgPSBudWxsITtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgdGhpcy5kaXNwb3NlVGV4dHVyZSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZGlzcG9zZVRleHR1cmUoKSB7XG4gICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgVEVYVFVSRV9NQU5BR0VSLnJlbGVhc2VUZXh0dXJlKFxuICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyEpO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlID0gbnVsbCE7XG4gICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDID0gbnVsbCE7XG4gIH1cblxuICBpbkdQVSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbDtcbiAgfVxuXG4gIGVxdWFscyh0OiBOREFycmF5KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgdC5zaGFwZSkgJiZcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLmdldFZhbHVlcygpLCB0LmdldFZhbHVlcygpKTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIHJhbmRGdW5jdGlvbjogKCkgPT4gbnVtYmVyKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIHZhbHVlc1tpXSA9IHJhbmRGdW5jdGlvbigpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oc2hhcGUsIHt2YWx1ZXN9KTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kTm9ybWFsPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2KSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFRydW5jYXRlZE5vcm1hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2LCB0cnVlKSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFVuaWZvcm08VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSwgYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgICByZXR1cm4gTkRBcnJheS5yYW5kPFQ+KHNoYXBlLCAoKSA9PiB1dGlsLnJhbmRVbmlmb3JtKGEsIGIpKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2NhbGFyIGV4dGVuZHMgTkRBcnJheSB7XG4gIGNvbnN0cnVjdG9yKGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgaWYgKGRhdGEudGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICBkYXRhLnRleHR1cmVTaGFwZVJDID0gWzEsIDFdO1xuICAgIH1cbiAgICBzdXBlcihbXSwgZGF0YSk7XG4gIH1cblxuICBzdGF0aWMgbmV3KHZhbHVlOiBudW1iZXIpIHtcbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KFt2YWx1ZV0pfSk7XG4gIH1cblxuICBzdGF0aWMgWkVSTyA9IFNjYWxhci5uZXcoMCk7XG4gIHN0YXRpYyBPTkUgPSBTY2FsYXIubmV3KDEpO1xuICBzdGF0aWMgVFdPID0gU2NhbGFyLm5ldygyKTtcbiAgc3RhdGljIE5FR19PTkUgPSBTY2FsYXIubmV3KC0xKTtcblxuICBnZXQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVswXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVswXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpWzBdICs9IHZhbHVlO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTFEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyXTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIGNvbnN0IHNoYXBlID0gKGRhdGEudmFsdWVzICE9IG51bGwpID9cbiAgICAgICAgW2RhdGEudmFsdWVzLmxlbmd0aF0gOlxuICAgICAgICBbdXRpbC5zaXplRnJvbVNoYXBlKGRhdGEudGV4dHVyZVNoYXBlUkMhKV07XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICB9XG5cbiAgc3RhdGljIG5ldyh2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGluZmVycmVkU2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAgIGBFcnJvciBjb25zdHJ1Y3RpbmcgQXJyYXkxRC4gU2hhcGUgb2YgdmFsdWVzICR7aW5mZXJyZWRTaGFwZX0gaXMgYCArXG4gICAgICAgICAgICAgIGBub3QgMSBkaW1lbnNpb25hbC5gKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTFEKHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVtpXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW2ldID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVtpXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jOiBbbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIGxvY1swXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXJdIHtcbiAgICByZXR1cm4gW2luZGV4XTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlcl0pOiBBcnJheTFEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTFEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5MkQgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3Ioc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAyLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAyJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcoXG4gICAgICBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdmFsdWVzOiBGbG9hdDMyQXJyYXl8bnVtYmVyW118bnVtYmVyW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5MkQuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTJEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgal0gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIGxvY3NbMV07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgICByZXR1cm4gW01hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTApLCBpbmRleCAlIHRoaXMuc3RyaWRlMF07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTJEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5M0QgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAzLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAzJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gIH1cblxuICBzdGF0aWMgbmV3KFxuICAgICAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5M0QuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTNEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsga10gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0pOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnN0cmlkZTAgKiBsb2NzWzBdICsgdGhpcy5zdHJpZGUxICogbG9jc1sxXSArIGxvY3NbMl07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICAgIGNvbnN0IGkgPSBNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUwKTtcbiAgICBpbmRleCAtPSBpICogdGhpcy5zdHJpZGUwO1xuICAgIHJldHVybiBbaSwgTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSksIGluZGV4ICUgdGhpcy5zdHJpZGUxXTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkzRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTREIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTI6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSA0LCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCA0Jyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gICAgdGhpcy5zdHJpZGUyID0gdGhpcy5zdHJpZGVzWzJdO1xuICB9XG5cbiAgc3RhdGljIG5ldyhcbiAgICAgIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXVtdKSB7XG4gICAgaWYgKCEodmFsdWVzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSkge1xuICAgICAgY29uc3QgaW5mZXJyZWRTaGFwZSA9IHV0aWwuaW5mZXJTaGFwZSh2YWx1ZXMpO1xuICAgICAgaWYgKGluZmVycmVkU2hhcGUubGVuZ3RoID4gMSkge1xuICAgICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKFxuICAgICAgICAgICAgc2hhcGUsIGluZmVycmVkU2hhcGUsXG4gICAgICAgICAgICBgRXJyb3Igd2hlbiBjb25zdHJ1Y3RpbmcgQXJyYXk0RC4gU2hhcGUgb2YgdmFsdWVzIGAgK1xuICAgICAgICAgICAgICAgIGAke2luZmVycmVkU2hhcGV9IGRvZXMgbm90IG1hdGNoIHRoZSBwcm92aWRlZCBzaGFwZSBgICtcbiAgICAgICAgICAgICAgICBgJHtzaGFwZX0uIGApO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbmV3IEFycmF5NEQoc2hhcGUsIHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVxuICAgICAgICBbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyB0aGlzLnN0cmlkZTIgKiBrICsgbF0gKz0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIHRoaXMuc3RyaWRlMSAqIGxvY3NbMV0gK1xuICAgICAgICB0aGlzLnN0cmlkZTIgKiBsb2NzWzJdICsgbG9jc1szXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBpID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMCk7XG4gICAgaW5kZXggLT0gaSAqIHRoaXMuc3RyaWRlMDtcbiAgICBjb25zdCBqID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSk7XG4gICAgaW5kZXggLT0gaiAqIHRoaXMuc3RyaWRlMTtcbiAgICByZXR1cm4gW2ksIGosIE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTIpLCBpbmRleCAlIHRoaXMuc3RyaWRlMl07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXk0RCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXk0RD4oc2hhcGUpO1xuICB9XG59XG5cbnR5cGUgQXJyYXlEYXRhID0gRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW118bnVtYmVyW11bXVtdfG51bWJlcltdW11bXVtdO1xuXG5mdW5jdGlvbiB0b1R5cGVkQXJyYXkoYTogQXJyYXlEYXRhKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIChhIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSA/IGEgOiBuZXcgRmxvYXQzMkFycmF5KHV0aWwuZmxhdHRlbihhKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuXG5pbXBvcnQgKiBhcyBjb252X2dwdSBmcm9tICcuL2NvbnZfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJXZWlnaHRzU291cmNlKFxuICAgIHhTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsXG4gICAgb3V0cHV0RGVwdGg6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHplcm9QYWQ6IG51bWJlcikge1xuICBjb25zdCBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCA9XG4gICAgICBjb252X2dwdS5nZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk7XG4gIGNvbnN0IGlucHV0RGVwdGggPSB4U2hhcGVSb3dDb2xEZXB0aFsyXTtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUm93Q29sRGVwdGgpO1xuXG4gIGNvbnN0IHlTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgIHhTaGFwZVJvd0NvbERlcHRoLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gIGNvbnN0IHlOdW1Sb3dzID0geVNoYXBlWzBdO1xuICBjb25zdCB5TnVtQ29scyA9IHlTaGFwZVsxXTtcbiAgY29uc3QgeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHlTaGFwZSk7XG5cbiAgY29uc3QgZlNpemVUaW1lc0lucHV0RGVwdGggPSBmU2l6ZSAqIGlucHV0RGVwdGg7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG4gIGA7XG5cbiAgcmV0dXJuIHByb2xvZ3VlICsgJ1xcbicgKyBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCArICdcXG4nICtcbiAgICAgIGBcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG4gICAgY29uc3QgdmVjMiBkeVNoYXBlQ1IgPSB2ZWMyKCR7eVRleFNoYXBlUkNbMV19LCAke3lUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHdUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh3VGV4Uiwgd1RleEMpIHRvIDREICh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICBmbG9hdCB3UiA9IGZsb29yKHdUZXhDUi55IC8gJHtmU2l6ZVRpbWVzSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCB3VGV4UkxlZnRvdmVyID0gd1RleENSLnkgLSB3UiAqICR7ZlNpemVUaW1lc0lucHV0RGVwdGh9LjA7XG4gICAgICBmbG9hdCB3QyA9IGZsb29yKHdUZXhSTGVmdG92ZXIgLyAke2lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDEgPSBtb2Qod1RleFJMZWZ0b3ZlciwgJHtpbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQyID0gd1RleENSLng7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggZHkoOiwgOiwgZDIpIHRvIGdldCBkdyh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHlSID0gMC4wOyB5UiA8ICR7eU51bVJvd3N9LjA7IHlSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHdSICsgeVIgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcbiAgICAgICAgZm9yIChmbG9hdCB5QyA9IDAuMDsgeUMgPCAke3lOdW1Db2xzfS4wOyB5QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHdDICsgeUMgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcblxuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh4UiwgeEMsIGQxKSB0byAyRCAoeFRleFIsIHhUZXhDKS5cbiAgICAgICAgICAvLyBNYXAgZnJvbSAzRCAoeVIsIHlDLCBkMikgdG8gMkQgKHlUZXhSLCB5VGV4QykuXG4gICAgICAgICAgdmVjMiB4eVRleEMgPSB2ZWMyKHhDLCB5QykgKiB2ZWMyKCR7aW5wdXREZXB0aH0uMCwgJHtvdXRwdXREZXB0aH0uMCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgdmVjMihkMSwgZDIpO1xuICAgICAgICAgIGZsb2F0IHhUZXhDID0geHlUZXhDLng7XG4gICAgICAgICAgZmxvYXQgeVRleEMgPSB4eVRleEMueTtcblxuICAgICAgICAgIC8vIFJlYWQgZHkoeVIsIHlDLCBkMikuXG4gICAgICAgICAgdmVjMiBkeVVWID0gKHZlYzIoeVRleEMsIHlUZXhSKSArIGhhbGZDUikgLyBkeVNoYXBlQ1I7XG4gICAgICAgICAgZmxvYXQgZHlWYWx1ZSA9IHRleHR1cmUyRChkeSwgZHlVVikucjtcblxuICAgICAgICAgIC8vIFJlYWQgeCh4UiwgeEMsIGQxKSAocG90ZW50aWFsbHkgemVyby1wYWRkZWQpLlxuICAgICAgICAgIGZsb2F0IHhWYWx1ZSA9XG4gICAgICAgICAgICBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCh4LCB4U2hhcGVDUiwgdmVjMih4VGV4QywgeFRleFIpKTtcblxuICAgICAgICAgIGRvdFByb2QgKz0gKHhWYWx1ZSAqIGR5VmFsdWUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvcmlnSW5wdXREZXB0aDogbnVtYmVyLFxuICAgIG9yaWdTdHJpZGU6IG51bWJlciwgb3JpZ1BhZDogbnVtYmVyLCBoYXNCaWFzOiBib29sZWFuKSB7XG4gIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gIGNvbnN0IFt4Um93cywgeENvbHMsIG9yaWdPdXRwdXREZXB0aF0gPSB4U2hhcGVSQ0Q7XG5cbiAgY29uc3QgeFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHhTaGFwZVJDRCk7XG4gIGNvbnN0IHdUZXhTaGFwZVJDID1cbiAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKG9yaWdJbnB1dERlcHRoLCBvcmlnT3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICBjb25zdCBnZXRCaWFzVmFsdWUgPSBoYXNCaWFzID9cbiAgICAgIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKG9yaWdJbnB1dERlcHRoKSA6XG4gICAgICAnJztcbiAgY29uc3QgYmlhc1Byb2xvZ3VlID0gaGFzQmlhcyA/ICd1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7JyA6ICcnO1xuICBjb25zdCBiaWFzT3BlcmF0aW9uID0gaGFzQmlhcyA/ICdkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTsnIDogJyc7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICAke2JpYXNQcm9sb2d1ZX1cbiAgICBgO1xuXG4gIHJldHVybiBwcm9sb2d1ZSArICdcXG4nICsgZ2V0Qmlhc1ZhbHVlICsgJ1xcbicgK1xuICAgICAgYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAtIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IHhSQ29ybmVyID0geFJDQ29ybmVyLng7XG4gICAgICBmbG9hdCB4Q0Nvcm5lciA9IHhSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBDb252b2x2ZSB4KD8sID8sIGQxKSB3aXRoIHcoOiwgOiwgZDIsIGQxKSB0byBnZXQgeSh5UiwgeUMsIGQyKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG5cbiAgICAgICAgZmxvYXQgeFIgPSAoeFJDb3JuZXIgKyB3UikgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgIC8vIFRPRE8oc21pbGtvdik6IFNwbGljZSB0aGlzIHdpdGggYW5vdGhlciB2ZXJzaW9uIHdoZXJlIHlvdSBjYWxsXG4gICAgICAgIC8vIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKCkuIEhlcmUgYW5kIGJlbG93LlxuICAgICAgICBpZiAoeFIgPCAwLjAgfHwgeFIgPj0gJHt4Um93c30uMCB8fCBmcmFjdCh4UikgPiAwLjApIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZsb2F0IHdSUGVybSA9ICR7ZlNpemV9LjAgLSAxLjAgLSB3UjtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHdDID0gMC4wOyB3QyA8ICR7ZlNpemV9LjA7IHdDICs9IDEuMCkge1xuXG4gICAgICAgICAgZmxvYXQgeEMgPSAoeENDb3JuZXIgKyB3QykgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgICAgaWYgKHhDIDwgMC4wIHx8IHhDID49ICR7eENvbHN9LjAgfHwgZnJhY3QoeEMpID4gMC4wKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBmbG9hdCB3Q1Blcm0gPSAke2ZTaXplfS4wIC0gMS4wIC0gd0M7XG4gICAgICAgICAgZmxvYXQgd1RleFIgPSB3UlBlcm0gKiAke2ZTaXplfS4wICogJHtvcmlnSW5wdXREZXB0aH0uMCArXG4gICAgICAgICAgICAgICAgICAgICAgICB3Q1Blcm0gKiAke29yaWdJbnB1dERlcHRofS4wICsgZDI7XG5cbiAgICAgICAgICBmb3IgKGZsb2F0IGQxID0gMC4wOyBkMSA8ICR7b3JpZ091dHB1dERlcHRofS4wOyBkMSArPSAxLjApIHtcbiAgICAgICAgICAgIGZsb2F0IHhUZXhDID0geEMgKiAke29yaWdPdXRwdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleEMgPSBkMTtcblxuICAgICAgICAgICAgLy8gUmVhZCB4KHhSLCB4QywgZDEpLlxuICAgICAgICAgICAgdmVjMiB4VVYgPSAodmVjMih4VGV4QywgeFRleFIpICsgaGFsZkNSKSAvIHhTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgeFZhbHVlID0gdGV4dHVyZTJEKHgsIHhVVikucjtcblxuICAgICAgICAgICAgLy8gUmVhZCB3KHdSUGVybSwgd0NQZXJtLCBkMiwgZDEpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAke2JpYXNPcGVyYXRpb259XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJCaWFzU291cmNlKFxuICAgIGR5U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuICBjb25zdCBbeU51bVJvd3MsIHlOdW1Db2xzLCBvdXRwdXREZXB0aF0gPSBkeVNoYXBlUkNEO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGR5O1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHtkeVRleFNoYXBlUkNbMV19LCAke2R5VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBiaWFzVGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBUaGUgYmlhcyB0ZXh0dXJlIFJDIHNoYXBlIGlzIFsxLCBkMl0uXG4gICAgICBmbG9hdCBkMiA9IGJpYXNUZXhDUi54O1xuXG4gICAgICBmbG9hdCBkZXJCaWFzID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB5UiA9IDAuMDsgeVIgPCAke3lOdW1Sb3dzfS4wOyB5UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHlDID0gMC4wOyB5QyA8ICR7eU51bUNvbHN9LjA7IHlDICs9IDEuMCkge1xuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh5UiwgeUMsIGQyKSB0byAyRCAoeVRleFIsIHlUZXhDKS5cbiAgICAgICAgICBmbG9hdCB5VGV4QyA9IHlDICogJHtvdXRwdXREZXB0aH0uMCArIGQyO1xuXG4gICAgICAgICAgLy8gUmVhZCBkeSh5UiwgeUMsIGQyKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMih5VGV4QywgeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgZGVyQmlhcyArPSBkeVZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRlckJpYXMsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVyQmlhcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGR5VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZXJXZWlnaHRzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeFRleDogV2ViR0xUZXh0dXJlLFxuICAgIGR5VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoZHlUZXgsICdkeScsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29udlRyYW5zcG9zZShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHhUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICB3ZWlnaHRzVGV4OiBXZWJHTFRleHR1cmUsIGJpYXNlc1RleDogV2ViR0xUZXh0dXJlfG51bGwsXG4gICAgcmVzdWx0VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUod2VpZ2h0c1RleCwgJ3dlaWdodHMnLCAxKTtcbiAgaWYgKGJpYXNlc1RleCAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGJpYXNlc1RleCwgJ2JpYXNlcycsIDIpO1xuICB9XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoaW4gc2FtcGxlcjJEIG1hdHJpeCwgdmVjMiBtYXRyaXhTaGFwZUNSLFxuICAgICAgICB2ZWMyIHJlcXVlc3RlZENSKSB7XG4gICAgICB2ZWMyIHV2ID0gKHJlcXVlc3RlZENSICsgdmVjMigwLjUsIDAuNSkpIC8gbWF0cml4U2hhcGVDUjtcbiAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeCwgdXYpLnI7XG4gICAgICBib29sIGxlc3NUaGFuWmVybyA9IGFueShsZXNzVGhhbih1diwgdmVjMigwLCAwKSkpO1xuICAgICAgYm9vbCBncmVhdGVyVGhhbk9uZSA9IGFueShncmVhdGVyVGhhbih1diwgdmVjMigxLCAxKSkpO1xuICAgICAgYm9vbCBvdXRzaWRlID0gbGVzc1RoYW5aZXJvIHx8IGdyZWF0ZXJUaGFuT25lO1xuICAgICAgcmV0dXJuIG1peCh2YWx1ZSwgMC4wLCBmbG9hdChvdXRzaWRlKSk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlciwgaGFzQmlhczogYm9vbGVhbikge1xuICBjb25zdCBbeFJvd3MsIHhDb2xzLCBpbnB1dERlcHRoXSA9IHhTaGFwZVJDRDtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcbiAgY29uc3Qgd1RleFNoYXBlUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICByZXR1cm4gYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgd1RleEMgPSBkMjtcblxuICAgICAgdmVjMiB4UkNDb3JuZXIgPSB2ZWMyKHlSLCB5QykgKiB2ZWMyKCR7c3RyaWRlfSwgJHtzdHJpZGV9KSAtXG4gICAgICAgICAgdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggdyg6LCA6LCBkMSwgZDIpIHRvIGdldCB5KHlSLCB5QywgZDIpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeFIgPSB4UkNvcm5lciArIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG4gICAgICAgICAgZmxvYXQgeEMgPSB4Q0Nvcm5lciArIHdDO1xuXG4gICAgICAgICAgZm9yIChmbG9hdCBkMSA9IDAuMDsgZDEgPCAke2lucHV0RGVwdGh9LjA7IGQxICs9IDEuMCkge1xuICAgICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleFIgPSB3UiAqICR7ZlNpemUgKiBpbnB1dERlcHRofS4wICtcbiAgICAgICAgICAgICAgICB3QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuXG4gICAgICAgICAgICBmbG9hdCB4VmFsdWUgPVxuICAgICAgICAgICAgICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKHgsIHhTaGFwZUNSLCB2ZWMyKHhUZXhDLCB4VGV4UikpO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHcod1IsIHdDLCBkMSwgZDIpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoJHtoYXNCaWFzfSkge1xuICAgICAgICBkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTtcbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldEJpYXNWYWx1ZVNvdXJjZShvdXRwdXREZXB0aDogbnVtYmVyKTpcbiAgICBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGZsb2F0IGdldEJpYXNWYWx1ZShpbiBzYW1wbGVyMkQgYmlhcywgZmxvYXQgYmlhc0MpIHtcbiAgICAgIGNvbnN0IHZlYzIgYmlhc1NoYXBlQ1IgPSB2ZWMyKCR7b3V0cHV0RGVwdGh9LCAxKTtcbiAgICAgIHZlYzIgYmlhc0NSID0gdmVjMihtb2QoYmlhc0MsICR7b3V0cHV0RGVwdGh9LjApLCAwKTtcbiAgICAgIHZlYzIgYmlhc1VWID0gKGJpYXNDUiArIHZlYzIoMC41LCAwLjUpKSAvIGJpYXNTaGFwZUNSO1xuICAgICAgcmV0dXJuIHRleHR1cmUyRChiaWFzLCBiaWFzVVYpLnI7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBhU2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCByZXN1bHREZXB0aDogbnVtYmVyLFxuICAgIGZpZWxkU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZDogbnVtYmVyLFxuICAgIGhhc0JpYXM6IGJvb2xlYW4pOiBzdHJpbmcge1xuICBjb25zdCBhU2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGFTaGFwZVJvd0NvbERlcHRoKTtcblxuICBjb25zdCB3ZWlnaHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID0gY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgICBhU2hhcGVSb3dDb2xEZXB0aFsyXSwgcmVzdWx0RGVwdGgsIGZpZWxkU2l6ZSk7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk7XG4gIGNvbnN0IGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkID1cbiAgICAgIGdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTtcbiAgY29uc3QgY29udm9sdmUgPSBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgICAgYVNoYXBlUm93Q29sRGVwdGgsIGZpZWxkU2l6ZSwgcmVzdWx0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCwgaGFzQmlhcyk7XG4gIGNvbnN0IGdldEJpYXNWYWx1ZSA9IGdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKHJlc3VsdERlcHRoKTtcblxuICByZXR1cm4gW1xuICAgIHByb2xvZ3VlLFxuICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkLFxuICAgIGdldEJpYXNWYWx1ZSxcbiAgICBjb252b2x2ZSxcbiAgXS5qb2luKCdcXG4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZvbHZlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHdlaWdodHM6IFdlYkdMVGV4dHVyZSwgYmlhc2VzOiBXZWJHTFRleHR1cmV8bnVsbCwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh3ZWlnaHRzLCAnd2VpZ2h0cycsIDEpO1xuICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYmlhc2VzLCAnYmlhc2VzJywgMik7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGdwZ3B1X3V0aWwgZnJvbSAnLi9ncGdwdV91dGlsJztcbmltcG9ydCAqIGFzIHRleF91dGlsIGZyb20gJy4vdGV4X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5pbXBvcnQge1dlYkdMTG9zZUNvbnRleHRFeHRlbnNpb259IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBjbGFzcyBHUEdQVUNvbnRleHQge1xuICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB0ZXh0dXJlRmxvYXRFeHRlbnNpb246IHt9O1xuICBjb2xvckJ1ZmZlckZsb2F0RXh0ZW5zaW9uOiB7fTtcbiAgbG9zZUNvbnRleHRFeHRlbnNpb246IFdlYkdMTG9zZUNvbnRleHRFeHRlbnNpb247XG4gIHZlcnRleEJ1ZmZlcjogV2ViR0xCdWZmZXI7XG4gIGluZGV4QnVmZmVyOiBXZWJHTEJ1ZmZlcjtcbiAgZnJhbWVidWZmZXI6IFdlYkdMRnJhbWVidWZmZXI7XG4gIG91dHB1dFRleHR1cmU6IFdlYkdMVGV4dHVyZXxudWxsID0gbnVsbDtcbiAgcHJvZ3JhbTogV2ViR0xQcm9ncmFtfG51bGwgPSBudWxsO1xuICBwcml2YXRlIGRpc3Bvc2VkID0gZmFsc2U7XG4gIHByaXZhdGUgYXV0b0RlYnVnVmFsaWRhdGUgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3RvcihnbD86IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICAgIGlmIChnbCAhPSBudWxsKSB7XG4gICAgICB0aGlzLmdsID0gZ2w7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZ2wgPSBncGdwdV91dGlsLmNyZWF0ZVdlYkdMQ29udGV4dCgpO1xuICAgIH1cblxuICAgIC8vIFdlYkdMIDIuMCBlbmFibGVzIHRleHR1cmUgZmxvYXRzIHdpdGhvdXQgYW4gZXh0ZW5zaW9uLlxuICAgIGlmICghd2ViZ2xfdXRpbC5pc1dlYkdMMkVuYWJsZWQoKSkge1xuICAgICAgdGhpcy50ZXh0dXJlRmxvYXRFeHRlbnNpb24gPVxuICAgICAgICAgIHdlYmdsX3V0aWwuZ2V0RXh0ZW5zaW9uT3JUaHJvdyh0aGlzLmdsLCAnT0VTX3RleHR1cmVfZmxvYXQnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jb2xvckJ1ZmZlckZsb2F0RXh0ZW5zaW9uID1cbiAgICAgICAgICB3ZWJnbF91dGlsLmdldEV4dGVuc2lvbk9yVGhyb3codGhpcy5nbCwgJ0VYVF9jb2xvcl9idWZmZXJfZmxvYXQnKTtcbiAgICB9XG5cbiAgICB0aGlzLmxvc2VDb250ZXh0RXh0ZW5zaW9uID1cbiAgICAgICAgd2ViZ2xfdXRpbC5nZXRFeHRlbnNpb25PclRocm93KHRoaXMuZ2wsICdXRUJHTF9sb3NlX2NvbnRleHQnKSBhc1xuICAgICAgICBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICAgIHRoaXMudmVydGV4QnVmZmVyID0gZ3BncHVfdXRpbC5jcmVhdGVWZXJ0ZXhCdWZmZXIodGhpcy5nbCk7XG4gICAgdGhpcy5pbmRleEJ1ZmZlciA9IGdwZ3B1X3V0aWwuY3JlYXRlSW5kZXhCdWZmZXIodGhpcy5nbCk7XG4gICAgdGhpcy5mcmFtZWJ1ZmZlciA9IHdlYmdsX3V0aWwuY3JlYXRlRnJhbWVidWZmZXIodGhpcy5nbCk7XG4gIH1cblxuICBwdWJsaWMgZGlzcG9zZSgpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGlmICh0aGlzLnByb2dyYW0gIT0gbnVsbCkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICdEaXNwb3NpbmcgYSBHUEdQVUNvbnRleHQgdGhhdCBzdGlsbCBoYXMgYSBib3VuZCBXZWJHTFByb2dyYW0uJyArXG4gICAgICAgICAgJyBUaGlzIGlzIHByb2JhYmx5IGEgcmVzb3VyY2UgbGVhaywgZGVsZXRlIHRoZSBwcm9ncmFtIHdpdGggJyArXG4gICAgICAgICAgJ0dQR1BVQ29udGV4dC5kZWxldGVQcm9ncmFtIGJlZm9yZSBkaXNwb3NpbmcuJyk7XG4gICAgfVxuICAgIGlmICh0aGlzLm91dHB1dFRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICdEaXNwb3NpbmcgYSBHUEdQVUNvbnRleHQgdGhhdCBzdGlsbCBoYXMgYSBib3VuZCBvdXRwdXQgbWF0cml4ICcgK1xuICAgICAgICAgICd0ZXh0dXJlLiAgVGhpcyBpcyBwcm9iYWJseSBhIHJlc291cmNlIGxlYWssIGRlbGV0ZSB0aGUgb3V0cHV0ICcgK1xuICAgICAgICAgICdtYXRyaXggdGV4dHVyZSB3aXRoIEdQR1BVQ29udGV4dC5kZWxldGVNYXRyaXhUZXh0dXJlIGJlZm9yZSAnICtcbiAgICAgICAgICAnZGlzcG9zaW5nLicpO1xuICAgIH1cbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmZpbmlzaCgpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEZyYW1lYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBudWxsKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZUZyYW1lYnVmZmVyKHRoaXMuZnJhbWVidWZmZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5BUlJBWV9CVUZGRVIsIG51bGwpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlQnVmZmVyKHRoaXMudmVydGV4QnVmZmVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICAgIGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBudWxsKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZUJ1ZmZlcih0aGlzLmluZGV4QnVmZmVyKSk7XG4gICAgdGhpcy5sb3NlQ29udGV4dEV4dGVuc2lvbi5sb3NlQ29udGV4dCgpO1xuICAgIHRoaXMuZGlzcG9zZWQgPSB0cnVlO1xuICB9XG5cbiAgcHVibGljIGVuYWJsZUF1dG9tYXRpY0RlYnVnVmFsaWRhdGlvbihlbmFibGVkOiBib29sZWFuKSB7XG4gICAgdGhpcy5hdXRvRGVidWdWYWxpZGF0ZSA9IGVuYWJsZWQ7XG4gICAgd2ViZ2xfdXRpbC5lbmFibGVEZWJ1Z1dlYkdMRXJyb3JDaGVja2luZyhlbmFibGVkKTtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHJldHVybiBncGdwdV91dGlsLmNyZWF0ZU1hdHJpeFRleHR1cmUodGhpcy5nbCwgcm93cywgY29sdW1ucyk7XG4gIH1cblxuICBwdWJsaWMgdXBsb2FkUGl4ZWxEYXRhVG9UZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLFxuICAgICAgcGl4ZWxzOiBJbWFnZURhdGF8SFRNTEltYWdlRWxlbWVudHxIVE1MQ2FudmFzRWxlbWVudHxIVE1MVmlkZW9FbGVtZW50KSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBncGdwdV91dGlsLnVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZSh0aGlzLmdsLCB0ZXh0dXJlLCBwaXhlbHMpO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZVBhY2tlZE1hdHJpeFRleHR1cmUocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOlxuICAgICAgV2ViR0xUZXh0dXJlIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHJldHVybiBncGdwdV91dGlsLmNyZWF0ZVBhY2tlZE1hdHJpeFRleHR1cmUodGhpcy5nbCwgcm93cywgY29sdW1ucyk7XG4gIH1cblxuICBwdWJsaWMgZGVsZXRlTWF0cml4VGV4dHVyZSh0ZXh0dXJlOiBXZWJHTFRleHR1cmUpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGlmICh0aGlzLm91dHB1dFRleHR1cmUgPT09IHRleHR1cmUpIHtcbiAgICAgIHdlYmdsX3V0aWwudW5iaW5kQ29sb3JUZXh0dXJlRnJvbUZyYW1lYnVmZmVyKHRoaXMuZ2wsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgICAgdGhpcy5vdXRwdXRUZXh0dXJlID0gbnVsbDtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5kZWxldGVUZXh0dXJlKHRleHR1cmUpKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGNvbnN0IG51bUNoYW5uZWxzID0gMTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICAgIHRoaXMuZ2wsIHRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIG1hdHJpeCwgbnVtQ2hhbm5lbHMpO1xuICB9XG5cbiAgcHVibGljIHVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwudXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgICAgICB0aGlzLmdsLCB0ZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBtYXRyaXgpO1xuICB9XG5cbiAgcHVibGljIGRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5kb3dubG9hZE1hdHJpeERyaXZlcihcbiAgICAgICAgdGV4dHVyZSxcbiAgICAgICAgKCkgPT5cbiAgICAgICAgICAgIGdwZ3B1X3V0aWwuZG93bmxvYWRNYXRyaXhGcm9tT3V0cHV0VGV4dHVyZSh0aGlzLmdsLCByb3dzLCBjb2x1bW5zKSk7XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRNYXRyaXhGcm9tUGFja2VkVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICAgIHJldHVybiB0aGlzLmRvd25sb2FkTWF0cml4RHJpdmVyKFxuICAgICAgICB0ZXh0dXJlLFxuICAgICAgICAoKSA9PiBncGdwdV91dGlsLmRvd25sb2FkTWF0cml4RnJvbVBhY2tlZE91dHB1dFRleHR1cmUoXG4gICAgICAgICAgICB0aGlzLmdsLCByb3dzLCBjb2x1bW5zKSk7XG4gIH1cblxuICBwdWJsaWMgY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZTogc3RyaW5nKTogV2ViR0xQcm9ncmFtIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICBjb25zdCBmcmFnbWVudFNoYWRlcjogV2ViR0xTaGFkZXIgPVxuICAgICAgICB3ZWJnbF91dGlsLmNyZWF0ZUZyYWdtZW50U2hhZGVyKGdsLCBmcmFnbWVudFNoYWRlclNvdXJjZSk7XG4gICAgY29uc3QgdmVydGV4U2hhZGVyOiBXZWJHTFNoYWRlciA9IGdwZ3B1X3V0aWwuY3JlYXRlVmVydGV4U2hhZGVyKGdsKTtcbiAgICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPSB3ZWJnbF91dGlsLmNyZWF0ZVByb2dyYW0oZ2wpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hdHRhY2hTaGFkZXIocHJvZ3JhbSwgdmVydGV4U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmF0dGFjaFNoYWRlcihwcm9ncmFtLCBmcmFnbWVudFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwubGlua1Byb2dyYW0oZ2wsIHByb2dyYW0pO1xuICAgIGlmICh0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlUHJvZ3JhbShnbCwgcHJvZ3JhbSk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZXRhY2hTaGFkZXIocHJvZ3JhbSwgdmVydGV4U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZVNoYWRlcih2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGV0YWNoU2hhZGVyKHByb2dyYW0sIGZyYWdtZW50U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZVNoYWRlcihmcmFnbWVudFNoYWRlcikpO1xuICAgIHJldHVybiBwcm9ncmFtO1xuICB9XG5cbiAgcHVibGljIGRlbGV0ZVByb2dyYW0ocHJvZ3JhbTogV2ViR0xQcm9ncmFtKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBpZiAocHJvZ3JhbSA9PT0gdGhpcy5wcm9ncmFtKSB7XG4gICAgICB0aGlzLnByb2dyYW0gPSBudWxsO1xuICAgIH1cbiAgICBpZiAocHJvZ3JhbSAhPSBudWxsKSB7XG4gICAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSkpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBzZXRQcm9ncmFtKHByb2dyYW06IFdlYkdMUHJvZ3JhbXxudWxsKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnByb2dyYW0gPSBwcm9ncmFtO1xuICAgIGlmICgodGhpcy5wcm9ncmFtICE9IG51bGwpICYmIHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVQcm9ncmFtKHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wudXNlUHJvZ3JhbShwcm9ncmFtKSk7XG4gIH1cblxuICBwdWJsaWMgZ2V0VW5pZm9ybUxvY2F0aW9uKHVuaWZvcm1OYW1lOiBzdHJpbmcpOiBXZWJHTFVuaWZvcm1Mb2NhdGlvbiB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnRocm93SWZOb1Byb2dyYW0oKTtcbiAgICByZXR1cm4gd2ViZ2xfdXRpbC5nZXRQcm9ncmFtVW5pZm9ybUxvY2F0aW9uT3JUaHJvdyhcbiAgICAgICAgdGhpcy5nbCwgdGhpcy5wcm9ncmFtISwgdW5pZm9ybU5hbWUpO1xuICB9XG5cbiAgcHVibGljIHNldElucHV0TWF0cml4VGV4dHVyZShcbiAgICAgIGlucHV0TWF0cml4VGV4dHVyZTogV2ViR0xUZXh0dXJlLCB1bmlmb3JtTmFtZTogc3RyaW5nLFxuICAgICAgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy50aHJvd0lmTm9Qcm9ncmFtKCk7XG4gICAgd2ViZ2xfdXRpbC5iaW5kVGV4dHVyZVRvUHJvZ3JhbVVuaWZvcm1TYW1wbGVyKFxuICAgICAgICB0aGlzLmdsLCB0aGlzLnByb2dyYW0hLCBpbnB1dE1hdHJpeFRleHR1cmUsIHVuaWZvcm1OYW1lLCB0ZXh0dXJlVW5pdCk7XG4gIH1cblxuICBwdWJsaWMgc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIG91dHB1dE1hdHJpeFRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpIHtcbiAgICB0aGlzLnNldE91dHB1dE1hdHJpeFRleHR1cmVEcml2ZXIob3V0cHV0TWF0cml4VGV4dHVyZSwgY29sdW1ucywgcm93cyk7XG4gIH1cblxuICBwdWJsaWMgc2V0T3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZShcbiAgICAgIG91dHB1dFBhY2tlZE1hdHJpeFRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICAgIHRleF91dGlsLmdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICAgIHRoaXMuc2V0T3V0cHV0TWF0cml4VGV4dHVyZURyaXZlcihvdXRwdXRQYWNrZWRNYXRyaXhUZXh0dXJlLCB3aWR0aCwgaGVpZ2h0KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbihcbiAgICAgIHN0YXJ0Um93OiBudW1iZXIsIG51bVJvd3M6IG51bWJlciwgc3RhcnRDb2x1bW46IG51bWJlcixcbiAgICAgIG51bUNvbHVtbnM6IG51bWJlcikge1xuICAgIHRoaXMuc2V0T3V0cHV0TWF0cml4V3JpdGVSZWdpb25Ecml2ZXIoXG4gICAgICAgIHN0YXJ0Q29sdW1uLCBzdGFydFJvdywgbnVtQ29sdW1ucywgbnVtUm93cyk7XG4gIH1cblxuICBwdWJsaWMgc2V0T3V0cHV0UGFja2VkTWF0cml4V3JpdGVSZWdpb24oXG4gICAgICBzdGFydFJvdzogbnVtYmVyLCBudW1Sb3dzOiBudW1iZXIsIHN0YXJ0Q29sdW1uOiBudW1iZXIsXG4gICAgICBudW1Db2x1bW5zOiBudW1iZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3NldE91dHB1dFBhY2tlZE1hdHJpeFdyaXRlUmVnaW9uIG5vdCBpbXBsZW1lbnRlZC4nKTtcbiAgfVxuXG4gIHB1YmxpYyBkZWJ1Z1ZhbGlkYXRlKCkge1xuICAgIGlmICh0aGlzLnByb2dyYW0gIT0gbnVsbCkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZVByb2dyYW0odGhpcy5nbCwgdGhpcy5wcm9ncmFtKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC52YWxpZGF0ZUZyYW1lYnVmZmVyKHRoaXMuZ2wpO1xuICB9XG5cbiAgcHVibGljIGV4ZWN1dGVQcm9ncmFtKCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy50aHJvd0lmTm9Qcm9ncmFtKCk7XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIGdwZ3B1X3V0aWwuYmluZFZlcnRleFByb2dyYW1BdHRyaWJ1dGVTdHJlYW1zKFxuICAgICAgICBnbCwgdGhpcy5wcm9ncmFtISwgdGhpcy52ZXJ0ZXhCdWZmZXIpO1xuICAgIGlmICh0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB0aGlzLmRlYnVnVmFsaWRhdGUoKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICAgIGdsLCAoKSA9PiBnbC5kcmF3RWxlbWVudHMoZ2wuVFJJQU5HTEVTLCA2LCBnbC5VTlNJR05FRF9TSE9SVCwgMCkpO1xuICB9XG5cbiAgcHVibGljIGJsb2NrVW50aWxBbGxQcm9ncmFtc0NvbXBsZXRlZCgpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuZmluaXNoKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBkb3dubG9hZE1hdHJpeERyaXZlcihcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICAgIGRvd25sb2FkQW5kRGVjb2RlOiAoKSA9PiBGbG9hdDMyQXJyYXkpOiBGbG9hdDMyQXJyYXkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgd2ViZ2xfdXRpbC5iaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICAgICAgdGhpcy5nbCwgdGV4dHVyZSwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgY29uc3QgcmVzdWx0ID0gZG93bmxvYWRBbmREZWNvZGUoKTtcbiAgICBpZiAodGhpcy5vdXRwdXRUZXh0dXJlICE9IG51bGwpIHtcbiAgICAgIHdlYmdsX3V0aWwuYmluZENvbG9yVGV4dHVyZVRvRnJhbWVidWZmZXIoXG4gICAgICAgICAgdGhpcy5nbCwgdGhpcy5vdXRwdXRUZXh0dXJlLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICAgIGlmICh0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVGcmFtZWJ1ZmZlcih0aGlzLmdsKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgd2ViZ2xfdXRpbC51bmJpbmRDb2xvclRleHR1cmVGcm9tRnJhbWVidWZmZXIodGhpcy5nbCwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIHNldE91dHB1dE1hdHJpeFRleHR1cmVEcml2ZXIoXG4gICAgICBvdXRwdXRNYXRyaXhUZXh0dXJlTWF5YmVQYWNrZWQ6IFdlYkdMVGV4dHVyZSwgd2lkdGg6IG51bWJlcixcbiAgICAgIGhlaWdodDogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgd2ViZ2xfdXRpbC5iaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICAgICAgZ2wsIG91dHB1dE1hdHJpeFRleHR1cmVNYXliZVBhY2tlZCwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVGcmFtZWJ1ZmZlcihnbCk7XG4gICAgfVxuICAgIHRoaXMub3V0cHV0VGV4dHVyZSA9IG91dHB1dE1hdHJpeFRleHR1cmVNYXliZVBhY2tlZDtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmlld3BvcnQoMCwgMCwgd2lkdGgsIGhlaWdodCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5zY2lzc29yKDAsIDAsIHdpZHRoLCBoZWlnaHQpKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0T3V0cHV0TWF0cml4V3JpdGVSZWdpb25Ecml2ZXIoXG4gICAgICB4OiBudW1iZXIsIHk6IG51bWJlciwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgICB0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLnNjaXNzb3IoeCwgeSwgd2lkdGgsIGhlaWdodCkpO1xuICB9XG5cbiAgcHJpdmF0ZSB0aHJvd0lmRGlzcG9zZWQoKSB7XG4gICAgaWYgKHRoaXMuZGlzcG9zZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQXR0ZW1wdGVkIHRvIHVzZSBkaXNwb3NlZCBHUEdQVUNvbnRleHQuJyk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB0aHJvd0lmTm9Qcm9ncmFtKCkge1xuICAgIGlmICh0aGlzLnByb2dyYW0gPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdObyBHUFUgcHJvZ3JhbSBpcyBjdXJyZW50bHkgc2V0LicpO1xuICAgIH1cbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB0ZXhfdXRpbCBmcm9tICcuL3RleF91dGlsJztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMoKTogV2ViR0xDb250ZXh0QXR0cmlidXRlcyB7XG4gIHJldHVybiB7XG4gICAgYWxwaGE6IGZhbHNlLFxuICAgIGFudGlhbGlhczogZmFsc2UsXG4gICAgcHJlbXVsdGlwbGllZEFscGhhOiBmYWxzZSxcbiAgICBwcmVzZXJ2ZURyYXdpbmdCdWZmZXI6IGZhbHNlLFxuICAgIGRlcHRoOiBmYWxzZSxcbiAgICBzdGVuY2lsOiBmYWxzZSxcbiAgICBmYWlsSWZNYWpvclBlcmZvcm1hbmNlQ2F2ZWF0OiB0cnVlXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVXZWJHTENvbnRleHQoY2FudmFzPzogSFRNTENhbnZhc0VsZW1lbnQpIHtcbiAgY29uc3QgYXR0cmlidXRlcyA9IGdldFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMoKTtcbiAgbGV0IGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIGlmIChjYW52YXMgIT0gbnVsbCkge1xuICAgIGdsID0gd2ViZ2xfdXRpbC5jcmVhdGVXZWJHTFJlbmRlcmluZ0NvbnRleHRGcm9tQ2FudmFzKGNhbnZhcywgYXR0cmlidXRlcyk7XG4gIH0gZWxzZSB7XG4gICAgZ2wgPSB3ZWJnbF91dGlsLmNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dChhdHRyaWJ1dGVzKTtcbiAgfVxuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5ERVBUSF9URVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLlNURU5DSUxfVEVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5CTEVORCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5ESVRIRVIpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuUE9MWUdPTl9PRkZTRVRfRklMTCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5TQU1QTEVfQ09WRVJBR0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmVuYWJsZShnbC5TQ0lTU09SX1RFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmVuYWJsZShnbC5DVUxMX0ZBQ0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmN1bGxGYWNlKGdsLkJBQ0spKTtcbiAgcmV0dXJuIGdsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVmVydGV4U2hhZGVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IHZlcnRleFNoYWRlclNvdXJjZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgYXR0cmlidXRlIHZlYzMgY2xpcFNwYWNlUG9zO1xuICAgIGF0dHJpYnV0ZSB2ZWMyIHV2O1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGdsX1Bvc2l0aW9uID0gdmVjNChjbGlwU3BhY2VQb3MsIDEpO1xuICAgICAgcmVzdWx0VVYgPSB1djtcbiAgICB9YDtcbiAgcmV0dXJuIHdlYmdsX3V0aWwuY3JlYXRlVmVydGV4U2hhZGVyKGdsLCB2ZXJ0ZXhTaGFkZXJTb3VyY2UpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVmVydGV4QnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEJ1ZmZlciB7XG4gIC8vIFt4IHkgeiB1IHZdICogW3VwcGVyLWxlZnQsIGxvd2VyLWxlZnQsIHVwcGVyLXJpZ2h0LCBsb3dlci1yaWdodF1cbiAgY29uc3QgdmVydGV4QXJyYXkgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgWy0xLCAxLCAwLCAwLCAxLCAtMSwgLTEsIDAsIDAsIDAsIDEsIDEsIDAsIDEsIDEsIDEsIC0xLCAwLCAxLCAwXSk7XG4gIHJldHVybiB3ZWJnbF91dGlsLmNyZWF0ZVN0YXRpY1ZlcnRleEJ1ZmZlcihnbCwgdmVydGV4QXJyYXkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlSW5kZXhCdWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMQnVmZmVyIHtcbiAgLy8gT3BlbkdMIChhbmQgV2ViR0wpIGhhdmUgXCJDQ1cgPT0gZnJvbnRcIiB3aW5kaW5nXG4gIGNvbnN0IHRyaWFuZ2xlVmVydGV4SW5kaWNlcyA9IG5ldyBVaW50MTZBcnJheShbMCwgMSwgMiwgMiwgMSwgM10pO1xuICByZXR1cm4gd2ViZ2xfdXRpbC5jcmVhdGVTdGF0aWNJbmRleEJ1ZmZlcihnbCwgdHJpYW5nbGVWZXJ0ZXhJbmRpY2VzKTtcbn1cblxuZnVuY3Rpb24gZ2V0VGV4dHVyZUludGVybmFsRm9ybWF0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIG51bUNoYW5uZWxzOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAod2ViZ2xfdXRpbC5pc1dlYkdMMkVuYWJsZWQoKSkge1xuICAgIGlmIChudW1DaGFubmVscyA9PT0gNCkge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgcmV0dXJuIChnbCBhcyBhbnkpLlJHQkEzMkY7XG4gICAgfVxuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKGdsIGFzIGFueSkuUjMyRjtcbiAgfVxuICByZXR1cm4gZ2wuUkdCQTtcbn1cblxuZnVuY3Rpb24gZ2V0VGV4dHVyZUZvcm1hdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBudW1DaGFubmVsczogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKHdlYmdsX3V0aWwuaXNXZWJHTDJFbmFibGVkKCkgJiYgbnVtQ2hhbm5lbHMgPT09IDEpIHtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgcmV0dXJuIChnbCBhcyBhbnkpLlJFRDtcbiAgfVxuICByZXR1cm4gZ2wuUkdCQTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcixcbiAgICBudW1DaGFubmVsczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgd2ViZ2xfdXRpbC52YWxpZGF0ZVRleHR1cmVTaXplKGdsLCB3aWR0aCwgaGVpZ2h0KTtcbiAgY29uc3QgdGV4dHVyZSA9IHdlYmdsX3V0aWwuY3JlYXRlVGV4dHVyZShnbCk7XG5cbiAgY29uc3QgdGV4MmQgPSBnbC5URVhUVVJFXzJEO1xuICBjb25zdCBpbnRlcm5hbEZvcm1hdCA9IGdldFRleHR1cmVJbnRlcm5hbEZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuICBjb25zdCBmb3JtYXQgPSBnZXRUZXh0dXJlRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZSh0ZXgyZCwgdGV4dHVyZSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC50ZXhQYXJhbWV0ZXJpKHRleDJkLCBnbC5URVhUVVJFX1dSQVBfUywgZ2wuQ0xBTVBfVE9fRURHRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC50ZXhQYXJhbWV0ZXJpKHRleDJkLCBnbC5URVhUVVJFX1dSQVBfVCwgZ2wuQ0xBTVBfVE9fRURHRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC50ZXhQYXJhbWV0ZXJpKHRleDJkLCBnbC5URVhUVVJFX01JTl9GSUxURVIsIGdsLk5FQVJFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9NQUdfRklMVEVSLCBnbC5ORUFSRVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC50ZXhJbWFnZTJEKFxuICAgICAgICAgIHRleDJkLCAwLCBpbnRlcm5hbEZvcm1hdCwgd2lkdGgsIGhlaWdodCwgMCwgZm9ybWF0LCBnbC5GTE9BVCwgbnVsbCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgbnVsbCkpO1xuICByZXR1cm4gdGV4dHVyZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1hdHJpeFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSAxO1xuICByZXR1cm4gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShnbCwgd2lkdGgsIGhlaWdodCwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQ29sb3JNYXRyaXhUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgIHRleF91dGlsLmdldENvbG9yTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgcmV0dXJuIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoZ2wsIHdpZHRoLCBoZWlnaHQsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVBhY2tlZE1hdHJpeFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgcmV0dXJuIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoZ2wsIHdpZHRoLCBoZWlnaHQsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRWZXJ0ZXhQcm9ncmFtQXR0cmlidXRlU3RyZWFtcyhcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sXG4gICAgdmVydGV4QnVmZmVyOiBXZWJHTEJ1ZmZlcikge1xuICBjb25zdCBwb3NPZmZzZXQgPSAwOyAgICAgICAgICAgICAgIC8vIHggaXMgdGhlIGZpcnN0IGJ1ZmZlciBlbGVtZW50XG4gIGNvbnN0IHV2T2Zmc2V0ID0gMyAqIDQ7ICAgICAgICAgICAgLy8gdXYgY29tZXMgYWZ0ZXIgW3ggeSB6XVxuICBjb25zdCBzdHJpZGUgPSAoMyAqIDQpICsgKDIgKiA0KTsgIC8vIHh5eiArIHV2LCBlYWNoIGVudHJ5IGlzIDQtYnl0ZSBmbG9hdC5cbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5BUlJBWV9CVUZGRVIsIHZlcnRleEJ1ZmZlcikpO1xuICB3ZWJnbF91dGlsLmJpbmRWZXJ0ZXhCdWZmZXJUb1Byb2dyYW1BdHRyaWJ1dGUoXG4gICAgICBnbCwgcHJvZ3JhbSwgJ2NsaXBTcGFjZVBvcycsIHZlcnRleEJ1ZmZlciwgMywgc3RyaWRlLCBwb3NPZmZzZXQpO1xuICB0cnkge1xuICAgIHdlYmdsX3V0aWwuYmluZFZlcnRleEJ1ZmZlclRvUHJvZ3JhbUF0dHJpYnV0ZShcbiAgICAgICAgZ2wsIHByb2dyYW0sICd1dicsIHZlcnRleEJ1ZmZlciwgMiwgc3RyaWRlLCB1dk9mZnNldCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICAvLyBQcm9ncmFtcyB3aXRoIDF4MSBvdXRwdXQgdGV4dHVyZXMgZG9uJ3QgdXNlIHRoZSB1diBhdHRyaWJ1dGUuXG4gICAgLy8gVGhpcyBjYW4gY2F1c2UgdGhlIHNoYWRlciBsaW5rZXIgdG8gZGVhZC1zdHJpcCBpdCwgc28gd2Ugc2hvdWxkbid0XG4gICAgLy8gY29tcGxhaW4gb3IgZmFpbCBpZiBpdCdzIG5vdCBwcmVzZW50LlxuICAgIGlmICghZS5oYXNPd25Qcm9wZXJ0eSgnbmFtZWRWZXJ0ZXhBdHRyaWJ1dGVOb3RGb3VuZCcpKSB7XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkUGl4ZWxEYXRhVG9UZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICBwaXhlbHM6IEltYWdlRGF0YXxIVE1MSW1hZ2VFbGVtZW50fEhUTUxDYW52YXNFbGVtZW50fEhUTUxWaWRlb0VsZW1lbnQpIHtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICBjb25zdCBpbnRlcm5hbEZvcm1hdCA9IGdldFRleHR1cmVJbnRlcm5hbEZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgdGV4dHVyZSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudGV4SW1hZ2UyRChcbiAgICAgICAgICBnbC5URVhUVVJFXzJELCAwLCBpbnRlcm5hbEZvcm1hdCwgZ2wuUkdCQSwgZ2wuRkxPQVQsIHBpeGVscykpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgbnVsbCkpO1xufVxuXG5mdW5jdGlvbiB1cGxvYWREYXRhVG9UZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgd2lkdGg6IG51bWJlcixcbiAgICBoZWlnaHQ6IG51bWJlciwgZGF0YTogRmxvYXQzMkFycmF5LCBudW1DaGFubmVsczogbnVtYmVyKSB7XG4gIGNvbnN0IHRleHR1cmVGb3JtYXQgPSBnZXRUZXh0dXJlRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG5cbiAgd2ViZ2xfdXRpbC52YWxpZGF0ZVRleHR1cmVTaXplKGdsLCB3aWR0aCwgaGVpZ2h0KTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnRleFN1YkltYWdlMkQoXG4gICAgICAgICAgZ2wuVEVYVFVSRV8yRCwgMCwgMCwgMCwgd2lkdGgsIGhlaWdodCwgdGV4dHVyZUZvcm1hdCwgZ2wuRkxPQVQsXG4gICAgICAgICAgZGF0YSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgbnVsbCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTWF0cml4VG9UZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLFxuICAgIGNvbHVtbnM6IG51bWJlciwgbWF0cml4OiBGbG9hdDMyQXJyYXksIG51bUNoYW5uZWxzOiBudW1iZXIpIHtcbiAgY29uc3QgW3csIGhdID1cbiAgICAgIHRleF91dGlsLmdldFVucGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG5cbiAgY29uc3QgY2hhbm5lbHNQZXJUZXh0dXJlID1cbiAgICAgIG51bUNoYW5uZWxzID09PSAxID8gd2ViZ2xfdXRpbC5nZXRDaGFubmVsc1BlclRleHR1cmUoKSA6IG51bUNoYW5uZWxzO1xuICBjb25zdCB1bnBhY2tlZEFycmF5ID1cbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkodGV4X3V0aWwuZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICAgICAgICBtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpKTtcbiAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9VbnBhY2tlZEFycmF5KFxuICAgICAgbWF0cml4LCB1bnBhY2tlZEFycmF5LCBjaGFubmVsc1BlclRleHR1cmUpO1xuXG4gIHVwbG9hZERhdGFUb1RleHR1cmUoZ2wsIHRleHR1cmUsIHcsIGgsIHVucGFja2VkQXJyYXksIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlcixcbiAgICBjb2x1bW5zOiBudW1iZXIsIG1hdHJpeDogRmxvYXQzMkFycmF5KSB7XG4gIGNvbnN0IFt3LCBoXSA9IHRleF91dGlsLmdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBwYWNrZWRSR0JBID0gbmV3IEZsb2F0MzJBcnJheShcbiAgICAgIHRleF91dGlsLmdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUocm93cywgY29sdW1ucykpO1xuICB0ZXhfdXRpbC5lbmNvZGVNYXRyaXhUb1BhY2tlZFJHQkEobWF0cml4LCByb3dzLCBjb2x1bW5zLCBwYWNrZWRSR0JBKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICB1cGxvYWREYXRhVG9UZXh0dXJlKGdsLCB0ZXh0dXJlLCB3LCBoLCBwYWNrZWRSR0JBLCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkb3dubG9hZE1hdHJpeEZyb21PdXRwdXRUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgW3csIGhdID1cbiAgICAgIHRleF91dGlsLmdldFVucGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG5cbiAgY29uc3QgY2hhbm5lbHNQZXJUZXh0dXJlID0gNDtcbiAgY29uc3QgdW5wYWNrZWRBcnJheSA9XG4gICAgICBuZXcgRmxvYXQzMkFycmF5KHRleF91dGlsLmdldFVucGFja2VkQXJyYXlTaXplRnJvbU1hdHJpeFNpemUoXG4gICAgICAgICAgcm93cyAqIGNvbHVtbnMsIGNoYW5uZWxzUGVyVGV4dHVyZSkpO1xuICBjb25zdCB0ZXh0dXJlRm9ybWF0ID0gZ2V0VGV4dHVyZUZvcm1hdChnbCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcblxuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC5yZWFkUGl4ZWxzKDAsIDAsIHcsIGgsIGdsLlJHQkEsIGdsLkZMT0FULCB1bnBhY2tlZEFycmF5KSk7XG5cbiAgY29uc3QgbWF0cml4ID0gbmV3IEZsb2F0MzJBcnJheShyb3dzICogY29sdW1ucyk7XG4gIHRleF91dGlsLmRlY29kZU1hdHJpeEZyb21VbnBhY2tlZEFycmF5KFxuICAgICAgdW5wYWNrZWRBcnJheSwgbWF0cml4LCBjaGFubmVsc1BlclRleHR1cmUpO1xuICByZXR1cm4gbWF0cml4O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZG93bmxvYWRNYXRyaXhGcm9tUGFja2VkT3V0cHV0VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IFt3LCBoXSA9IHRleF91dGlsLmdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBwYWNrZWRSR0JBID0gbmV3IEZsb2F0MzJBcnJheShcbiAgICAgIHRleF91dGlsLmdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUocm93cywgY29sdW1ucykpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC5yZWFkUGl4ZWxzKDAsIDAsIHcsIGgsIGdsLlJHQkEsIGdsLkZMT0FULCBwYWNrZWRSR0JBKSk7XG4gIGNvbnN0IG1hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkocm93cyAqIGNvbHVtbnMpO1xuICByZXR1cm4gdGV4X3V0aWwuZGVjb2RlTWF0cml4RnJvbVBhY2tlZFJHQkEocGFja2VkUkdCQSwgcm93cywgY29sdW1ucywgbWF0cml4KTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiBhRGltQ1IgPSB2ZWMyKCR7Y29sdW1uc30uMCwgJHtyb3dzfS4wKTtcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgYU1heCA9IHRleHR1cmUyRChtYXRyaXhBLCBoYWxmQ1IgLyBhRGltQ1IpLnI7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGMsIHIpICsgaGFsZkNSKSAvIGFEaW1DUjtcbiAgICAgICAgICBmbG9hdCBhQ3VyID0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICAgIGFNYXggPSBtYXgoYU1heCwgYUN1cik7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZmxvYXQgZXhwU3VtID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgYURpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGFEaW1DUi54OyBjICs9IDEuMCkge1xuICAgICAgICAgIHZlYzIgdXYgPSAodmVjMihjLCByKSArIGhhbGZDUikgLyBhRGltQ1I7XG4gICAgICAgICAgZmxvYXQgYUN1ciA9IHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICAgICAgICBleHBTdW0gKz0gZXhwKGFDdXIgLSBhTWF4KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGFNYXggKyBsb2coZXhwU3VtKSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dTdW1FeHAoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbG9nU3VtRXhwUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCAxLCAxKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShsb2dTdW1FeHBQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRMb2dTdW1FeHBEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMpKTtcbiAgY29uc3QgYVRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCByZXN1bHRUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSgxLCAxKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgbG9nU3VtRXhwKGdwZ3B1LCBwcm9ncmFtLCBhVGV4dHVyZSwgcm93cywgY29sdW1ucywgcmVzdWx0VGV4dHVyZSk7XG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUocmVzdWx0VGV4dHVyZSwgMSwgMSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHJlc3VsdFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG4gIHJldHVybiByZXN1bHRbMF07XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xCYWNrcHJvcChcbiAgICBkeVNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICBvcmlnUGFkOiBudW1iZXIpIHtcbiAgY29uc3Qgb3JpZ0lucHV0RGVwdGggPSBkeVNoYXBlUkNEWzJdO1xuICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICBjb25zdCBbZHlSb3dzLCBkeUNvbHMsIGRlcHRoXSA9IGR5U2hhcGVSQ0Q7XG5cbiAgY29uc3QgZHlUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChkeVNoYXBlUkNEKTtcblxuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBkeTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXhQb3M7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgZHlTaGFwZUNSID0gdmVjMigke2R5VGV4U2hhcGVSQ1sxXX0sICR7ZHlUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIGR4VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoZHhUZXhSLCBkeFRleEMpIHRvIDNEIChkeFIsIGR4QywgZCkuXG4gICAgICBmbG9hdCBkeFIgPSBkeFRleENSLnk7XG4gICAgICBmbG9hdCBkeEMgPSBmbG9vcihkeFRleENSLnggLyAke29yaWdJbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQgPSBtb2QoZHhUZXhDUi54LCAke29yaWdJbnB1dERlcHRofS4wKTtcblxuICAgICAgdmVjMiBkeVJDQ29ybmVyID0gdmVjMihkeFIsIGR4QykgLSB2ZWMyKCR7cGFkfS4wLCAke3BhZH0uMCk7XG4gICAgICBmbG9hdCBkeVJDb3JuZXIgPSBkeVJDQ29ybmVyLng7XG4gICAgICBmbG9hdCBkeUNDb3JuZXIgPSBkeVJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIGR5KD8sID8sIGQpIHdpdGggcG9zIG1hc2soOiwgOiwgZCkgdG8gZ2V0IGR4KHlSLCBkeEMsIGQpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcblxuICAgICAgICBmbG9hdCBkeVIgPSAoZHlSQ29ybmVyICsgd1IpIC8gJHtvcmlnU3RyaWRlfS4wO1xuICAgICAgICAvLyBUT0RPKG5zdGhvcmF0KTogU3BsaWNlIHRoaXMgd2l0aCBhbm90aGVyIHZlcnNpb24gd2hlcmUgeW91IGNhbGxcbiAgICAgICAgLy8gZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoKS4gSGVyZSBhbmQgYmVsb3cuXG4gICAgICAgIGlmIChkeVIgPCAwLjAgfHwgZHlSID49ICR7ZHlSb3dzfS4wIHx8IGZyYWN0KGR5UikgPiAwLjApIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZsb2F0IGR5VGV4UiA9IGR5UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHdDID0gMC4wOyB3QyA8ICR7ZlNpemV9LjA7IHdDICs9IDEuMCkge1xuXG4gICAgICAgICAgZmxvYXQgZHlDID0gKGR5Q0Nvcm5lciArIHdDKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgICBpZiAoZHlDIDwgMC4wIHx8IGR5QyA+PSAke2R5Q29sc30uMCB8fCBmcmFjdChkeUMpID4gMC4wKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBmbG9hdCBkeVRleEMgPSBkeUMgKiAke2RlcHRofS4wICsgZDtcblxuICAgICAgICAgIC8vIFJlYWQgZHkoZHlSLCBkeUMsIGQpLlxuICAgICAgICAgIHZlYzIgZHlVViA9ICh2ZWMyKGR5VGV4QywgZHlUZXhSKSArIGhhbGZDUikgLyBkeVNoYXBlQ1I7XG4gICAgICAgICAgZmxvYXQgZHlWYWx1ZSA9IHRleHR1cmUyRChkeSwgZHlVVikucjtcblxuICAgICAgICAgIC8vIFJlYWQgbWF4UG9zKGR5UiwgZHlDLCBkKS5cbiAgICAgICAgICBmbG9hdCBtYXhQb3NWYWx1ZSA9XG4gICAgICAgICAgICAgICR7ZlNpemUgKiBmU2l6ZSAtIDF9LjAgLSB0ZXh0dXJlMkQobWF4UG9zLCBkeVVWKS5yO1xuXG4gICAgICAgICAgLy8gR2V0IHRoZSBjdXJyZW50IHZhbHVlLCBjaGVjayBpdCBhZ2FpbnN0IHRoZSB2YWx1ZSBmcm9tIHRoZVxuICAgICAgICAgIC8vIHBvc2l0aW9uIG1hdHJpeC5cbiAgICAgICAgICBmbG9hdCBjdXJQb3NWYWx1ZSA9IHdSICogJHtmU2l6ZX0uMCArIHdDO1xuICAgICAgICAgIGZsb2F0IG1hc2sgPSBmbG9hdChtYXhQb3NWYWx1ZSA9PSBjdXJQb3NWYWx1ZSA/IDEuMCA6IDAuMCk7XG5cbiAgICAgICAgICBkb3RQcm9kICs9IGR5VmFsdWUgKiBtYXNrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWF4UG9vbEJhY2twcm9wKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgZHlUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICBtYXhQb3NpdGlvbnNUZXg6IFdlYkdMVGV4dHVyZSwgcmVzdWx0VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGR5VGV4LCAnZHknLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKG1heFBvc2l0aW9uc1RleCwgJ21heFBvcycsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vcG9vbF9ncHUnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sUG9zaXRpb25zU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsIHRydWUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsIGZhbHNlKTtcbn1cblxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQ29tbW9uU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlciwgY29tcHV0ZU1heFBvc2l0aW9uczogYm9vbGVhbikge1xuICByZXR1cm4gcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdtYXgnLCBjb21wdXRlTWF4UG9zaXRpb25zKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1heFBvb2xDb21tb24oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIHBvb2xfZ3B1LnBvb2xDb21tb24oZ3BncHUsIHByb2dyYW0sIHgsIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2wpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vbWF0aCc7XG5pbXBvcnQge0FycmF5MkR9IGZyb20gJy4uL25kYXJyYXknO1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHNoYWRlcl9jb21waWxlciBmcm9tICcuL3NoYWRlcl9jb21waWxlcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlcihcbiAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBvdXQ6IEFycmF5MkQsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sXG4gICAgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF0pO1xuICBjb25zdCBhU25pcHBldCA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/ICdhUm93LCBpJyA6ICdpLCBhUm93JztcbiAgY29uc3QgYlNuaXBwZXQgPVxuICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyAnaSwgYkNvbCcgOiAnYkNvbCwgaSc7XG5cbiAgY29uc3QgaW5wdXRzID0gW3tuYW1lOiAnbWF0cml4QScsIGFycmF5OiBhfSwge25hbWU6ICdtYXRyaXhCJywgYXJyYXk6IGJ9XTtcbiAgY29uc3QgdXNlckNvZGUgPSBgXG4gICAgY29uc3QgZmxvYXQgc2hhcmVkRGltID0gJHtzaGFyZWREaW19LjA7XG5cbiAgICBmbG9hdCBkb3RBUm93QkNvbChmbG9hdCBhUm93LCBmbG9hdCBiQ29sKSB7XG4gICAgICBmbG9hdCByZXN1bHQgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IGkgPSAwLjA7IGkgPCBzaGFyZWREaW07IGkgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IGEgPSBnZXRNYXRyaXhBKCR7YVNuaXBwZXR9KTtcbiAgICAgICAgZmxvYXQgYiA9IGdldE1hdHJpeEIoJHtiU25pcHBldH0pO1xuICAgICAgICByZXN1bHQgKz0gKGEgKiBiKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiByZXNSQyA9IGdldE91dHB1dENvb3JkcygpO1xuICAgICAgc2V0T3V0cHV0KGRvdEFSb3dCQ29sKHJlc1JDLngsIHJlc1JDLnkpKTtcbiAgICB9XG4gIGA7XG4gIHJldHVybiBzaGFkZXJfY29tcGlsZXIubWFrZVNoYWRlcihpbnB1dHMsIG91dCwgdXNlckNvZGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbXVsdGlwbHlNYXRyaXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbXVsdGlwbHlQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBiOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgb3V0VGV4U2hhcGVbMF0sIG91dFRleFNoYXBlWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtdWx0aXBseVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb259IGZyb20gJy4uL21hdGgnO1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHNoYXJlZERpbWVuc2lvbjogbnVtYmVyLCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLFxuICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBzdHJpbmcge1xuICAvKlxuICAgICAgQSA9IFswIDEgICBCID0gWzAgMSAgb3V0ID0gW0EwKkIwK0ExKkIyIEEwKkIxK0ExKkIzXG4gICAgICAgICAgIDIgM10gICAgICAgMiAzXSAgICAgICAgQTIqQjArQTEqQjIgQTIqQjErQXcqQjNdXG4gICAgICBvdXQuMCA9IEEwICogQjAgKyBBMSAqIEIyXG4gICAgICBvdXQuMSA9IEEwICogQjEgKyBBMSAqIEIzXG4gICAgICBvdXQuMiA9IEEyICogQjAgKyBBMyAqIEIyXG4gICAgICBvdXQuMyA9IEEyICogQjEgKyBBMyAqIEIzXG5cbiAgICAgIEEqQiAgICAgPSBBLnh4enogKiBCLnh5eHkgKyBBLnl5d3cgKiBCLnp3endcbiAgICAgIEFedCpCICAgPSBBLnh4eXkgKiBCLnh5eHkgKyBBLnp6d3cgKiBCLnp3endcbiAgICAgIEEqQl50ICAgPSBBLnh4enogKiBCLnh6eHogKyBBLnl5d3cgKiBCLnl3eXdcbiAgICAgIEFedCpCXnQgPSBBLnh4eXkgKiBCLnh6eHogKyBBLnp6d3cgKiBCLnl3eXdcbiAgICovXG4gIGNvbnN0IHNoYXJlZERpbWVuc2lvblBhY2tlZCA9IE1hdGguY2VpbChzaGFyZWREaW1lbnNpb24gLyAyKTtcbiAgY29uc3QgYVNhbXBsZSA9IChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgICdjZW50ZXIsIHJlc3VsdFVWLnQnIDpcbiAgICAgICdyZXN1bHRVVi50LCBjZW50ZXInO1xuICBjb25zdCBiU2FtcGxlID0gKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgP1xuICAgICAgJ3Jlc3VsdFVWLnMsIGNlbnRlcicgOlxuICAgICAgJ2NlbnRlciwgcmVzdWx0VVYucyc7XG4gIGNvbnN0IGFTd2l6emxlOiBbc3RyaW5nLCBzdHJpbmddID1cbiAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gWydhLnh4enonLCAnYS55eXd3J10gOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbJ2EueHh5eScsICdhLnp6d3cnXTtcbiAgY29uc3QgYlN3aXp6bGU6IFtzdHJpbmcsIHN0cmluZ10gPVxuICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBbJ2IueHl4eScsICdiLnp3encnXSA6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFsnYi54enh6JywgJ2IueXd5dyddO1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEI7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgZmxvYXQgc2hhcmVkRGltZW5zaW9uID0gJHtzaGFyZWREaW1lbnNpb25QYWNrZWR9LjA7XG5cbiAgICB2ZWM0IGRvdDJ4MkFSb3dCQ29sKCkge1xuICAgICAgdmVjNCByZXN1bHQgPSB2ZWM0KDAsIDAsIDAsIDApO1xuICAgICAgZm9yIChmbG9hdCBpID0gMC4wOyBpIDwgc2hhcmVkRGltZW5zaW9uOyBpICs9IDEuMCkge1xuICAgICAgICBmbG9hdCBjZW50ZXIgPSAoaSArIDAuNSkgLyBzaGFyZWREaW1lbnNpb247XG4gICAgICAgIHZlYzQgYSA9IHRleHR1cmUyRChtYXRyaXhBLCB2ZWMyKCR7YVNhbXBsZX0pKTtcbiAgICAgICAgdmVjNCBiID0gdGV4dHVyZTJEKG1hdHJpeEIsIHZlYzIoJHtiU2FtcGxlfSkpO1xuICAgICAgICByZXN1bHQgKz1cbiAgICAgICAgICAoJHthU3dpenpsZVswXX0gKiAke2JTd2l6emxlWzBdfSkgKyAoJHthU3dpenpsZVsxXX0gKiAke2JTd2l6emxlWzFdfSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IGRvdDJ4MkFSb3dCQ29sKCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtdWx0aXBseU1hdHJpeFBhY2tlZChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBtdWx0aXBseVByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGI6IFdlYkdMVGV4dHVyZSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtdWx0aXBseVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNdWx0aXBseU1hdHJpeFBhY2tlZERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBGbG9hdDMyQXJyYXksXG4gICAgYlNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCByZXN1bHROdW1Sb3dzID0gKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgP1xuICAgICAgYVNoYXBlUm93Q29sWzBdIDpcbiAgICAgIGFTaGFwZVJvd0NvbFsxXTtcbiAgY29uc3QgcmVzdWx0TnVtQ29scyA9IChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgIGJTaGFwZVJvd0NvbFsxXSA6XG4gICAgICBiU2hhcGVSb3dDb2xbMF07XG4gIGNvbnN0IHNoYXJlZERpbWVuc2lvbiA9IChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgIGFTaGFwZVJvd0NvbFsxXSA6XG4gICAgICBhU2hhcGVSb3dDb2xbMF07XG5cbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oXG4gICAgICBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShzaGFyZWREaW1lbnNpb24sIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG5cbiAgY29uc3QgYVRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKGFTaGFwZVJvd0NvbFswXSwgYVNoYXBlUm93Q29sWzFdKTtcbiAgY29uc3QgYlRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKGJTaGFwZVJvd0NvbFswXSwgYlNoYXBlUm93Q29sWzFdKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZVBhY2tlZE1hdHJpeFRleHR1cmUocmVzdWx0TnVtUm93cywgcmVzdWx0TnVtQ29scyk7XG5cbiAgZ3BncHUudXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgICAgYVRleHR1cmUsIGFTaGFwZVJvd0NvbFswXSwgYVNoYXBlUm93Q29sWzFdLCBhKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgICAgYlRleHR1cmUsIGJTaGFwZVJvd0NvbFswXSwgYlNoYXBlUm93Q29sWzFdLCBiKTtcblxuICBtdWx0aXBseU1hdHJpeFBhY2tlZChcbiAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhVGV4dHVyZSwgYlRleHR1cmUsIHJlc3VsdFRleHR1cmUsXG4gICAgICBbcmVzdWx0TnVtUm93cywgcmVzdWx0TnVtQ29sc10pO1xuXG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVBhY2tlZFRleHR1cmUoXG4gICAgICByZXN1bHRUZXh0dXJlLCByZXN1bHROdW1Sb3dzLCByZXN1bHROdW1Db2xzKTtcblxuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcblxuICByZXR1cm4gcmVzdWx0O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclBvb2xDb21tb25Tb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyLCBwb29sVHlwZTogJ21heCd8J21pbid8J2F2ZycsIGNvbXB1dGVQb3NpdGlvbnM6IGJvb2xlYW4pIHtcbiAgaWYgKHBvb2xUeXBlID09PSAnYXZnJyAmJiBjb21wdXRlUG9zaXRpb25zKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgY29tcHV0ZSBwb3NpdGlvbnMgZm9yIGF2ZXJhZ2UgcG9vbC4nKTtcbiAgfVxuXG4gIGNvbnN0IGRlcHRoID0geFNoYXBlUkNEWzJdO1xuXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSQ0QpO1xuXG4gIGxldCByZXR1cm5WYWx1ZSA9ICdtaW5NYXhWYWx1ZSc7XG4gIGlmIChjb21wdXRlUG9zaXRpb25zKSB7XG4gICAgcmV0dXJuVmFsdWUgPSAnbWluTWF4UG9zaXRpb24nO1xuICB9IGVsc2UgaWYgKHBvb2xUeXBlID09PSAnYXZnJykge1xuICAgIHJldHVyblZhbHVlID0gJ2F2Z1ZhbHVlJztcbiAgfVxuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIHhTaGFwZUNSID0gdmVjMigke3hUZXhTaGFwZVJDWzFdfSwgJHt4VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgJHtJU19OQU5fU0hBREVSX0ZVTkN9XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQyKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtkZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkID0gbW9kKHlUZXhDUi54LCAke2RlcHRofS4wKTtcblxuICAgICAgdmVjMiB4UkNDb3JuZXIgPSB2ZWMyKHlSLCB5QykgKiB2ZWMyKCR7c3RyaWRlfSwgJHtzdHJpZGV9KSAtXG4gICAgICAgICAgdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIG1heC9taW4geCg/LCA/LCBkKSB0byBnZXQgeSh5UiwgeUMsIGQpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWRcbiAgICAgIGZsb2F0IG1pbk1heFZhbHVlID0gMC4wO1xuICAgICAgZmxvYXQgbWluTWF4VmFsdWVGb3VuZCA9IDAuMDtcbiAgICAgIGZsb2F0IG1pbk1heFBvc2l0aW9uID0gMC4wO1xuICAgICAgZmxvYXQgYXZnVmFsdWUgPSAwLjA7XG5cbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IHhSID0geFJDb3JuZXIgKyB3UjtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHdDID0gMC4wOyB3QyA8ICR7ZlNpemV9LjA7IHdDICs9IDEuMCkge1xuICAgICAgICAgIGZsb2F0IHhDID0geENDb3JuZXIgKyB3QztcbiAgICAgICAgICBmbG9hdCB4VGV4QyA9IHhDICogJHtkZXB0aH0uMCArIGQ7XG5cbiAgICAgICAgICB2ZWMyIHRleENSID0gdmVjMih4VGV4QywgeFRleFIpO1xuXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHJlcXVlc3RlZCBVViBpcyBpbnZhbGlkLlxuICAgICAgICAgIHZlYzIgdXYgPSAodGV4Q1IgKyBoYWxmQ1IpIC8geFNoYXBlQ1I7XG4gICAgICAgICAgYm9vbCBsZXNzVGhhblplcm8gPSBhbnkobGVzc1RoYW4odXYsIHZlYzIoMCwgMCkpKTtcbiAgICAgICAgICBib29sIGdyZWF0ZXJUaGFuT25lID0gYW55KGdyZWF0ZXJUaGFuKHV2LCB2ZWMyKDEsIDEpKSk7XG4gICAgICAgICAgYm9vbCBvdXRzaWRlID0gbGVzc1RoYW5aZXJvIHx8IGdyZWF0ZXJUaGFuT25lO1xuICAgICAgICAgIGlmIChvdXRzaWRlKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRCh4LCB1dikucjtcbiAgICAgICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLCAwLCAwKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCR7cG9vbFR5cGUgPT09ICdhdmcnfSkge1xuICAgICAgICAgICAgYXZnVmFsdWUgKz0gdmFsdWUgLyAke2ZTaXplICogZlNpemV9LjA7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIElmIGEgbWluIC8gbWF4IHZhbHVlIGhhcyBhbHJlYWR5IGJlZW4gZm91bmQsIHVzZSBpdC4gSWYgbm90LCB1c2VcbiAgICAgICAgICAgIC8vIHRoZSBjdXJyZW50IHZhbHVlLlxuICAgICAgICAgICAgZmxvYXQgY3VycmVudE1pbk1heFZhbHVlID0gbWl4KFxuICAgICAgICAgICAgICAgIHZhbHVlLCBtaW5NYXhWYWx1ZSwgbWluTWF4VmFsdWVGb3VuZCk7XG4gICAgICAgICAgICBpZiAodmFsdWUgJHtwb29sVHlwZSA9PT0gJ21pbicgPyAnPD0nIDogJz49J30gY3VycmVudE1pbk1heFZhbHVlKSB7XG4gICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gdmFsdWU7XG4gICAgICAgICAgICAgIG1pbk1heFZhbHVlRm91bmQgPSAxLjA7XG4gICAgICAgICAgICAgIGlmICgke2NvbXB1dGVQb3NpdGlvbnN9KSB7XG4gICAgICAgICAgICAgICAgbWluTWF4UG9zaXRpb24gPSB3UiAqICR7ZlNpemV9LjAgKyB3QztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCgke3JldHVyblZhbHVlfSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwb29sQ29tbW9uKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoeCwgJ3gnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbmRhcnJheSc7XG5cbmV4cG9ydCB0eXBlIElucHV0ID0ge1xuICBuYW1lOiBzdHJpbmc7IGFycmF5OiBOREFycmF5O1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXJLZXkoaW5wdXRzOiBOREFycmF5W10sIG91dHB1dDogTkRBcnJheSk6IHN0cmluZyB7XG4gIGNvbnN0IGlucyA9IGlucHV0cy5tYXAoeCA9PiB4LnNoYXBlICsgJ18nICsgeC5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgcmV0dXJuIGlucy5qb2luKCdfJykgKyAnXycgKyBvdXRwdXQuc2hhcGUgKyAnXycgKyBvdXRwdXQuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXIoXG4gICAgaW5wdXRzOiBJbnB1dFtdLCBvdXRwdXQ6IE5EQXJyYXksIHVzZXJDb2RlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBpbnB1dFByZWZpeFNuaXBwZXQgPVxuICAgICAgaW5wdXRzLm1hcCh4ID0+IGB1bmlmb3JtIHNhbXBsZXIyRCAke3gubmFtZX07YCkuam9pbignXFxuJyk7XG4gIGNvbnN0IGlucHV0U2FtcGxpbmdTbmlwcGV0ID1cbiAgICAgIGlucHV0cy5tYXAoeCA9PiBnZXRJbnB1dFNhbXBsaW5nU25pcHBldCh4KSkuam9pbignXFxuJyk7XG4gIGNvbnN0IG91dFRleFNoYXBlID0gb3V0cHV0LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gIGNvbnN0IG91dHB1dFNhbXBsaW5nU25pcHBldCA9XG4gICAgICBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQob3V0cHV0LnNoYXBlLCBvdXRUZXhTaGFwZSk7XG4gIGNvbnN0IHNvdXJjZSA9IFtcbiAgICBTSEFERVJfUFJFRklYLCBpbnB1dFByZWZpeFNuaXBwZXQsIFNBTVBMRV8yRF9TTklQUEVULCBpbnB1dFNhbXBsaW5nU25pcHBldCxcbiAgICBvdXRwdXRTYW1wbGluZ1NuaXBwZXQsIHVzZXJDb2RlXG4gIF0uam9pbignXFxuJyk7XG4gIHJldHVybiBzb3VyY2U7XG59XG5cbmZ1bmN0aW9uIGdldElucHV0U2FtcGxpbmdTbmlwcGV0KGlucHV0OiBJbnB1dCkge1xuICBjb25zdCBhcnIgPSBpbnB1dC5hcnJheTtcbiAgY29uc3Qgc2hhcGUgPSBhcnIuc2hhcGU7XG4gIGNvbnN0IHRleFNoYXBlID0gYXJyLmdldFRleHR1cmVTaGFwZVJDKHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0pO1xuICBzd2l0Y2ggKHNoYXBlLmxlbmd0aCkge1xuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiBnZXRTYW1wbGVyMkQoaW5wdXQubmFtZSwgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGUpO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYCR7YXJyLnJhbmt9LUQgaW5wdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQoXG4gICAgb3V0U2hhcGU6IG51bWJlcltdLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIHN3aXRjaCAob3V0U2hhcGUubGVuZ3RoKSB7XG4gICAgY2FzZSAyOlxuICAgICAgcmV0dXJuIGdldE91dHB1dDJEQ29vcmRzKG91dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIG91dFRleFNoYXBlKTtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGAke291dFNoYXBlLmxlbmd0aH0tRCBvdXRwdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5jb25zdCBTSEFERVJfUFJFRklYID0gYFxuICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcbiAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICB2b2lkIHNldE91dHB1dChmbG9hdCB2YWwpIHtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbCwgMCwgMCwgMCk7XG4gIH1cbmA7XG5cbmNvbnN0IFNBTVBMRV8yRF9TTklQUEVUID0gYFxuICBmbG9hdCBzYW1wbGUyRChzYW1wbGVyMkQgdGV4dHVyZSwgZmxvYXQgdGV4TnVtUiwgZmxvYXQgdGV4TnVtQywgZmxvYXQgbnVtQyxcbiAgICAgIGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgZmxvYXQgaW5kZXggPSBkb3QodmVjMihyb3csIGNvbCksIHZlYzIobnVtQywgMS4wKSk7XG4gICAgZmxvYXQgdGV4UiA9IGZsb29yKGluZGV4IC8gdGV4TnVtQyk7XG4gICAgZmxvYXQgdGV4QyA9IG1vZChpbmRleCwgdGV4TnVtQyk7XG4gICAgdmVjMiB1diA9ICh2ZWMyKHRleEMsIHRleFIpICsgaGFsZkNSKSAvIHZlYzIodGV4TnVtQywgdGV4TnVtUik7XG4gICAgcmV0dXJuIHRleHR1cmUyRCh0ZXh0dXJlLCB1dikucjtcbiAgfVxuYDtcblxuZnVuY3Rpb24gZ2V0T3V0cHV0MkRDb29yZHMoXG4gICAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIHRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGlmICh1dGlsLmFycmF5c0VxdWFsKHNoYXBlLCB0ZXhTaGFwZSkpIHtcbiAgICByZXR1cm4gYFxuICAgICAgdmVjMiBnZXRPdXRwdXRDb29yZHMoKSB7XG4gICAgICAgIHJldHVybiBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgfVxuICAgIGA7XG4gIH1cbiAgcmV0dXJuIGBcbiAgICB2ZWMyIGdldE91dHB1dENvb3JkcygpIHtcbiAgICAgIHZlYzIgcmVzVGV4UkMgPSBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgZmxvYXQgaW5kZXggPSBkb3QocmVzVGV4UkMsIHZlYzIoJHt0ZXhTaGFwZVsxXX0uMCwgMS4wKSk7XG4gICAgICBmbG9hdCByID0gZmxvb3IoaW5kZXggLyAke3NoYXBlWzFdfS4wKTtcbiAgICAgIGZsb2F0IGMgPSBtb2QoaW5kZXgsICR7c2hhcGVbMV19LjApO1xuICAgICAgcmV0dXJuIHZlYzIociwgYyk7XG4gICAgfVxuICBgO1xufVxuXG5mdW5jdGlvbiBnZXRTYW1wbGVyMkQoXG4gICAgdGV4TmFtZTogc3RyaW5nLCBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgY29uc3QgZnVuY05hbWUgPSAnZ2V0JyArIHRleE5hbWUuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyB0ZXhOYW1lLnNsaWNlKDEpO1xuICBjb25zdCB0UiA9IHRleFNoYXBlWzBdO1xuICBjb25zdCB0QyA9IHRleFNoYXBlWzFdO1xuICBpZiAodXRpbC5hcnJheXNFcXVhbChzaGFwZSwgdGV4U2hhcGUpKSB7XG4gICAgcmV0dXJuIGBcbiAgICAgIGZsb2F0ICR7ZnVuY05hbWV9KGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgICAgIHZlYzIgdXYgPSAodmVjMihjb2wsIHJvdykgKyBoYWxmQ1IpIC8gdmVjMigke3RDfS4wLCAke3RSfS4wKTtcbiAgICAgICAgcmV0dXJuIHRleHR1cmUyRCgke3RleE5hbWV9LCB1dikucjtcbiAgICAgIH1cbiAgICBgO1xuICB9XG4gIHJldHVybiBgXG4gICAgZmxvYXQgJHtmdW5jTmFtZX0oZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICAgIHJldHVybiBzYW1wbGUyRCgke3RleE5hbWV9LCAke3RSfS4wLCAke3RDfS4wLCAke3NoYXBlWzFdfS4wLCByb3csIGNvbCk7XG4gICAgfVxuICBgO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2NvbHVtbnMsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICBtYXRyaXhTaXplOiBudW1iZXIsIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIG1hdHJpeFNpemUgKiBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb2xvck1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbY29sdW1ucyAqIDQsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWF0cml4U2l6ZUZyb21VbnBhY2tlZEFycmF5U2l6ZShcbiAgICB1bnBhY2tlZFNpemU6IG51bWJlciwgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAodW5wYWNrZWRTaXplICUgY2hhbm5lbHNQZXJUZXh0dXJlICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAndW5wYWNrZWRTaXplICgnICsgdW5wYWNrZWRTaXplICsgJykgbXVzdCBiZSBhIG11bHRpcGxlIG9mICcgK1xuICAgICAgICBjaGFubmVsc1BlclRleHR1cmUpO1xuICB9XG4gIHJldHVybiB1bnBhY2tlZFNpemUgLyBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBlbmNvZGVNYXRyaXhUb1VucGFja2VkQXJyYXkoXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXksIHVucGFja2VkQXJyYXk6IEZsb2F0MzJBcnJheSxcbiAgICBjaGFubmVsc1BlclRleHR1cmU6IG51bWJlcikge1xuICBjb25zdCByZXF1aXJlZFNpemUgPVxuICAgICAgZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBpZiAodW5wYWNrZWRBcnJheS5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd1bnBhY2tlZEFycmF5IGxlbmd0aCAoJyArIHVucGFja2VkQXJyYXkubGVuZ3RoICtcbiAgICAgICAgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgc3JjID0gMDsgc3JjIDwgbWF0cml4Lmxlbmd0aDsgKytzcmMpIHtcbiAgICB1bnBhY2tlZEFycmF5W2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICBkc3QgKz0gY2hhbm5lbHNQZXJUZXh0dXJlO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICB1bnBhY2tlZEFycmF5OiBGbG9hdDMyQXJyYXksIG1hdHJpeDogRmxvYXQzMkFycmF5LFxuICAgIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldE1hdHJpeFNpemVGcm9tVW5wYWNrZWRBcnJheVNpemUoXG4gICAgICB1bnBhY2tlZEFycmF5Lmxlbmd0aCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcbiAgaWYgKG1hdHJpeC5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgbGV0IGRzdCA9IDA7XG4gIGZvciAobGV0IHNyYyA9IDA7IHNyYyA8IHVucGFja2VkQXJyYXkubGVuZ3RoOyBzcmMgKz0gY2hhbm5lbHNQZXJUZXh0dXJlKSB7XG4gICAgbWF0cml4W2RzdCsrXSA9IHVucGFja2VkQXJyYXlbc3JjXTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtNYXRoLmNlaWwoY29sdW1ucyAvIDIpLCBNYXRoLmNlaWwocm93cyAvIDIpXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBbdywgaF0gPSBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgcmV0dXJuIHcgKiBoICogNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgcGFja2VkUkdCQTogRmxvYXQzMkFycmF5KSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUocm93cywgY29sdW1ucyk7XG4gIGlmIChwYWNrZWRSR0JBLmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ3BhY2tlZFJHQkEgbGVuZ3RoICgnICsgcGFja2VkUkdCQS5sZW5ndGggK1xuICAgICAgICAnKSBtdXN0IGJlID49ICcgKyByZXF1aXJlZFNpemUpO1xuICB9XG4gIC8qXG4gICAgVW5wYWNrZWQgbWF0cml4LCByb3ctbWFqb3Igb3JkZXIgaW4gRmxvYXQzMkFycmF5WzE2XTogIEEgQiBDIERcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRSBGIEcgSFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJIEogSyBMXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE0gTiBPIFBcblxuICAgIFBhY2tlZCBtYXRyaXgsIDJ4MiBSR0JBMzIgdGV4dHVyZSAobWVtb3J5IHZpZXcpOiAgICAgICBBQkVGIENER0ggSUpNTiBLTE9QXG5cbiAgICBQYWNrZWQgbWF0cml4LCAyeDIgUkdCQTMyIHRleHR1cmUgKG1hdHJpeCB2aWV3KTogICAgICAgQUJ8Q0RcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRUZ8R0hcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0rLS1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSUp8S0xcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTU58T1BcbiAgICovXG4gIGNvbnN0IFt0ZXh0dXJlV2lkdGgsIHRleHR1cmVIZWlnaHRdID1cbiAgICAgIGdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBvZGRXaWR0aCA9IChjb2x1bW5zICUgMikgPT09IDE7XG4gIGNvbnN0IG9kZEhlaWdodCA9IChyb3dzICUgMikgPT09IDE7XG4gIGNvbnN0IHdpZHRoSW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihjb2x1bW5zIC8gMik7XG4gIGNvbnN0IGhlaWdodEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3Iocm93cyAvIDIpO1xuXG4gIC8vIGxvb3Agb3ZlciBmdWxsIDJ4MiBibG9ja3NcbiAge1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IChvZGRXaWR0aCA/IDQgOiAwKTtcbiAgICBjb25zdCBvbmVSb3cgPSBjb2x1bW5zO1xuICAgIGxldCBkc3QgPSAwO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIGNvbnN0IG1hdHJpeFNyY1JvdyA9IChibG9ja1kgKiAyICogY29sdW1ucyk7XG4gICAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgICAgY29uc3QgbWF0cml4U3JjQ29sID0gYmxvY2tYICogMjtcbiAgICAgICAgY29uc3Qgc3JjID0gbWF0cml4U3JjUm93ICsgbWF0cml4U3JjQ29sO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAxXSA9IG1hdHJpeFtzcmMgKyAxXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAyXSA9IG1hdHJpeFtzcmMgKyBvbmVSb3ddO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDNdID0gbWF0cml4W3NyYyArIG9uZVJvdyArIDFdO1xuICAgICAgICBkc3QgKz0gNDtcbiAgICAgIH1cbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBkb3duIGZpbmFsIG9kZCBjb2x1bW5cbiAgaWYgKG9kZFdpZHRoKSB7XG4gICAgbGV0IHNyYyA9IGNvbHVtbnMgLSAxO1xuICAgIGxldCBkc3QgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGNvbnN0IHNyY1N0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgcGFja2VkUkdCQVtkc3RdID0gbWF0cml4W3NyY107XG4gICAgICBwYWNrZWRSR0JBW2RzdCArIDJdID0gbWF0cml4W3NyYyArIGNvbHVtbnNdO1xuICAgICAgc3JjICs9IHNyY1N0cmlkZTtcbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBhY3Jvc3MgZmluYWwgcm93XG4gIGlmIChvZGRIZWlnaHQpIHtcbiAgICBsZXQgc3JjID0gKHJvd3MgLSAxKSAqIGNvbHVtbnM7XG4gICAgbGV0IGRzdCA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgZHN0ICs9IDI7XG4gICAgfVxuICB9XG5cbiAgLy8gZmlsbCBpbiBib3R0b20tcmlnaHQgdGV4ZWxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIHBhY2tlZFJHQkFbcGFja2VkUkdCQS5sZW5ndGggLSA0XSA9IG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV07XG4gIH1cblxuICByZXR1cm4gcGFja2VkUkdCQTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU1hdHJpeEZyb21QYWNrZWRSR0JBKFxuICAgIHBhY2tlZFJHQkE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCByZXF1aXJlZFNpemUgPSByb3dzICogY29sdW1ucztcbiAgaWYgKHJlcXVpcmVkU2l6ZSA8IG1hdHJpeC5sZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgY29uc3Qgb2RkV2lkdGggPSAoY29sdW1ucyAlIDIpID09PSAxO1xuICBjb25zdCBvZGRIZWlnaHQgPSAocm93cyAlIDIpID09PSAxO1xuICBjb25zdCB3aWR0aEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3IoY29sdW1ucyAvIDIpO1xuICBjb25zdCBoZWlnaHRJbkZ1bGxCbG9ja3MgPSBNYXRoLmZsb29yKHJvd3MgLyAyKTtcbiAgY29uc3QgW3RleHR1cmVXaWR0aCwgdGV4dHVyZUhlaWdodF0gPVxuICAgICAgZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG5cbiAgLy8gbG9vcCBvdmVyIGZ1bGwgMngyIGJsb2Nrc1xuICB7XG4gICAgY29uc3Qgc3JjU3RyaWRlID0gb2RkV2lkdGggPyA0IDogMDtcbiAgICBjb25zdCBkc3RTdHJpZGUgPSBjb2x1bW5zICsgKG9kZFdpZHRoID8gMSA6IDApO1xuICAgIGxldCBzcmMgPSAwO1xuICAgIGxldCBkc3RSb3cxID0gMDtcbiAgICBsZXQgZHN0Um93MiA9IGNvbHVtbnM7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgZm9yIChsZXQgYmxvY2tYID0gMDsgYmxvY2tYIDwgd2lkdGhJbkZ1bGxCbG9ja3M7ICsrYmxvY2tYKSB7XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICB9XG4gICAgICBzcmMgKz0gc3JjU3RyaWRlO1xuICAgICAgZHN0Um93MSArPSBkc3RTdHJpZGU7XG4gICAgICBkc3RSb3cyICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGRvd24gZmluYWwgY29sdW1uXG4gIGlmIChvZGRXaWR0aCkge1xuICAgIGxldCBzcmMgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGxldCBkc3QgPSBjb2x1bW5zIC0gMTtcbiAgICBjb25zdCBzcmNTdHJpZGUgPSB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIG1hdHJpeFtkc3RdID0gcGFja2VkUkdCQVtzcmNdO1xuICAgICAgbWF0cml4W2RzdCArIGNvbHVtbnNdID0gcGFja2VkUkdCQVtzcmMgKyAyXTtcbiAgICAgIHNyYyArPSBzcmNTdHJpZGU7XG4gICAgICBkc3QgKz0gZHN0U3RyaWRlO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvb3AgYWNyb3NzIGZpbmFsIHJvd1xuICBpZiAob2RkSGVpZ2h0KSB7XG4gICAgbGV0IHNyYyA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGxldCBkc3QgPSAocm93cyAtIDEpICogY29sdW1ucztcbiAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIHNyYyArPSAyO1xuICAgIH1cbiAgfVxuXG4gIC8vIGZpbGwgaW4gYm90dG9tLXJpZ2h0IGNlbGxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV0gPSBwYWNrZWRSR0JBW3BhY2tlZFJHQkEubGVuZ3RoIC0gNF07XG4gIH1cblxuICByZXR1cm4gbWF0cml4O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5sZXQgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xubGV0IFdFQkdMMl9FTkFCTEVEOiBib29sZWFufHVuZGVmaW5lZCA9IG51bGwhO1xubGV0IE1BWF9URVhUVVJFX1NJWkU6IG51bWJlciA9IG51bGwhO1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMge1xuICBhbHBoYT86IGJvb2xlYW47XG4gIGFudGlhbGlhcz86IGJvb2xlYW47XG4gIHByZW11bHRpcGxpZWRBbHBoYT86IGJvb2xlYW47XG4gIHByZXNlcnZlRHJhd2luZ0J1ZmZlcj86IGJvb2xlYW47XG4gIGRlcHRoPzogYm9vbGVhbjtcbiAgc3RlbmNpbD86IGJvb2xlYW47XG4gIGZhaWxJZk1ham9yUGVyZm9ybWFuY2VDYXZlYXQ/OiBib29sZWFuO1xufVxuXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGNvbnN0IElTX05BTl9TSEFERVJfRlVOQyA9IGBcbmJvb2wgaXNOYU4oZmxvYXQgdmFsKSB7XG4gIHJldHVybiB2YWwgPT0gdmFsID8gZmFsc2UgOiB0cnVlO1xufVxuYDtcblxuZXhwb3J0IGludGVyZmFjZSBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uIHsgbG9zZUNvbnRleHQoKTogdm9pZDsgfVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0KGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOlxuICAgIFdlYkdMUmVuZGVyaW5nQ29udGV4dCB7XG4gIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICBjYW52YXMud2lkdGggPSAxO1xuICBjYW52YXMuaGVpZ2h0ID0gMTtcbiAgcmV0dXJuIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoY2FudmFzLCBhdHRyaWJ1dGVzKTtcbn1cblxuLyoqXG4gKiBGb3JjZSB0aGUgbGlicmFyeSB0byBwcmVmZXIgV2ViR0wgMS4wIGluc3RlYWQgb2YgV2ViR0wgMi4wIGV2ZW4gd2hlbiBXZWJHTFxuICogMi4wIGlzIGF2YWlsYWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZWZlcldlYkdMMSgpIHtcbiAgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xuICBXRUJHTDJfRU5BQkxFRCA9IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBQcmVmZXIgV2ViR0wgMi4wIHRvIFdlYkdMIDEuMC4gVGhpcyBpcyB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZmVyV2ViR0wyKCkge1xuICBVU0VfV0VCR0wyX1dIRU5fQVZBSUxBQkxFID0gdHJ1ZTtcbiAgV0VCR0wyX0VOQUJMRUQgPSB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1dlYkdMMkVuYWJsZWQoKSB7XG4gIGlmICghVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGlmIChXRUJHTDJfRU5BQkxFRCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgdGVtcENhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNvbnN0IGdsID0gdGVtcENhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbDInKTtcbiAgICBpZiAoZ2wgIT0gbnVsbCkge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSB0cnVlO1xuXG4gICAgICBjb25zdCBsb3NlQ29udGV4dEV4dGVuc2lvbiA9XG4gICAgICAgICAgZ2V0RXh0ZW5zaW9uT3JUaHJvdyhcbiAgICAgICAgICAgICAgZ2wgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0LCAnV0VCR0xfbG9zZV9jb250ZXh0JykgYXNcbiAgICAgICAgICBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICAgICAgbG9zZUNvbnRleHRFeHRlbnNpb24ubG9zZUNvbnRleHQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIFdFQkdMMl9FTkFCTEVEO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0RnJvbUNhbnZhcyhcbiAgICBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50LFxuICAgIGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQge1xuICBsZXQgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgaWYgKGlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgZ2wgPSBjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wyJywgYXR0cmlidXRlcykgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB9IGVsc2Uge1xuICAgIGdsID0gKGNhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbCcsIGF0dHJpYnV0ZXMpIHx8XG4gICAgICAgICAgY2FudmFzLmdldENvbnRleHQoJ2V4cGVyaW1lbnRhbC13ZWJnbCcsIGF0dHJpYnV0ZXMpKSBhc1xuICAgICAgICBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIH1cblxuICBpZiAoZ2wgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignVGhpcyBicm93c2VyIGRvZXMgbm90IHN1cHBvcnQgV2ViR0wuJyk7XG4gIH1cbiAgcmV0dXJuIGdsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2FsbEFuZENoZWNrPFQ+KGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZ1bmM6ICgpID0+IFQpOiBUIHtcbiAgY29uc3QgcmV0dXJuVmFsdWUgPSBmdW5jKCk7XG4gIGNoZWNrV2ViR0xFcnJvcihnbCk7XG4gIHJldHVybiByZXR1cm5WYWx1ZTtcbn1cblxubGV0IHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCA9IGZhbHNlO1xuXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRGVidWdXZWJHTEVycm9yQ2hlY2tpbmcoZW5hYmxlZDogYm9vbGVhbikge1xuICB3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQgPSBlbmFibGVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tXZWJHTEVycm9yKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgaWYgKHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCkge1xuICAgIGNvbnN0IGVycm9yID0gZ2wuZ2V0RXJyb3IoKTtcbiAgICBpZiAoZXJyb3IgIT09IGdsLk5PX0VSUk9SKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlYkdMIEVycm9yOiAnICsgZ2V0V2ViR0xFcnJvck1lc3NhZ2UoZ2wsIGVycm9yKSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRXZWJHTEVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5OT19FUlJPUjpcbiAgICAgIHJldHVybiAnTk9fRVJST1InO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9FTlVNOlxuICAgICAgcmV0dXJuICdJTlZBTElEX0VOVU0nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9WQUxVRTpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9WQUxVRSc7XG4gICAgY2FzZSBnbC5JTlZBTElEX09QRVJBVElPTjpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9PUEVSQVRJT04nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9GUkFNRUJVRkZFUl9PUEVSQVRJT046XG4gICAgICByZXR1cm4gJ0lOVkFMSURfRlJBTUVCVUZGRVJfT1BFUkFUSU9OJztcbiAgICBjYXNlIGdsLk9VVF9PRl9NRU1PUlk6XG4gICAgICByZXR1cm4gJ09VVF9PRl9NRU1PUlknO1xuICAgIGNhc2UgZ2wuQ09OVEVYVF9MT1NUX1dFQkdMOlxuICAgICAgcmV0dXJuICdDT05URVhUX0xPU1RfV0VCR0wnO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gJ1Vua25vd24gZXJyb3IgY29kZSAnICsgc3RhdHVzO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHRlbnNpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGV4dGVuc2lvbk5hbWU6IHN0cmluZyk6IHt9IHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPHt9PihcbiAgICAgIGdsLCAoKSA9PiBnbC5nZXRFeHRlbnNpb24oZXh0ZW5zaW9uTmFtZSksXG4gICAgICAnRXh0ZW5zaW9uIFwiJyArIGV4dGVuc2lvbk5hbWUgKyAnXCIgbm90IHN1cHBvcnRlZCBvbiB0aGlzIGJyb3dzZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhTaGFkZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdmVydGV4U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IHZlcnRleFNoYWRlcjogV2ViR0xTaGFkZXIgPSB0aHJvd0lmTnVsbDxXZWJHTFNoYWRlcj4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlU2hhZGVyKGdsLlZFUlRFWF9TSEFERVIpLFxuICAgICAgJ1VuYWJsZSB0byBjcmVhdGUgdmVydGV4IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZSh2ZXJ0ZXhTaGFkZXIsIHZlcnRleFNoYWRlclNvdXJjZSkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmNvbXBpbGVTaGFkZXIodmVydGV4U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIodmVydGV4U2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0U2hhZGVySW5mb0xvZyh2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBjb21waWxlIHZlcnRleCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIHZlcnRleFNoYWRlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYWdtZW50U2hhZGVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZyYWdtZW50U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyOiBXZWJHTFNoYWRlciA9IHRocm93SWZOdWxsPFdlYkdMU2hhZGVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVTaGFkZXIoZ2wuRlJBR01FTlRfU0hBREVSKSxcbiAgICAgICdVbmFibGUgdG8gY3JlYXRlIGZyYWdtZW50IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZShmcmFnbWVudFNoYWRlciwgZnJhZ21lbnRTaGFkZXJTb3VyY2UpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jb21waWxlU2hhZGVyKGZyYWdtZW50U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIoZnJhZ21lbnRTaGFkZXIsIGdsLkNPTVBJTEVfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRTaGFkZXJJbmZvTG9nKGZyYWdtZW50U2hhZGVyKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gY29tcGlsZSBmcmFnbWVudCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIGZyYWdtZW50U2hhZGVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xQcm9ncmFtIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMUHJvZ3JhbT4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlUHJvZ3JhbSgpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTFByb2dyYW0uJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsaW5rUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5saW5rUHJvZ3JhbShwcm9ncmFtKSk7XG4gIGlmIChnbC5nZXRQcm9ncmFtUGFyYW1ldGVyKHByb2dyYW0sIGdsLkxJTktfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRQcm9ncmFtSW5mb0xvZyhwcm9ncmFtKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gbGluayB2ZXJ0ZXggYW5kIGZyYWdtZW50IHNoYWRlcnMuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlUHJvZ3JhbShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC52YWxpZGF0ZVByb2dyYW0ocHJvZ3JhbSkpO1xuICBpZiAoZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5WQUxJREFURV9TVEFUVVMpID09PSBmYWxzZSkge1xuICAgIGNvbnNvbGUubG9nKGdsLmdldFByb2dyYW1JbmZvTG9nKHByb2dyYW0pKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1NoYWRlciBwcm9ncmFtIHZhbGlkYXRpb24gZmFpbGVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdGF0aWNWZXJ0ZXhCdWZmZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgZGF0YTogRmxvYXQzMkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5idWZmZXJEYXRhKGdsLkFSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVN0YXRpY0luZGV4QnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGRhdGE6IFVpbnQxNkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGJ1ZmZlcikpO1xuICBjYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wuYnVmZmVyRGF0YShnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IG51bWJlciB7XG4gIGlmIChNQVhfVEVYVFVSRV9TSVpFICE9IG51bGwpIHtcbiAgICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbiAgfVxuICBNQVhfVEVYVFVSRV9TSVpFID1cbiAgICAgIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2whLmdldFBhcmFtZXRlcihnbCEuTUFYX1RFWFRVUkVfU0laRSkpO1xuICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldENoYW5uZWxzUGVyVGV4dHVyZSgpOiBudW1iZXIge1xuICBpZiAoaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICByZXR1cm4gMTtcbiAgfVxuICByZXR1cm4gNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVRleHR1cmUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMVGV4dHVyZSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTFRleHR1cmU+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVRleHR1cmUoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xUZXh0dXJlLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVUZXh0dXJlU2l6ZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlU2l6ZTogbnVtYmVyID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGlmICgod2lkdGggPD0gMCkgfHwgKGhlaWdodCA8PSAwKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcignUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICsgJyBpcyBpbnZhbGlkLicpO1xuICB9XG4gIGlmICgod2lkdGggPiBtYXhUZXh0dXJlU2l6ZSkgfHwgKGhlaWdodCA+IG1heFRleHR1cmVTaXplKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIGNvbnN0IG1heCA9ICdbJyArIG1heFRleHR1cmVTaXplICsgJ3gnICsgbWF4VGV4dHVyZVNpemUgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICtcbiAgICAgICAgJyBncmVhdGVyIHRoYW4gV2ViR0wgbWF4aW11bSBvbiB0aGlzIGJyb3dzZXIgLyBHUFUgJyArIG1heCArICcuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYW1lYnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEZyYW1lYnVmZmVyIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMRnJhbWVidWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUZyYW1lYnVmZmVyKCksICdVbmFibGUgdG8gY3JlYXRlIFdlYkdMRnJhbWVidWZmZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYXR0cmlidXRlOiBzdHJpbmcsXG4gICAgYnVmZmVyOiBXZWJHTEJ1ZmZlciwgYXJyYXlFbnRyaWVzUGVySXRlbTogbnVtYmVyLCBpdGVtU3RyaWRlSW5CeXRlczogbnVtYmVyLFxuICAgIGl0ZW1PZmZzZXRJbkJ5dGVzOiBudW1iZXIpIHtcbiAgY29uc3QgbG9jID0gZ2wuZ2V0QXR0cmliTG9jYXRpb24ocHJvZ3JhbSwgYXR0cmlidXRlKTtcbiAgaWYgKGxvYyA9PT0gLTEpIHtcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihcbiAgICAgICAgJ1VuYWJsZSB0byBnZXQgYXR0cmlidXRlIFwiJyArIGF0dHJpYnV0ZSArICdcIiBvbiBXZWJHTFByb2dyYW0uJyk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIChlcnJvciBhcyBhbnkpLm5hbWVkVmVydGV4QXR0cmlidXRlTm90Rm91bmQgPSBhdHRyaWJ1dGU7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudmVydGV4QXR0cmliUG9pbnRlcihcbiAgICAgICAgICBsb2MsIGFycmF5RW50cmllc1Blckl0ZW0sIGdsLkZMT0FULCBmYWxzZSwgaXRlbVN0cmlkZUluQnl0ZXMsXG4gICAgICAgICAgaXRlbU9mZnNldEluQnl0ZXMpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGVWZXJ0ZXhBdHRyaWJBcnJheShsb2MpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRUZXh0dXJlVW5pdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZFRleHR1cmVVbml0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICB1bmlmb3JtTmFtZTogc3RyaW5nKTogV2ViR0xVbmlmb3JtTG9jYXRpb24ge1xuICByZXR1cm4gdGhyb3dJZk51bGw8V2ViR0xVbmlmb3JtTG9jYXRpb24+KFxuICAgICAgZ2wsICgpID0+IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCB1bmlmb3JtTmFtZSksXG4gICAgICAndW5pZm9ybSBcIicgKyB1bmlmb3JtTmFtZSArICdcIiBub3QgcHJlc2VudCBpbiBwcm9ncmFtLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFRleHR1cmVUb1Byb2dyYW1Vbmlmb3JtU2FtcGxlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICB1bmlmb3JtU2FtcGxlck5hbWU6IHN0cmluZywgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGJpbmRUZXh0dXJlVW5pdChnbCwgdGV4dHVyZSwgdGV4dHVyZVVuaXQpKTtcbiAgY29uc3Qgc2FtcGxlckxvY2F0aW9uID1cbiAgICAgIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KGdsLCBwcm9ncmFtLCB1bmlmb3JtU2FtcGxlck5hbWUpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnVuaWZvcm0xaShzYW1wbGVyTG9jYXRpb24sIHRleHR1cmVVbml0KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEZyYW1lYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBudWxsKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmlld3BvcnQoMCwgMCwgZ2wuY2FudmFzLndpZHRoLCBnbC5jYW52YXMuaGVpZ2h0KSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuc2Npc3NvcigwLCAwLCBnbC5jYW52YXMud2lkdGgsIGdsLmNhbnZhcy5oZWlnaHQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIHRleHR1cmUsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIG51bGwsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlRnJhbWVidWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICBjb25zdCBzdGF0dXMgPSBnbC5jaGVja0ZyYW1lYnVmZmVyU3RhdHVzKGdsLkZSQU1FQlVGRkVSKTtcbiAgaWYgKHN0YXR1cyAhPT0gZ2wuRlJBTUVCVUZGRVJfQ09NUExFVEUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdFcnJvciBiaW5kaW5nIGZyYW1lYnVmZmVyOiAnICsgZ2V0RnJhbWVidWZmZXJFcnJvck1lc3NhZ2UoZ2wsIHN0YXR1cykpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFtZWJ1ZmZlckVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfQVRUQUNITUVOVCc7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX01JU1NJTkdfQVRUQUNITUVOVDpcbiAgICAgIHJldHVybiAnRlJBTUVCVUZGRVJfSU5DT01QTEVURV9NSVNTSU5HX0FUVEFDSE1FTlQnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9ESU1FTlNJT05TOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0RJTUVOU0lPTlMnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfVU5TVVBQT1JURUQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICd1bmtub3duIGVycm9yICcgKyBzdGF0dXM7XG4gIH1cbn1cblxuZnVuY3Rpb24gdGhyb3dJZk51bGw8VD4oXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcmV0dXJuVE9yTnVsbDogKCkgPT4gVCB8IG51bGwsXG4gICAgZmFpbHVyZU1lc3NhZ2U6IHN0cmluZyk6IFQge1xuICBjb25zdCB0T3JOdWxsOiBUfG51bGwgPSBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IHJldHVyblRPck51bGwoKSk7XG4gIGlmICh0T3JOdWxsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoZmFpbHVyZU1lc3NhZ2UpO1xuICB9XG4gIHJldHVybiB0T3JOdWxsIGFzIFQ7XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlVW5pdCA9IGdsLk1BWF9DT01CSU5FRF9URVhUVVJFX0lNQUdFX1VOSVRTIC0gMTtcbiAgY29uc3QgZ2xUZXh0dXJlVW5pdCA9IHRleHR1cmVVbml0ICsgZ2wuVEVYVFVSRTA7XG4gIGlmIChnbFRleHR1cmVVbml0IDwgZ2wuVEVYVFVSRTAgfHwgZ2xUZXh0dXJlVW5pdCA+IG1heFRleHR1cmVVbml0KSB7XG4gICAgY29uc3QgdGV4dHVyZVVuaXRSYW5nZSA9ICdbZ2wuVEVYVFVSRTAsIGdsLlRFWFRVUkUnICsgbWF4VGV4dHVyZVVuaXQgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKCd0ZXh0dXJlVW5pdCBtdXN0IGJlIGluICcgKyB0ZXh0dXJlVW5pdFJhbmdlICsgJy4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGV4dHVyZVNoYXBlRnJvbUxvZ2ljYWxTaGFwZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBsb2dpY2FsU2hhcGU6IG51bWJlcltdLFxuICAgIHByZWZlcnJlZFRleFNoYXBlPzogW251bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBtYXhUZXhTaXplID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUobG9naWNhbFNoYXBlKTtcbiAgaWYgKHByZWZlcnJlZFRleFNoYXBlICE9IG51bGwpIHtcbiAgICBjb25zdCBzaXplUHJlZmVycmVkID0gdXRpbC5zaXplRnJvbVNoYXBlKHByZWZlcnJlZFRleFNoYXBlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgc2l6ZSA9PT0gc2l6ZVByZWZlcnJlZCxcbiAgICAgICAgYFNpemUgb2Ygc2hhcGUgKCR7c2l6ZX0pIG11c3QgbWF0Y2ggc2l6ZSBvZiBgICtcbiAgICAgICAgICAgIGBwcmVmZXJyZWRTaGFwZSAoJHtzaXplUHJlZmVycmVkfSlgKTtcbiAgICBpZiAocHJlZmVycmVkVGV4U2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgICBwcmVmZXJyZWRUZXhTaGFwZVsxXSA8PSBtYXhUZXhTaXplKSB7XG4gICAgICByZXR1cm4gcHJlZmVycmVkVGV4U2hhcGU7XG4gICAgfVxuICB9XG5cbiAgaWYgKGxvZ2ljYWxTaGFwZS5sZW5ndGggPD0gMSAmJiBzaXplIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gW3NpemUsIDFdO1xuICB9IGVsc2UgaWYgKFxuICAgICAgbG9naWNhbFNoYXBlLmxlbmd0aCA9PT0gMiAmJiBsb2dpY2FsU2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgbG9naWNhbFNoYXBlWzFdIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gbG9naWNhbFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl07XG4gIH0gZWxzZSBpZiAoXG4gICAgICBsb2dpY2FsU2hhcGUubGVuZ3RoID09PSAzICYmIGxvZ2ljYWxTaGFwZVswXSA8PSBtYXhUZXhTaXplICYmXG4gICAgICBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl0gPD0gbWF4VGV4U2l6ZSkge1xuICAgIHJldHVybiBbbG9naWNhbFNoYXBlWzBdLCBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl1dO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB1dGlsLnNpemVUb1NxdWFyaXNoU2hhcGUoc2l6ZSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IGZ1bmN0aW9uIGV4cGVjdEFycmF5c0Nsb3NlKFxuICAgIGFjdHVhbDogRmxvYXQzMkFycmF5LCBleHBlY3RlZDogRmxvYXQzMkFycmF5LCBlcHNpbG9uOiBudW1iZXIpIHtcbiAgaWYgKGFjdHVhbC5sZW5ndGggIT09IGV4cGVjdGVkLmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ01hdHJpY2VzIGhhdmUgZGlmZmVyZW50IGxlbmd0aHMgKCcgKyBhY3R1YWwubGVuZ3RoICsgJyB2cyAnICtcbiAgICAgICAgZXhwZWN0ZWQubGVuZ3RoICsgJykuJyk7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBleHBlY3RlZC5sZW5ndGg7ICsraSkge1xuICAgIGNvbnN0IGEgPSBhY3R1YWxbaV07XG4gICAgY29uc3QgZSA9IGV4cGVjdGVkW2ldO1xuICAgIGlmIChpc05hTihhKSAmJiBpc05hTihlKSkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGlmIChpc05hTihhKSB8fCBpc05hTihlKSB8fCBNYXRoLmFicyhhIC0gZSkgPiBlcHNpbG9uKSB7XG4gICAgICBjb25zdCBhY3R1YWxTdHIgPSAnYWN0dWFsWycgKyBpICsgJ10gPT09ICcgKyBhO1xuICAgICAgY29uc3QgZXhwZWN0ZWRTdHIgPSAnZXhwZWN0ZWRbJyArIGkgKyAnXSA9PT0gJyArIGU7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FycmF5cyBkaWZmZXI6ICcgKyBhY3R1YWxTdHIgKyAnLCAnICsgZXhwZWN0ZWRTdHIpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tQXJyYXlJblJhbmdlKFxuICAgIG46IG51bWJlciwgbWluVmFsdWU6IG51bWJlciwgbWF4VmFsdWU6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHYgPSBuZXcgRmxvYXQzMkFycmF5KG4pO1xuICBjb25zdCByYW5nZSA9IG1heFZhbHVlIC0gbWluVmFsdWU7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgdltpXSA9IChNYXRoLnJhbmRvbSgpICogcmFuZ2UpICsgbWluVmFsdWU7XG4gIH1cbiAgcmV0dXJuIHY7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYWtlSWRlbnRpdHkobjogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgaSA9IG5ldyBGbG9hdDMyQXJyYXkobiAqIG4pO1xuICBmb3IgKGxldCBqID0gMDsgaiA8IG47ICsraikge1xuICAgIGlbKGogKiBuKSArIGpdID0gMTtcbiAgfVxuICByZXR1cm4gaTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNldFZhbHVlKFxuICAgIG06IEZsb2F0MzJBcnJheSwgbU51bVJvd3M6IG51bWJlciwgbU51bUNvbHM6IG51bWJlciwgdjogbnVtYmVyLCByb3c6IG51bWJlcixcbiAgICBjb2x1bW46IG51bWJlcikge1xuICBpZiAocm93ID49IG1OdW1Sb3dzKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdyb3cgKCcgKyByb3cgKyAnKSBtdXN0IGJlIGluIFswICcgKyBtTnVtUm93cyArICddLicpO1xuICB9XG4gIGlmIChjb2x1bW4gPj0gbU51bUNvbHMpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NvbHVtbiAoJyArIGNvbHVtbiArICcpIG11c3QgYmUgaW4gWzAgJyArIG1OdW1Db2xzICsgJ10uJyk7XG4gIH1cbiAgbVsocm93ICogbU51bUNvbHMpICsgY29sdW1uXSA9IHY7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcHVNdWx0aXBseU1hdHJpeChcbiAgICBhOiBGbG9hdDMyQXJyYXksIGFSb3c6IG51bWJlciwgYUNvbDogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJSb3c6IG51bWJlcixcbiAgICBiQ29sOiBudW1iZXIpIHtcbiAgY29uc3QgcmVzdWx0ID0gbmV3IEZsb2F0MzJBcnJheShhUm93ICogYkNvbCk7XG4gIGZvciAobGV0IHIgPSAwOyByIDwgYVJvdzsgKytyKSB7XG4gICAgZm9yIChsZXQgYyA9IDA7IGMgPCBiQ29sOyArK2MpIHtcbiAgICAgIGxldCBkID0gMDtcbiAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgYUNvbDsgKytrKSB7XG4gICAgICAgIGQgKz0gYVsociAqIGFDb2wpICsga10gKiBiWyhrICogYkNvbCkgKyBjXTtcbiAgICAgIH1cbiAgICAgIHJlc3VsdFsociAqIGJDb2wpICsgY10gPSBkO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3B1RG90UHJvZHVjdChhOiBGbG9hdDMyQXJyYXksIGI6IEZsb2F0MzJBcnJheSk6IG51bWJlciB7XG4gIGlmIChhLmxlbmd0aCAhPT0gYi5sZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2NwdURvdFByb2R1Y3Q6IGluY29tcGF0aWJsZSB2ZWN0b3JzLicpO1xuICB9XG4gIGxldCBkID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhLmxlbmd0aDsgKytpKSB7XG4gICAgZCArPSBhW2ldICogYltpXTtcbiAgfVxuICByZXR1cm4gZDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IHR5cGUgVmVjdG9yID0gbnVtYmVyW10gfCBGbG9hdDY0QXJyYXkgfCBGbG9hdDMyQXJyYXkgfCBJbnQzMkFycmF5IHxcbiAgICBJbnQ4QXJyYXkgfCBJbnQxNkFycmF5O1xuXG4vKiogU2h1ZmZsZXMgdGhlIGFycmF5IHVzaW5nIEZpc2hlci1ZYXRlcyBhbGdvcml0aG0uICovXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gc2h1ZmZsZShhcnJheTogYW55W118VWludDMyQXJyYXl8SW50MzJBcnJheXxcbiAgICAgICAgICAgICAgICAgICAgICAgIEZsb2F0MzJBcnJheSk6IHZvaWQge1xuICBsZXQgY291bnRlciA9IGFycmF5Lmxlbmd0aDtcbiAgbGV0IHRlbXAgPSAwO1xuICBsZXQgaW5kZXggPSAwO1xuICAvLyBXaGlsZSB0aGVyZSBhcmUgZWxlbWVudHMgaW4gdGhlIGFycmF5XG4gIHdoaWxlIChjb3VudGVyID4gMCkge1xuICAgIC8vIFBpY2sgYSByYW5kb20gaW5kZXhcbiAgICBpbmRleCA9IChNYXRoLnJhbmRvbSgpICogY291bnRlcikgfCAwO1xuICAgIC8vIERlY3JlYXNlIGNvdW50ZXIgYnkgMVxuICAgIGNvdW50ZXItLTtcbiAgICAvLyBBbmQgc3dhcCB0aGUgbGFzdCBlbGVtZW50IHdpdGggaXRcbiAgICB0ZW1wID0gYXJyYXlbY291bnRlcl07XG4gICAgYXJyYXlbY291bnRlcl0gPSBhcnJheVtpbmRleF07XG4gICAgYXJyYXlbaW5kZXhdID0gdGVtcDtcbiAgfVxufVxuXG4vKiogQ2xhbXBzIGEgdmFsdWUgdG8gYSBzcGVjaWZpZWQgcmFuZ2UuICovXG5leHBvcnQgZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIHg6IG51bWJlciwgbWF4OiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbih4LCBtYXgpKTtcbn1cblxuLyoqIFJldHVybnMgYSBzYW1wbGUgZnJvbSBhIHVuaWZvcm0gW2EsIGJdIGRpc3RyaWJ1dGlvbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kVW5pZm9ybShhOiBudW1iZXIsIGI6IG51bWJlcikge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKSAqIChiIC0gYSkgKyBhO1xufVxuXG4vKipcbiAqIFNhbXBsZXMgZnJvbSBhIGdhdXNzaWFuIGRpc3RyaWJ1dGlvbi5cbiAqXG4gKiBAcGFyYW0gbWVhbiBUaGUgbWVhbi4gRGVmYXVsdCBpcyAwLlxuICogQHBhcmFtIHN0ZERldiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uLiBEZWZhdWx0IGlzIDEuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kR2F1c3MobWVhbiA9IDAsIHN0ZERldiA9IDEsIHRydW5jYXRlZCA9IGZhbHNlKTogbnVtYmVyIHtcbiAgbGV0IHYxOiBudW1iZXIsIHYyOiBudW1iZXIsIHM6IG51bWJlcjtcbiAgZG8ge1xuICAgIHYxID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHYyID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHMgPSB2MSAqIHYxICsgdjIgKiB2MjtcbiAgfSB3aGlsZSAocyA+IDEpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IE1hdGguc3FydCgtMiAqIE1hdGgubG9nKHMpIC8gcykgKiB2MTtcbiAgaWYgKHRydW5jYXRlZCAmJiByZXN1bHQgPiAyKSB7XG4gICAgcmV0dXJuIHJhbmRHYXVzcyhtZWFuLCBzdGREZXYsIHRydWUpO1xuICB9XG4gIHJldHVybiBtZWFuICsgc3RkRGV2ICogcmVzdWx0O1xufVxuXG4vKiogUmV0dXJucyBzcXVhcmVkIGV1Y2xlZGlhbiBkaXN0YW5jZSBiZXR3ZWVuIHR3byB2ZWN0b3JzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3RTcXVhcmVkKGE6IFZlY3RvciwgYjogVmVjdG9yKTogbnVtYmVyIHtcbiAgbGV0IHJlc3VsdCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYS5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGRpZmYgPSBhW2ldIC0gYltpXTtcbiAgICByZXN1bHQgKz0gZGlmZiAqIGRpZmY7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydChleHByOiBib29sZWFuLCBtc2c6IHN0cmluZykge1xuICBpZiAoIWV4cHIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgc2hhcGVBOiBudW1iZXJbXSwgc2hhcGVCOiBudW1iZXJbXSwgZXJyb3JNZXNzYWdlUHJlZml4ID0gJycpOiB2b2lkIHtcbiAgYXNzZXJ0KFxuICAgICAgYXJyYXlzRXF1YWwoc2hhcGVBLCBzaGFwZUIpLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgYFNoYXBlcyAke3NoYXBlQX0gYW5kICR7c2hhcGVCfSBtdXN0IG1hdGNoYCk7XG59XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKGFycjogYW55W10sIHJldD86IG51bWJlcltdKTogbnVtYmVyW10ge1xuICByZXQgPSAocmV0ID09PSB1bmRlZmluZWQgPyBbXSA6IHJldCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgKytpKSB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoYXJyW2ldKSkge1xuICAgICAgZmxhdHRlbihhcnJbaV0sIHJldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldC5wdXNoKGFycltpXSk7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQ7XG59XG5cbmV4cG9ydCB0eXBlIEFycmF5RGF0YSA9IG51bWJlcnxudW1iZXJbXXxudW1iZXJbXVtdfG51bWJlcltdW11bXXxudW1iZXJbXVtdW11bXTtcblxuZXhwb3J0IGZ1bmN0aW9uIGluZmVyU2hhcGUoYXJyOiBBcnJheURhdGEpOiBudW1iZXJbXSB7XG4gIGNvbnN0IHNoYXBlOiBudW1iZXJbXSA9IFtdO1xuICB3aGlsZSAoYXJyIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICBzaGFwZS5wdXNoKGFyci5sZW5ndGgpO1xuICAgIGFyciA9IGFyclswXTtcbiAgfVxuICByZXR1cm4gc2hhcGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaXplRnJvbVNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IG51bWJlciB7XG4gIGlmIChzaGFwZS5sZW5ndGggPT09IDApIHtcbiAgICAvLyBTY2FsYXIuXG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgbGV0IHNpemUgPSBzaGFwZVswXTtcbiAgZm9yIChsZXQgaSA9IDE7IGkgPCBzaGFwZS5sZW5ndGg7IGkrKykge1xuICAgIHNpemUgKj0gc2hhcGVbaV07XG4gIH1cbiAgcmV0dXJuIHNpemU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1NjYWxhclNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IGJvb2xlYW4ge1xuICByZXR1cm4gc2hhcGUubGVuZ3RoID09PSAwO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gYXJyYXlzRXF1YWwobjE6IGFueVtdfEZsb2F0MzJBcnJheSwgbjI6IGFueVtdfEZsb2F0MzJBcnJheSkge1xuICBpZiAobjEubGVuZ3RoICE9PSBuMi5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuMS5sZW5ndGg7IGkrKykge1xuICAgIGlmIChuMVtpXSAhPT0gbjJbaV0pIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0ludChhOiBudW1iZXIpOiBib29sZWFuIHtcbiAgcmV0dXJuIGEgJSAxID09PSAwO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdGFuaCh4OiBudW1iZXIpOiBudW1iZXIge1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIGlmICgoTWF0aCBhcyBhbnkpLnRhbmggIT0gbnVsbCkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKE1hdGggYXMgYW55KS50YW5oKHgpO1xuICB9XG4gIGlmICh4ID09PSBJbmZpbml0eSkge1xuICAgIHJldHVybiAxO1xuICB9IGVsc2UgaWYgKHggPT09IC1JbmZpbml0eSkge1xuICAgIHJldHVybiAtMTtcbiAgfSBlbHNlIHtcbiAgICBjb25zdCBlMnggPSBNYXRoLmV4cCgyICogeCk7XG4gICAgcmV0dXJuIChlMnggLSAxKSAvIChlMnggKyAxKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2l6ZVRvU3F1YXJpc2hTaGFwZShzaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgZm9yIChsZXQgYSA9IE1hdGguZmxvb3IoTWF0aC5zcXJ0KHNpemUpKTsgYSA+IDE7IC0tYSkge1xuICAgIGlmIChzaXplICUgYSA9PT0gMCkge1xuICAgICAgcmV0dXJuIFthLCBzaXplIC8gYV07XG4gICAgfVxuICB9XG4gIHJldHVybiBbMSwgc2l6ZV07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTaHVmZmxlZEluZGljZXMobjogbnVtYmVyKTogVWludDMyQXJyYXkge1xuICBjb25zdCBzaHVmZmxlZEluZGljZXMgPSBuZXcgVWludDMyQXJyYXkobik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgc2h1ZmZsZWRJbmRpY2VzW2ldID0gaTtcbiAgfVxuICBzaHVmZmxlKHNodWZmbGVkSW5kaWNlcyk7XG4gIHJldHVybiBzaHVmZmxlZEluZGljZXM7XG59XG4iXX0= diff --git a/demos/benchmarks/conv_gpu_benchmark.ts b/demos/benchmarks/conv_gpu_benchmark.ts new file mode 100644 index 0000000000..fffd644e2c --- /dev/null +++ b/demos/benchmarks/conv_gpu_benchmark.ts @@ -0,0 +1,90 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../../src/math/conv_util'; +import * as conv_gpu from '../../src/math/webgl/conv_gpu'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 40; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const inputShapeRCD: [number, number, number] = [size, size, 1]; + const outputDepth = 1; + const fieldSize = 11; + const stride = 1; + const zeroPad = conv_util.computeDefaultPad(inputShapeRCD, fieldSize, stride); + const outputShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + inputShapeRCD, fieldSize, outputDepth, stride, zeroPad); + + const inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + const outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + const weightsTexShapeRC = conv_util.computeWeightsTexShape( + inputShapeRCD[2], outputDepth, fieldSize); + const biasesTexShapeRC = conv_util.computeBiasesTexShape(outputDepth); + + const hasBias = true; + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram(conv_gpu.getFragmentShaderSource( + inputShapeRCD, outputDepth, fieldSize, stride, zeroPad, hasBias)); + + const inputTexture = + gpgpu.createMatrixTexture(inputTexShapeRC[0], inputTexShapeRC[1]); + const weightsTexture = + gpgpu.createMatrixTexture(weightsTexShapeRC[0], weightsTexShapeRC[1]); + const biasesTexture = + gpgpu.createMatrixTexture(biasesTexShapeRC[0], biasesTexShapeRC[1]); + const outputTexture = + gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + + const inputData = test_util.randomArrayInRange( + inputTexShapeRC[0] * inputTexShapeRC[1], -1, 1); + const weightsData = test_util.randomArrayInRange( + weightsTexShapeRC[0] * weightsTexShapeRC[1], -1, 1); + const biasesData = test_util.randomArrayInRange( + biasesTexShapeRC[0] * biasesTexShapeRC[1], -1, 1); + + gpgpu.uploadMatrixToTexture( + inputTexture, inputTexShapeRC[0], inputTexShapeRC[1], inputData); + gpgpu.uploadMatrixToTexture( + weightsTexture, weightsTexShapeRC[0], weightsTexShapeRC[1], weightsData); + gpgpu.uploadMatrixToTexture( + biasesTexture, biasesTexShapeRC[0], biasesTexShapeRC[1], biasesData); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + conv_gpu.convolve( + gpgpu, program, inputTexture, weightsTexture, biasesTexture, + outputTexture, outputTexShapeRC); + } + + gpgpu.downloadMatrixFromTexture( + outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + const end = performance.now(); + + const avgTime = (end - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(inputTexture); + gpgpu.deleteMatrixTexture(weightsTexture); + gpgpu.deleteMatrixTexture(biasesTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; diff --git a/demos/benchmarks/conv_transpose_gpu_benchmark.ts b/demos/benchmarks/conv_transpose_gpu_benchmark.ts new file mode 100644 index 0000000000..dcc26f67e8 --- /dev/null +++ b/demos/benchmarks/conv_transpose_gpu_benchmark.ts @@ -0,0 +1,85 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../../src/math/conv_util'; +import * as conv_backprop_gpu from '../../src/math/webgl/conv_backprop_gpu'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 100; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const xShapeRCD: [number, number, number] = [size, size, 1]; + const origOutputDepth = 2; + const fieldSize = 11; + const origStride = 1; + const origPad = 1; + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + const origInputDepth = xShapeRCD[2]; + const src = conv_backprop_gpu.getFragmentShaderConvTransposeSource( + xShapeRCD, fieldSize, origInputDepth, origStride, origPad, false); + const program = gpgpu.createProgram(src); + + // Upload x. + const xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + const xData = + test_util.randomArrayInRange(xTexShapeRC[0] * xTexShapeRC[1], -1, 1); + gpgpu.uploadMatrixToTexture(xTex, xTexShapeRC[0], xTexShapeRC[1], xData); + + // Upload weights. + const wTexShapeRC = conv_util.computeWeightsTexShape( + origInputDepth, origOutputDepth, fieldSize); + const wData = + test_util.randomArrayInRange(wTexShapeRC[0] * wTexShapeRC[1], -1, 1); + const wTex = gpgpu.createMatrixTexture(wTexShapeRC[0], wTexShapeRC[1]); + gpgpu.uploadMatrixToTexture(wTex, wTexShapeRC[0], wTexShapeRC[1], wData); + + // Figure out the output shape by dilating the input. + const dilatedRC = + conv_util.computeDilatedRC([xShapeRCD[0], xShapeRCD[1]], origStride); + const pad = fieldSize - 1 - origPad; + const resultShapeRCD = conv_util.computeOutputShape3D( + [dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, + 1, pad); + + const resultTexRC = conv_util.computeTexShapeFrom3D(resultShapeRCD); + const resultTex = gpgpu.createMatrixTexture(resultTexRC[0], resultTexRC[1]); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + conv_backprop_gpu.convTranspose( + gpgpu, program, xTex, wTex, null, resultTex, resultTexRC); + } + + const y = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexRC[0], resultTexRC[1]); + + const end = performance.now(); + + const avgTime = (end - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteMatrixTexture(wTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; diff --git a/demos/benchmarks/logsumexp_cpu_benchmark.ts b/demos/benchmarks/logsumexp_cpu_benchmark.ts new file mode 100644 index 0000000000..2d398d0b6b --- /dev/null +++ b/demos/benchmarks/logsumexp_cpu_benchmark.ts @@ -0,0 +1,32 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMathCPU} from '../../src/math/math_cpu'; +import {Array2D, NDArray} from '../../src/math/ndarray'; + +import {BenchmarkTest} from './benchmark'; + +const OPS_PER_RUN = 10; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const math = new NDArrayMathCPU(); + const a = NDArray.randUniform([size, size], -1, 1); + const start = performance.now(); + for (let i = 0; i < OPS_PER_RUN; i++) { + math.logSumExp(a); + } + const end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; diff --git a/demos/benchmarks/logsumexp_gpu_benchmark.ts b/demos/benchmarks/logsumexp_gpu_benchmark.ts new file mode 100644 index 0000000000..03f270359b --- /dev/null +++ b/demos/benchmarks/logsumexp_gpu_benchmark.ts @@ -0,0 +1,51 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as logsumexp_gpu from '../../src/math/webgl/logsumexp_gpu'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 100; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const gpgpu = new GPGPUContext(); + + const program = + gpgpu.createProgram(logsumexp_gpu.getFragmentShaderSource(size, size)); + + const aTexture = gpgpu.createMatrixTexture(size, size); + const resultTexture = gpgpu.createMatrixTexture(size, size); + + const a = test_util.randomArrayInRange(size * size, -1, 1); + gpgpu.uploadMatrixToTexture(aTexture, size, size, a); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + logsumexp_gpu.logSumExp( + gpgpu, program, aTexture, size, size, resultTexture); + } + + gpgpu.downloadMatrixFromTexture(resultTexture, size, size); + const avgTime = (performance.now() - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; diff --git a/demos/benchmarks/math-benchmark-run-groups.ts b/demos/benchmarks/math-benchmark-run-groups.ts new file mode 100644 index 0000000000..f616e055bd --- /dev/null +++ b/demos/benchmarks/math-benchmark-run-groups.ts @@ -0,0 +1,79 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {BenchmarkRun, BenchmarkRunGroup} from './benchmark'; +import * as conv_gpu_benchmark from './conv_gpu_benchmark'; +import * as conv_transpose_gpu_benchmark from './conv_transpose_gpu_benchmark'; +import * as logsumexp_cpu_benchmark from './logsumexp_cpu_benchmark'; +import * as logsumexp_gpu_benchmark from './logsumexp_gpu_benchmark'; +import * as max_pool_backprop_gpu_benchmark from './max_pool_backprop_gpu_benchmark'; +import * as max_pool_gpu_benchmark from './max_pool_gpu_benchmark'; +import * as mulmat_cpu_benchmark from './mulmat_cpu_benchmark'; +import * as mulmat_gpu_benchmark from './mulmat_gpu_benchmark'; +import * as tex_util_benchmark from './tex_util_benchmark'; + +export const BENCHMARK_RUN_GROUPS: BenchmarkRunGroup[] = [ + { + name: + 'Matrix Multiplication (CPU vs GPU): matmul([size, size], [size, size])', + min: 0, + max: 1024, + stepSize: 64, + stepToSizeTransformation: (step: number) => Math.max(1, step), + benchmarkRuns: [ + new BenchmarkRun('mulmat_gpu', mulmat_gpu_benchmark.BENCHMARK_TEST), + new BenchmarkRun('mulmat_cpu', mulmat_cpu_benchmark.BENCHMARK_TEST) + ], + }, + { + name: 'Convolution (GPU): conv over image [size, size, 1]', + min: 0, + max: 1024, + stepSize: 64, + stepToSizeTransformation: (step: number) => Math.max(1, step), + benchmarkRuns: [new BenchmarkRun( + 'd1=1, d2=1, f=11, s=1', conv_gpu_benchmark.BENCHMARK_TEST)], + }, + { + name: 'Convolution Transposed (GPU): deconv over image [size, size, 1]', + min: 0, + max: 1024, + stepSize: 64, + stepToSizeTransformation: (step: number) => Math.max(1, step), + benchmarkRuns: [new BenchmarkRun( + 'd1=1, d2=1, f=11, s=1', conv_transpose_gpu_benchmark.BENCHMARK_TEST)], + }, + { + name: 'Max pool (GPU)', + min: 0, + max: 1024, + stepSize: 64, + stepToSizeTransformation: (step: number) => Math.max(1, step), + benchmarkRuns: [new BenchmarkRun( + 'd1=1, d2=1, f=11, s=1', + max_pool_gpu_benchmark.MAX_POOL_BENCHMARK_TEST)], + }, + { + name: 'LogSumExp (CPU vs GPU): input [size, size]', + min: 0, + max: 1024, + stepSize: 64, + stepToSizeTransformation: (step: number) => Math.max(1, step), + benchmarkRuns: [ + new BenchmarkRun('logsumexp_gpu', logsumexp_gpu_benchmark.BENCHMARK_TEST), + new BenchmarkRun('logsumexp_cpu', logsumexp_cpu_benchmark.BENCHMARK_TEST) + ], + } +]; diff --git a/demos/benchmarks/math-benchmark.html b/demos/benchmarks/math-benchmark.html new file mode 100644 index 0000000000..500bff9514 --- /dev/null +++ b/demos/benchmarks/math-benchmark.html @@ -0,0 +1,95 @@ + + + + + + + + + diff --git a/demos/benchmarks/math-benchmark.ts b/demos/benchmarks/math-benchmark.ts new file mode 100644 index 0000000000..6b4bd2533f --- /dev/null +++ b/demos/benchmarks/math-benchmark.ts @@ -0,0 +1,225 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import '../demo-header'; +import '../demo-footer'; + +// tslint:disable-next-line:no-unused-variable +import {PolymerElement, PolymerHTMLElement} from '../polymer-spec'; +import {BenchmarkRunGroup} from './benchmark'; + +import {BENCHMARK_RUN_GROUPS} from './math-benchmark-run-groups'; + +// tslint:disable-next-line:variable-name +export let MathBenchmarkPolymer = PolymerElement( + {is: 'math-benchmark', properties: {benchmarkRunGroupNames: Array}}); + +export class MathBenchmark extends MathBenchmarkPolymer { + // Polymer properties. + private benchmarkRunGroupNames: string[]; + private stopMessages: boolean[]; + + ready() { + // Set up the benchmarks UI. + const benchmarkRunGroupNames: string[] = []; + this.stopMessages = []; + for (let i = 0; i < BENCHMARK_RUN_GROUPS.length; i++) { + benchmarkRunGroupNames.push(BENCHMARK_RUN_GROUPS[i].name); + this.stopMessages.push(false); + } + this.benchmarkRunGroupNames = benchmarkRunGroupNames; + + // In a setTimeout to let the UI update before we add event listeners. + setTimeout(() => { + const runButtons = this.querySelectorAll('.run-test'); + const stopButtons = this.querySelectorAll('.run-stop'); + for (let i = 0; i < runButtons.length; i++) { + runButtons[i].addEventListener('click', () => { + this.runBenchmarkGroup(i); + }); + stopButtons[i].addEventListener('click', () => { + this.stopMessages[i] = true; + }); + } + }, 0); + } + + private runBenchmarkGroup(benchmarkRunGroupIndex: number) { + const benchmarkRunGroup = BENCHMARK_RUN_GROUPS[benchmarkRunGroupIndex]; + + const canvas = this.querySelectorAll('.run-plot')[benchmarkRunGroupIndex] as + HTMLCanvasElement; + const context = canvas.getContext('2d') as CanvasRenderingContext2D; + + const datasets: ChartDataSets[] = []; + for (let i = 0; i < benchmarkRunGroup.benchmarkRuns.length; i++) { + const hue = Math.floor(360 * i / benchmarkRunGroup.benchmarkRuns.length); + datasets.push({ + data: benchmarkRunGroup.benchmarkRuns[i].chartData, + fill: false, + label: benchmarkRunGroup.benchmarkRuns[i].name, + borderColor: 'hsl(' + hue + ', 100%, 40%)', + backgroundColor: 'hsl(' + hue + ', 100%, 70%)', + pointRadius: 0, + pointHitRadius: 5, + borderWidth: 1, + lineTension: 0 + }); + } + + const chart = new Chart(context, { + type: 'line', + data: {datasets}, + options: { + animation: {duration: 0}, + responsive: false, + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom', + ticks: { + min: benchmarkRunGroup.min, + max: benchmarkRunGroup.max, + stepSize: benchmarkRunGroup.stepSize, + callback: (label: string) => { + return benchmarkRunGroup.stepToSizeTransformation != null ? + benchmarkRunGroup.stepToSizeTransformation(+label) : + +label; + } + // tslint:disable-next-line:no-any + } as any // Note: the typings for this are incorrect, cast as any. + }], + yAxes: [{ + ticks: { + callback: (label, index, labels) => { + return label + 'ms'; + } + }, + }] + }, + tooltips: {mode: 'label'}, + title: {text: benchmarkRunGroup.name} + } + }); + canvas.style.display = 'none'; + + const runMessage = + this.querySelectorAll('.run-message')[benchmarkRunGroupIndex] as + HTMLElement; + runMessage.style.display = 'block'; + + const runNumbersTable = + this.querySelectorAll('.run-numbers-table')[benchmarkRunGroupIndex] as + HTMLElement; + runNumbersTable.innerHTML = ''; + runNumbersTable.style.display = 'none'; + + // Set up the header for the table. + const headers = ['size']; + for (let i = 0; i < benchmarkRunGroup.benchmarkRuns.length; i++) { + headers.push(benchmarkRunGroup.benchmarkRuns[i].name); + } + runNumbersTable.appendChild(this.buildRunNumbersRow(headers)); + + this.runBenchmarkSteps( + chart, benchmarkRunGroup, benchmarkRunGroupIndex, + benchmarkRunGroup.min); + } + + private buildRunNumbersRow(values: string[]) { + const runNumberRowElement = document.createElement('div'); + runNumberRowElement.className = 'run-numbers-row math-benchmark'; + + for (let i = 0; i < values.length; i++) { + const runNumberCellElement = document.createElement('div'); + runNumberCellElement.className = 'run-numbers-cell math-benchmark'; + runNumberCellElement.innerText = values[i]; + runNumberRowElement.appendChild(runNumberCellElement); + } + return runNumberRowElement; + } + + private runBenchmarkSteps( + chart: Chart, benchmarkRunGroup: BenchmarkRunGroup, + benchmarkRunGroupIndex: number, step: number) { + const runNumbersTable = + this.querySelectorAll('.run-numbers-table')[benchmarkRunGroupIndex] as + HTMLElement; + if (step > benchmarkRunGroup.max || + this.stopMessages[benchmarkRunGroupIndex]) { + this.stopMessages[benchmarkRunGroupIndex] = false; + + runNumbersTable.style.display = ''; + + const canvas = + this.querySelectorAll('.run-plot')[benchmarkRunGroupIndex] as + HTMLCanvasElement; + canvas.style.display = 'block'; + chart.update(); + + const runMessage = + this.querySelectorAll('.run-message')[benchmarkRunGroupIndex] as + HTMLElement; + runMessage.style.display = 'none'; + + return; + } + + const runNumberRowElement = document.createElement('div'); + runNumberRowElement.className = 'run-numbers-row math-benchmark'; + + const rowValues: string[] = ['' + step]; + for (let i = 0; i < benchmarkRunGroup.benchmarkRuns.length; i++) { + const benchmarkRun = benchmarkRunGroup.benchmarkRuns[i]; + const benchmarkTest = benchmarkRun.benchmarkTest; + + const size = benchmarkRunGroup.stepToSizeTransformation != null ? + benchmarkRunGroup.stepToSizeTransformation(step) : + step; + + let resultString: string; + let logString: string; + let time = 0; + let success = true; + + try { + time = benchmarkTest(size); + resultString = time.toFixed(3) + 'ms'; + logString = resultString; + } catch (e) { + success = false; + resultString = 'Error'; + logString = e.message; + } + + if (time >= 0) { + if (success) { + benchmarkRun.chartData.push({x: step, y: time}); + } + rowValues.push(resultString); + } + console.log(benchmarkRun.name + '[' + step + ']: ' + logString); + } + runNumbersTable.appendChild(this.buildRunNumbersRow(rowValues)); + + step += benchmarkRunGroup.stepSize; + // Allow the UI to update. + setTimeout( + () => this.runBenchmarkSteps( + chart, benchmarkRunGroup, benchmarkRunGroupIndex, step), + 100); + } +} +document.registerElement(MathBenchmark.prototype.is, MathBenchmark); diff --git a/demos/benchmarks/max_pool_backprop_gpu_benchmark.ts b/demos/benchmarks/max_pool_backprop_gpu_benchmark.ts new file mode 100644 index 0000000000..7dab36b3d0 --- /dev/null +++ b/demos/benchmarks/max_pool_backprop_gpu_benchmark.ts @@ -0,0 +1,82 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../../src/math/conv_util'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as max_pool_backprop_gpu from '../../src/math/webgl/max_pool_backprop_gpu'; +import * as test_util from '../../src/test_util'; +import * as util from '../../src/util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 100; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const dyShapeRCD: [number, number, number] = [size, size, 1]; + const outputDepth = 1; + const fieldSize = 11; + const stride = 1; + const zeroPad = conv_util.computeDefaultPad(dyShapeRCD, fieldSize, stride); + const outputShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + dyShapeRCD, fieldSize, outputDepth, stride, zeroPad); + + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + const outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram( + max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop( + dyShapeRCD, fieldSize, stride, zeroPad)); + + const dyTexture = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + const maxPositionsTexture = + gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + const outputTexture = + gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + + const dyData = + test_util.randomArrayInRange(dyTexShapeRC[0] * dyTexShapeRC[1], -1, 1); + const maxPositionsData = new Float32Array(util.sizeFromShape(dyShapeRCD)); + for (let i = 0; i < maxPositionsData.length; i++) { + maxPositionsData[i] = Math.floor(Math.random() * fieldSize * fieldSize); + } + + gpgpu.uploadMatrixToTexture( + dyTexture, dyTexShapeRC[0], dyTexShapeRC[1], dyData); + gpgpu.uploadMatrixToTexture( + maxPositionsTexture, dyTexShapeRC[0], dyTexShapeRC[1], maxPositionsData); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + max_pool_backprop_gpu.maxPoolBackprop( + gpgpu, program, dyTexture, maxPositionsTexture, outputTexture, + outputTexShapeRC); + } + + gpgpu.downloadMatrixFromTexture( + outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + const end = performance.now(); + + const avgTime = (end - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(dyTexture); + gpgpu.deleteMatrixTexture(maxPositionsTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; \ No newline at end of file diff --git a/demos/benchmarks/max_pool_gpu_benchmark.ts b/demos/benchmarks/max_pool_gpu_benchmark.ts new file mode 100644 index 0000000000..bb1e6a6e24 --- /dev/null +++ b/demos/benchmarks/max_pool_gpu_benchmark.ts @@ -0,0 +1,121 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../../src/math/conv_util'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as max_pool_gpu from '../../src/math/webgl/max_pool_gpu'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 40; + +export const MAX_POOL_BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const inputShapeRCD: [number, number, number] = [size, size, 1]; + const outputDepth = 1; + const fieldSize = 11; + const stride = 1; + const zeroPad = conv_util.computeDefaultPad(inputShapeRCD, fieldSize, stride); + const outputShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + inputShapeRCD, fieldSize, outputDepth, stride, zeroPad); + + const inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + const outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + + const gpgpu = new GPGPUContext(); + const program = + gpgpu.createProgram(max_pool_gpu.getFragmentShaderMaxPoolSource( + inputShapeRCD, fieldSize, stride, zeroPad)); + + const inputTexture = + gpgpu.createMatrixTexture(inputTexShapeRC[0], inputTexShapeRC[1]); + const outputTexture = + gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + + const inputData = test_util.randomArrayInRange( + inputTexShapeRC[0] * inputTexShapeRC[1], -1, 1); + + gpgpu.uploadMatrixToTexture( + inputTexture, inputTexShapeRC[0], inputTexShapeRC[1], inputData); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + max_pool_gpu.maxPoolCommon( + gpgpu, program, inputTexture, outputTexture, outputTexShapeRC); + } + + gpgpu.downloadMatrixFromTexture( + outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + const end = performance.now(); + + const avgTime = (end - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(inputTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; + +export const MAX_POOL_POSNS_BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const inputShapeRCD: [number, number, number] = [size, size, 1]; + const outputDepth = 1; + const fieldSize = 11; + const stride = 1; + const zeroPad = conv_util.computeDefaultPad(inputShapeRCD, fieldSize, stride); + const outputShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + inputShapeRCD, fieldSize, outputDepth, stride, zeroPad); + + const inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + const outputTexShapeRC = conv_util.computeTexShapeFrom3D(outputShapeRCD); + + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = + gpgpu.createProgram(max_pool_gpu.getFragmentShaderMaxPoolPositionsSource( + inputShapeRCD, fieldSize, stride, zeroPad)); + + const inputTexture = + gpgpu.createMatrixTexture(inputTexShapeRC[0], inputTexShapeRC[1]); + const outputTexture = + gpgpu.createMatrixTexture(outputTexShapeRC[0], outputTexShapeRC[1]); + + const inputData = test_util.randomArrayInRange( + inputTexShapeRC[0] * inputTexShapeRC[1], -1, 1); + + gpgpu.uploadMatrixToTexture( + inputTexture, inputTexShapeRC[0], inputTexShapeRC[1], inputData); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + max_pool_gpu.maxPoolCommon( + gpgpu, program, inputTexture, outputTexture, outputTexShapeRC); + } + + gpgpu.downloadMatrixFromTexture( + outputTexture, outputTexShapeRC[0], outputTexShapeRC[1]); + const end = performance.now(); + + const avgTime = (end - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(inputTexture); + gpgpu.deleteMatrixTexture(outputTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; \ No newline at end of file diff --git a/demos/benchmarks/mulmat_cpu_benchmark.ts b/demos/benchmarks/mulmat_cpu_benchmark.ts new file mode 100644 index 0000000000..1e6ab8ea90 --- /dev/null +++ b/demos/benchmarks/mulmat_cpu_benchmark.ts @@ -0,0 +1,37 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMathCPU} from '../../src/math/math_cpu'; +import {Array2D, NDArray} from '../../src/math/ndarray'; + +import {BenchmarkTest} from './benchmark'; + +const OPS_PER_SMALL_RUN = 1; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + if (size > 512) { + return -1; + } + const math = new NDArrayMathCPU(); + const a = NDArray.randUniform([size, size], -1, 1); + const b = NDArray.randUniform([size, size], -1, 1); + const runs = (size < 192) ? OPS_PER_SMALL_RUN : 1; + const start = performance.now(); + for (let i = 0; i < runs; i++) { + math.matMul(a, b); + } + const end = performance.now(); + return (end - start) / runs; +}; diff --git a/demos/benchmarks/mulmat_gpu_benchmark.ts b/demos/benchmarks/mulmat_gpu_benchmark.ts new file mode 100644 index 0000000000..3b69e9a0ba --- /dev/null +++ b/demos/benchmarks/mulmat_gpu_benchmark.ts @@ -0,0 +1,98 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MatrixOrientation} from '../../src/math/math'; +import {Array2D} from '../../src/math/ndarray'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as mulmat_gpu from '../../src/math/webgl/mulmat_gpu'; +import * as mulmat_packed_gpu from '../../src/math/webgl/mulmat_packed_gpu'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OP_RUNS = 40; + +export const BENCHMARK_TEST: BenchmarkTest = (size: number) => { + const gpgpu = new GPGPUContext(); + const aTexture = gpgpu.createMatrixTexture(size, size); + const bTexture = gpgpu.createMatrixTexture(size, size); + const resultTexture = gpgpu.createMatrixTexture(size, size); + + const aArr = new Array2D( + [size, size], {texture: aTexture, textureShapeRC: [size, size]}); + const bArr = new Array2D( + [size, size], {texture: bTexture, textureShapeRC: [size, size]}); + const resArr = new Array2D( + [size, size], {texture: resultTexture, textureShapeRC: [size, size]}); + const program = gpgpu.createProgram(mulmat_gpu.getFragmentShader( + aArr, bArr, resArr, MatrixOrientation.REGULAR, + MatrixOrientation.REGULAR)); + + const a = test_util.randomArrayInRange(size * size, -1, 1); + const b = test_util.randomArrayInRange(size * size, -1, 1); + gpgpu.uploadMatrixToTexture(aTexture, size, size, a); + gpgpu.uploadMatrixToTexture(bTexture, size, size, b); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + mulmat_gpu.multiplyMatrix( + gpgpu, program, aTexture, bTexture, resultTexture, [size, size]); + } + + const actual = gpgpu.downloadMatrixFromTexture(resultTexture, size, size); + const avgTime = (performance.now() - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; + +export const BENCHMARK_TEST_PACKED: BenchmarkTest = (size: number) => { + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = + gpgpu.createProgram(mulmat_packed_gpu.getFragmentShaderSource( + size, MatrixOrientation.REGULAR, MatrixOrientation.REGULAR)); + + const aTexture = gpgpu.createPackedMatrixTexture(size, size); + const bTexture = gpgpu.createPackedMatrixTexture(size, size); + const resultTexture = gpgpu.createPackedMatrixTexture(size, size); + + const a = test_util.randomArrayInRange(size * size, -1, 1); + const b = test_util.randomArrayInRange(size * size, -1, 1); + gpgpu.uploadMatrixToPackedTexture(aTexture, size, size, a); + gpgpu.uploadMatrixToPackedTexture(bTexture, size, size, b); + + const start = performance.now(); + for (let i = 0; i < OP_RUNS; i++) { + mulmat_packed_gpu.multiplyMatrixPacked( + gpgpu, program, aTexture, bTexture, resultTexture, [size, size]); + } + + const actual = + gpgpu.downloadMatrixFromPackedTexture(resultTexture, size, size); + const avgTime = (performance.now() - start) / OP_RUNS; + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return avgTime; +}; diff --git a/demos/benchmarks/tex_util_benchmark.ts b/demos/benchmarks/tex_util_benchmark.ts new file mode 100644 index 0000000000..4b4e684e86 --- /dev/null +++ b/demos/benchmarks/tex_util_benchmark.ts @@ -0,0 +1,80 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as gpgpu_util from '../../src/math/webgl/gpgpu_util'; +import * as tex_util from '../../src/math/webgl/tex_util'; +import * as webgl_util from '../../src/math/webgl/webgl_util'; +import * as test_util from '../../src/test_util'; + +import {BenchmarkTest} from './benchmark'; + +const OPS_PER_RUN = 100; + +export const BENCHMARK_ENCODE_UNPACKED: BenchmarkTest = (size: number) => { + const matrix = test_util.randomArrayInRange(size * size, -1, 1); + const channelsPerTexture = webgl_util.getChannelsPerTexture(); + const unpackedArray = + new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize( + matrix.length, channelsPerTexture)); + const start = performance.now(); + for (let i = 0; i < OPS_PER_RUN; ++i) { + tex_util.encodeMatrixToUnpackedArray( + matrix, unpackedArray, channelsPerTexture); + } + const end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; + +export const BENCHMARK_ENCODE_PACKED: BenchmarkTest = (size: number) => { + const matrix = test_util.randomArrayInRange(size * size, -1, 1); + const packedRGBA = new Float32Array( + tex_util.getPackedRGBAArraySizeFromMatrixShape(size, size)); + const start = performance.now(); + for (let i = 0; i < OPS_PER_RUN; ++i) { + tex_util.encodeMatrixToPackedRGBA(matrix, size, size, packedRGBA); + } + const end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; + +export const BENCHMARK_DECODE_UNPACKED: BenchmarkTest = (size: number) => { + const matrix = test_util.randomArrayInRange(size * size, -1, 1); + const channelsPerTexture = webgl_util.getChannelsPerTexture(); + const unpackedArray = + new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize( + matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray( + matrix, unpackedArray, channelsPerTexture); + const start = performance.now(); + for (let i = 0; i < OPS_PER_RUN; ++i) { + tex_util.decodeMatrixFromUnpackedArray( + unpackedArray, matrix, channelsPerTexture); + } + const end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; + +export const BENCHMARK_DECODE_PACKED: BenchmarkTest = (size: number) => { + const matrix = test_util.randomArrayInRange(size * size, -1, 1); + const packedRGBA = new Float32Array( + tex_util.getPackedRGBAArraySizeFromMatrixShape(size, size)); + tex_util.encodeMatrixToPackedRGBA(matrix, size, size, packedRGBA); + const start = performance.now(); + for (let i = 0; i < OPS_PER_RUN; ++i) { + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, size, size, matrix); + } + const end = performance.now(); + return (end - start) / OPS_PER_RUN; +}; diff --git a/demos/chartjs.d.ts b/demos/chartjs.d.ts new file mode 100644 index 0000000000..da223558c2 --- /dev/null +++ b/demos/chartjs.d.ts @@ -0,0 +1,448 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// Type definitions for Chart.js +// Project: https://github.com/nnnick/Chart.js +// Definitions by: Alberto Nuti +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/* This project is licensed under the MIT license. +Copyrights are respective of each contributor listed at the beginning of each +definition file. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ + +declare enum ChartType { line, bar, radar, doughnut, polarArea, bubble } +declare enum TimeUnit { + millisecond, + second, + minute, + hour, + day, + week, + month, + quarter, + year +} +interface ChartLegendItem { + text?: string; + fillStyle?: string; + hidden?: boolean; + lineCap?: string; + lineDash?: number[]; + lineDashOffset?: number; + lineJoin?: string; + lineWidth?: number; + strokeStyle?: string; +} +interface ChartTooltipItem { + xLabel?: string; + yLabel?: string; + datasetIndex?: number; + index?: number; +} +interface ChartTooltipCallback { + beforeTitle?: (item?: ChartTooltipItem[], data?: any) => void; + title?: (item?: ChartTooltipItem[], data?: any) => void; + afterTitle?: (item?: ChartTooltipItem[], data?: any) => void; + beforeBody?: (item?: ChartTooltipItem[], data?: any) => void; + beforeLabel?: (tooltipItem?: ChartTooltipItem, data?: any) => void; + label?: (tooltipItem?: ChartTooltipItem, data?: any) => void; + afterLabel?: (tooltipItem?: ChartTooltipItem, data?: any) => void; + afterBody?: (item?: ChartTooltipItem[], data?: any) => void; + beforeFooter?: (item?: ChartTooltipItem[], data?: any) => void; + footer?: (item?: ChartTooltipItem[], data?: any) => void; + afterfooter?: (item?: ChartTooltipItem[], data?: any) => void; +} +interface ChartAnimationParameter { + chartInstance?: any; + animationObject?: any; +} +interface ChartPoint { + x?: number; + y?: number; +} + +interface ChartConfiguration { + type?: string; + data?: ChartData; + options?: ChartOptions; +} + +interface ChartData {} + +interface LinearChartData extends ChartData { + labels?: string[]; + datasets?: ChartDataSets[]; +} + +interface ChartOptions { + responsive?: boolean; + responsiveAnimationDuration?: number; + maintainAspectRatio?: boolean; + events?: string[]; + onClick?: (any?: any) => any; + title?: ChartTitleOptions; + legend?: ChartLegendOptions; + tooltips?: ChartTooltipOptions; + hover?: ChartHoverOptions; + animation?: ChartAnimationOptions; + elements?: ChartElementsOptions; + scales?: ChartScales; +} + +interface ChartFontOptions { + defaultFontColor?: ChartColor; + defaultFontFamily?: string; + defaultFontSize?: number; + defaultFontStyle?: string; +} + +interface ChartTitleOptions { + display?: boolean; + position?: string; + fullWdith?: boolean; + fontSize?: number; + fontFamily?: string; + fontColor?: ChartColor; + fontStyle?: string; + padding?: number; + text?: string; +} + +interface ChartLegendOptions { + display?: boolean; + position?: string; + fullWidth?: boolean; + onClick?: (event: any, legendItem: any) => void; + labels?: ChartLegendLabelOptions; +} + +interface ChartLegendLabelOptions { + boxWidth?: number; + fontSize?: number; + fontStyle?: number; + fontColor?: ChartColor; + fontFamily?: string; + padding?: number; + generateLabels?: (chart: any) => any; +} + +interface ChartTooltipOptions { + enabled?: boolean; + custom?: (a: any) => void; + mode?: string; + backgroundColor?: ChartColor; + titleFontFamily?: string; + titleFontSize?: number; + titleFontStyle?: string; + titleFontColor?: ChartColor; + titleSpacing?: number; + titleMarginBottom?: number; + bodyFontFamily?: string; + bodyFontSize?: number; + bodyFontStyle?: string; + bodyFontColor?: ChartColor; + bodySpacing?: number; + footerFontFamily?: string; + footerFontSize?: number; + footerFontStyle?: string; + footerFontColor?: ChartColor; + footerSpacing?: number; + footerMarginTop?: number; + xPadding?: number; + yPadding?: number; + caretSize?: number; + cornerRadius?: number; + multiKeyBackground?: string; + callbacks?: ChartTooltipCallback; +} + +interface ChartHoverOptions { + mode?: string; + animationDuration?: number; + onHover?: (active: any) => void; +} + +interface ChartAnimationObject { + currentStep?: number; + numSteps?: number; + easing?: string; + render?: (arg: any) => void; + onAnimationProgress?: (arg: any) => void; + onAnimationComplete?: (arg: any) => void; +} + +interface ChartAnimationOptions { + duration?: number; + easing?: string; + onProgress?: (chart: any) => void; + onComplete?: (chart: any) => void; +} + +interface ChartElementsOptions { + point?: ChartPointOptions; + line?: ChartLineOptions; + arg?: ChartArcOtpions; + rectangle?: ChartRectangleOptions; +} + +interface ChartArcOtpions { + backgroundColor?: ChartColor; + borderColor?: ChartColor; + borderWidth?: number; +} + +interface ChartLineOptions { + tension?: number; + backgroundColor?: ChartColor; + borderWidth?: number; + borderColor?: ChartColor; + borderCapStyle?: string; + borderDash?: any[]; + borderDashOffset?: number; + borderJoinStyle?: string; +} + +interface ChartPointOptions { + radius?: number; + pointStyle?: string; + backgroundColor?: ChartColor; + borderWidth?: number; + borderColor?: ChartColor; + hitRadius?: number; + hoverRadius?: number; + hoverBorderWidth?: number; +} + +interface ChartRectangleOptions { + backgroundColor?: ChartColor; + borderWidth?: number; + borderColor?: ChartColor; + borderSkipped?: string; +} +interface GridLineOptions { + display?: boolean; + color?: ChartColor; + lineWidth?: number; + drawBorder?: boolean; + drawOnChartArea?: boolean; + drawticks?: boolean; + tickMarkLength?: number; + zeroLineWidth?: number; + zeroLineColor?: ChartColor; + offsetGridLines?: boolean; +} + +interface ScaleTitleOptions { + display?: boolean; + labelString?: string; + fontColor?: ChartColor; + fontFamily?: string; + fontSize?: number; + fontStyle?: string; +} + +interface TickOptions { + autoSkip?: boolean; + callback?: (value: any, index: any, values: any) => string; + display?: boolean; + fontColor?: ChartColor; + fontFamily?: string; + fontSize?: number; + fontStyle?: string; + labelOffset?: number; + maxRotation?: number; + minRotation?: number; + mirror?: boolean; + padding?: number; + reverse?: boolean; + min?: any; + max?: any; +} +interface AngleLineOptions { + display?: boolean; + color?: ChartColor; + lineWidth?: number; +} + +interface PointLabelOptions { + callback?: (arg: any) => any; + fontColor?: ChartColor; + fontFamily?: string; + fontSize?: number; + fontStyle?: string; +} + +interface TickOptions { + backdropColor?: ChartColor; + backdropPaddingX?: number; + backdropPaddingY?: number; + maxTicksLimit?: number; + showLabelBackdrop?: boolean; +} +interface LinearTickOptions extends TickOptions { + beginAtZero?: boolean; + min?: number; + max?: number; + maxTicksLimit?: number; + stepSize?: number; + suggestedMin?: number; + suggestedMax?: number; +} + +interface LogarithmicTickOptions extends TickOptions { + min?: number; + max?: number; +} + +type ChartColor = string|CanvasGradient|CanvasPattern; + +interface ChartDataSets { + backgroundColor?: ChartColor; + borderWidth?: number; + borderColor?: ChartColor; + borderCapStyle?: string; + borderDash?: number[]; + borderDashOffset?: number; + borderJoinStyle?: string; + data?: number[]|ChartPoint[]; + fill?: boolean; + label?: string; + lineTension?: number; + pointBorderColor?: ChartColor|ChartColor[]; + pointBackgroundColor?: ChartColor|ChartColor[]; + pointBorderWidth?: number|number[]; + pointRadius?: number|number[]; + pointHoverRadius?: number|number[]; + pointHitRadius?: number|number[]; + pointHoverBackgroundColor?: ChartColor|ChartColor[]; + pointHoverBorderColor?: ChartColor|ChartColor[]; + pointHoverBorderWidth?: number|number[]; + pointStyle?: string|string[]|HTMLImageElement|HTMLImageElement[]; + xAxisID?: string; + yAxisID?: string; +} + +interface ChartScales { + type?: string; + display?: boolean; + position?: string; + beforeUpdate?: (scale?: any) => void; + beforeSetDimension?: (scale?: any) => void; + beforeDataLimits?: (scale?: any) => void; + beforeBuildTicks?: (scale?: any) => void; + beforeTickToLabelConversion?: (scale?: any) => void; + beforeCalculateTickRotation?: (scale?: any) => void; + beforeFit?: (scale?: any) => void; + afterUpdate?: (scale?: any) => void; + afterSetDimension?: (scale?: any) => void; + afterDataLimits?: (scale?: any) => void; + afterBuildTicks?: (scale?: any) => void; + afterTickToLabelConversion?: (scale?: any) => void; + afterCalculateTickRotation?: (scale?: any) => void; + afterFit?: (scale?: any) => void; + gridLines?: GridLineOptions; + scaleLabel?: ScaleTitleOptions; + ticks?: TickOptions; + xAxes?: ChartXAxe[]; + yAxes?: ChartYAxe[]; +} + +interface ChartXAxe { + type?: string; + display?: boolean; + id?: string; + stacked?: boolean; + categoryPercentage?: number; + barPercentage?: number; + barThickness?: number; + gridLines?: GridLineOptions; + position?: string; + ticks?: TickOptions; + time?: TimeScale; + scaleLabel?: ScaleTitleOptions; +} + +interface ChartYAxe { + type?: string; + display?: boolean; + id?: string; + stacked?: boolean; + position?: string; + ticks?: TickOptions; + scaleLabel?: ScaleTitleOptions; +} + +interface LinearScale extends ChartScales { + ticks?: LinearTickOptions; +} + +interface LogarithmicScale extends ChartScales { + ticks?: LogarithmicTickOptions; +} + +interface TimeScale extends ChartScales { + format?: string; + displayFormats?: string; + isoWeekday?: boolean; + max?: string; + min?: string; + parser?: string|((arg: any) => any); + round?: string; + tooltipFormat?: string; + unit?: string|TimeUnit; + unitStepSize?: number; +} + +interface RadialLinearScale { + lineArc?: boolean; + angleLines?: AngleLineOptions; + pointLabels?: PointLabelOptions; + ticks?: TickOptions; +} + +declare class Chart { + constructor(context: CanvasRenderingContext2D, options: ChartConfiguration); + config: ChartConfiguration; + destroy: () => {}; + update: (duration?: any, lazy?: any) => {}; + render: (duration?: any, lazy?: any) => {}; + stop: () => {}; + resize: () => {}; + clear: () => {}; + toBase64: () => string; + generateLegend: () => {}; + getElementAtEvent: (e: any) => {}; + getElementsAtEvent: (e: any) => {}[]; + getDatasetAtEvent: (e: any) => {}[]; + + defaults: {global: ChartOptions;} +} diff --git a/demos/deeplearnjs.ts b/demos/deeplearnjs.ts new file mode 100644 index 0000000000..2162ae1663 --- /dev/null +++ b/demos/deeplearnjs.ts @@ -0,0 +1,18 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// This file is just an alias that points to the current deeplearnjs version +// at this branch, so demos can import the library as '../deeplearnjs'. +export * from '../src/index'; diff --git a/demos/demo-footer.html b/demos/demo-footer.html new file mode 100644 index 0000000000..5a58016b69 --- /dev/null +++ b/demos/demo-footer.html @@ -0,0 +1,56 @@ + + + + + + diff --git a/demos/demo-footer.ts b/demos/demo-footer.ts new file mode 100644 index 0000000000..b619e6d665 --- /dev/null +++ b/demos/demo-footer.ts @@ -0,0 +1,15 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +Polymer({is: 'demo-footer'}); diff --git a/demos/demo-header.html b/demos/demo-header.html new file mode 100644 index 0000000000..eb8464822c --- /dev/null +++ b/demos/demo-header.html @@ -0,0 +1,48 @@ + + + + + + + + + + diff --git a/demos/demo-header.ts b/demos/demo-header.ts new file mode 100644 index 0000000000..4c0ea28db3 --- /dev/null +++ b/demos/demo-header.ts @@ -0,0 +1,15 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +Polymer({is: 'demo-header'}); diff --git a/demos/font-embedding/bundle.js b/demos/font-embedding/bundle.js new file mode 100644 index 0000000000..5d0fd6a2a4 --- /dev/null +++ b/demos/font-embedding/bundle.js @@ -0,0 +1,8217 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 56443) { + return; + } + var embedding = []; + var embeddingNDArray = this.variables['input_from_feature_columns/font_embedding/weights']; + for (var i = 0; i < embeddingNDArray.shape[1]; i++) { + var num = embeddingNDArray.get(+fontId, i); + embedding.push({ init: num, val: num }); + } + this.set('embedding', embedding); + }; + FontEmbedding.prototype.sliderChange = function (e) { + var element = e.target; + var index = parseInt(element.getAttribute('data-index'), 10); + this.embedding[index].val = element.immediateValue; + this.infer(this.fontId, this.char); + }; + FontEmbedding.prototype.buildModel = function () { + this.vis = this.$.vis; + this.vis.setShape([64, 64]); + this.vis.setSize(128, 128); + this.newFontId(this.fontId); + var fc1wName = 'Stack/fully_connected_1/weights'; + var fc1bName = 'Stack/fully_connected_1/biases'; + var fc2wName = 'Stack/fully_connected_2/weights'; + var fc2bName = 'Stack/fully_connected_2/biases'; + var fc3wName = 'Stack/fully_connected_3/weights'; + var fc3bName = 'Stack/fully_connected_3/biases'; + var fc4wName = 'Stack/fully_connected_4/weights'; + var fc4bName = 'Stack/fully_connected_4/biases'; + var fc5wName = 'fully_connected/weights'; + var fc5bName = 'fully_connected/biases'; + this.graph = new learnjs_1.Graph(); + var g = this.graph; + this.inputTensor = g.placeholder('input', [102]); + var relu1 = g.layers.dense('fc1', this.inputTensor, this.variables[fc1bName].shape[1], function (x) { return g.relu(x); }, true, new learnjs_1.NDArrayInitializer(this.variables[fc1wName]), new learnjs_1.NDArrayInitializer(this.variables[fc1bName])); + var relu2 = g.layers.dense('fc2', relu1, this.variables[fc2bName].shape[1], function (x) { return g.relu(x); }, true, new learnjs_1.NDArrayInitializer(this.variables[fc2wName]), new learnjs_1.NDArrayInitializer(this.variables[fc2bName])); + var relu3 = g.layers.dense('fc3', relu2, this.variables[fc3bName].shape[1], function (x) { return g.relu(x); }, true, new learnjs_1.NDArrayInitializer(this.variables[fc3wName]), new learnjs_1.NDArrayInitializer(this.variables[fc3bName])); + var relu4 = g.layers.dense('fc4', relu3, this.variables[fc4bName].shape[1], function (x) { return g.relu(x); }, true, new learnjs_1.NDArrayInitializer(this.variables[fc4wName]), new learnjs_1.NDArrayInitializer(this.variables[fc4bName])); + this.outputTensor = g.layers.dense('fc5', relu4, this.variables[fc5bName].shape[1], function (x) { return g.sigmoid(x); }, true, new learnjs_1.NDArrayInitializer(this.variables[fc5wName]), new learnjs_1.NDArrayInitializer(this.variables[fc5bName])); + this.math = new learnjs_1.NDArrayMathGPU(); + this.session = new learnjs_1.Session(g, this.math); + }; + FontEmbedding.prototype.infer = function (fontId, char) { + var _this = this; + if (fontId.length === 0 || +fontId < 0 || +fontId > 56443 || + char.length === 0) { + return; + } + var charId = this.charIdMap[char.charAt(0)]; + if (charId == null) { + return; + } + var onehot = Array.apply(null, Array(this.numberOfValidChars)) + .map(Number.prototype.valueOf, 0); + onehot[charId] = 1; + var embedding = this.embedding.map(function (settings) { return settings.val; }); + this.math.scope(function (keep, track) { + var inputData = track(learnjs_1.Array1D.new(embedding.concat(onehot))); + var infer = _this.session.eval(_this.outputTensor, [{ tensor: _this.inputTensor, data: inputData }]); + var scalar = track(learnjs_1.Scalar.new(255)); + var scaled = _this.math.scalarTimesArray(scalar, infer); + var adjusted = _this.math.scalarMinusArray(scalar, scaled); + _this.vis.saveImageDataFromNDArray(adjusted.as3D(64, 64, 1)); + _this.vis.draw(); + }); + }; + return FontEmbedding; +}(exports.FontEmbeddingPolymer)); +exports.FontEmbedding = FontEmbedding; +document.registerElement(FontEmbedding.prototype.is, FontEmbedding); + +},{"../demo-footer":1,"../demo-header":2,"../learnjs":4,"../ndarray-image-visualizer":5,"../polymer-spec":6}],4:[function(require,module,exports){ +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("../src/index")); + +},{"../src/index":13}],5:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var polymer_spec_1 = require("./polymer-spec"); +exports.NDArrayImageVisualizerPolymer = polymer_spec_1.PolymerElement({ is: 'ndarray-image-visualizer', properties: {} }); +var NDArrayImageVisualizer = (function (_super) { + __extends(NDArrayImageVisualizer, _super); + function NDArrayImageVisualizer() { + return _super !== null && _super.apply(this, arguments) || this; + } + NDArrayImageVisualizer.prototype.ready = function () { + this.canvas = this.querySelector('#canvas'); + this.canvas.width = 0; + this.canvas.height = 0; + this.canvasContext = + this.canvas.getContext('2d'); + this.canvas.style.display = 'none'; + }; + NDArrayImageVisualizer.prototype.setShape = function (shape) { + this.canvas.width = shape[1]; + this.canvas.height = shape[0]; + }; + NDArrayImageVisualizer.prototype.setSize = function (width, height) { + this.canvas.style.width = width + 'px'; + this.canvas.style.height = height + 'px'; + }; + NDArrayImageVisualizer.prototype.saveImageDataFromNDArray = function (ndarray) { + this.imageData = this.canvasContext.createImageData(this.canvas.width, this.canvas.height); + if (ndarray.shape[2] === 1) { + this.drawGrayscaleImageData(ndarray); + } + else if (ndarray.shape[2] === 3) { + this.drawRGBImageData(ndarray); + } + }; + NDArrayImageVisualizer.prototype.drawRGBImageData = function (ndarray) { + var pixelOffset = 0; + for (var i = 0; i < ndarray.shape[0]; i++) { + for (var j = 0; j < ndarray.shape[1]; j++) { + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 1); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 2); + this.imageData.data[pixelOffset++] = 255; + } + } + }; + NDArrayImageVisualizer.prototype.drawGrayscaleImageData = function (ndarray) { + var pixelOffset = 0; + for (var i = 0; i < ndarray.shape[0]; i++) { + for (var j = 0; j < ndarray.shape[1]; j++) { + var value = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = 255; + } + } + }; + NDArrayImageVisualizer.prototype.draw = function () { + this.canvas.style.display = ''; + this.canvasContext.putImageData(this.imageData, 0, 0); + }; + return NDArrayImageVisualizer; +}(exports.NDArrayImageVisualizerPolymer)); +exports.NDArrayImageVisualizer = NDArrayImageVisualizer; +document.registerElement(NDArrayImageVisualizer.prototype.is, NDArrayImageVisualizer); + +},{"./polymer-spec":6}],6:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function PolymerElement(spec) { + return Polymer.Class(spec); +} +exports.PolymerElement = PolymerElement; + +},{}],7:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var MANIFEST_FILE = 'manifest.json'; +var CheckpointLoader = (function () { + function CheckpointLoader(urlPath) { + this.urlPath = urlPath; + if (this.urlPath.charAt(this.urlPath.length - 1) !== '/') { + this.urlPath += '/'; + } + } + CheckpointLoader.prototype.loadManifest = function () { + var _this = this; + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', _this.urlPath + MANIFEST_FILE); + xhr.onload = function () { + _this.checkpointManifest = JSON.parse(xhr.responseText); + resolve(); + }; + xhr.onerror = function (error) { + throw new Error(MANIFEST_FILE + " not found at " + _this.urlPath + ". " + error); + }; + xhr.send(); + }); + }; + CheckpointLoader.prototype.getCheckpointManifest = function () { + var _this = this; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + resolve(_this.checkpointManifest); + }); + }); + } + return new Promise(function (resolve, reject) { + resolve(_this.checkpointManifest); + }); + }; + CheckpointLoader.prototype.getAllVariables = function () { + var _this = this; + if (this.variables != null) { + return new Promise(function (resolve, reject) { + resolve(_this.variables); + }); + } + return new Promise(function (resolve, reject) { + _this.getCheckpointManifest().then(function (checkpointDefinition) { + var variableNames = Object.keys(_this.checkpointManifest); + var variablePromises = []; + for (var i = 0; i < variableNames.length; i++) { + variablePromises.push(_this.getVariable(variableNames[i])); + } + Promise.all(variablePromises).then(function (variables) { + _this.variables = {}; + for (var i = 0; i < variables.length; i++) { + _this.variables[variableNames[i]] = variables[i]; + } + resolve(_this.variables); + }); + }); + }); + }; + CheckpointLoader.prototype.getVariable = function (varName) { + var _this = this; + if (!(varName in this.checkpointManifest)) { + throw new Error('Cannot load non-existant variable ' + varName); + } + var variableRequestPromiseMethod = function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + var fname = _this.checkpointManifest[varName].filename; + xhr.open('GET', _this.urlPath + fname); + xhr.onload = function () { + var values = new Float32Array(xhr.response); + var ndarray = ndarray_1.NDArray.make(_this.checkpointManifest[varName].shape, { values: values }); + resolve(ndarray); + }; + xhr.onerror = function (error) { + throw new Error('Could not fetch variable ' + varName + ': ' + error); + }; + xhr.send(); + }; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + new Promise(variableRequestPromiseMethod).then(resolve); + }); + }); + } + return new Promise(variableRequestPromiseMethod); + }; + return CheckpointLoader; +}()); +exports.CheckpointLoader = CheckpointLoader; + +},{"./math/ndarray":24}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var STATS_SAMPLE_PERCENTAGE = 0.1; +var InMemoryDataset = (function () { + function InMemoryDataset(dataShapes) { + this.dataShapes = dataShapes; + this.normalizationInfo = {}; + } + InMemoryDataset.prototype.getDataShape = function (dataIndex) { + return this.dataShapes[dataIndex]; + }; + InMemoryDataset.prototype.getData = function () { + return this.dataset; + }; + InMemoryDataset.prototype.getStats = function () { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + return this.dataset.map(function (d) { return _this.getStatsForData(d); }); + }; + InMemoryDataset.prototype.getStatsForData = function (data) { + var inputMin = Number.POSITIVE_INFINITY; + var inputMax = Number.NEGATIVE_INFINITY; + var exampleIndices = data.map(function (example, i) { return i; }); + util.shuffle(exampleIndices); + exampleIndices = + exampleIndices.slice(exampleIndices.length * STATS_SAMPLE_PERCENTAGE); + for (var i = 0; i < exampleIndices.length; i++) { + var inputValues = data[exampleIndices[i]].getValues(); + for (var j = 0; j < inputValues.length; j++) { + inputMin = Math.min(inputMin, inputValues[j]); + inputMax = Math.max(inputMax, inputValues[j]); + } + } + return { + inputMin: inputMin, + inputMax: inputMax, + exampleCount: data.length, + shape: data[0].shape, + }; + }; + InMemoryDataset.prototype.normalizeExamplesToRange = function (examples, curLowerBounds, curUpperBounds, newLowerBounds, newUpperBounds) { + var curBoundsIsPerDimension = (curUpperBounds instanceof Float32Array && + curLowerBounds instanceof Float32Array); + var newBoundsIsPerDimension = (newLowerBounds instanceof Float32Array && + newUpperBounds instanceof Float32Array); + var inputSize = util.sizeFromShape(examples[0].shape); + var newExamples = []; + examples.forEach(function (example) { + var inputValues = example.getValues(); + var normalizedValues = new Float32Array(inputSize); + for (var j = 0; j < inputSize; j++) { + var curLowerBound = curBoundsIsPerDimension ? + curLowerBounds[j] : + curLowerBounds; + var curUpperBound = curBoundsIsPerDimension ? + curUpperBounds[j] : + curUpperBounds; + var curRange = curUpperBound - curLowerBound; + var newLowerBound = newBoundsIsPerDimension ? + newLowerBounds[j] : + newLowerBounds; + var newUpperBound = newBoundsIsPerDimension ? + newUpperBounds[j] : + newUpperBounds; + var newRange = newUpperBound - newLowerBound; + if (curRange === 0) { + normalizedValues[j] = newLowerBound; + } + else { + normalizedValues[j] = newLowerBound + + newRange * (inputValues[j] - curLowerBound) / curRange; + } + } + newExamples.push(ndarray_1.NDArray.make(example.shape, { values: normalizedValues })); + }); + return newExamples; + }; + InMemoryDataset.prototype.computeBounds = function (dataIndex) { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + var size = util.sizeFromShape(this.dataset[dataIndex][0].shape); + this.normalizationInfo[dataIndex] = { + isNormalized: false, + minValues: new Float32Array(size), + maxValues: new Float32Array(size) + }; + for (var i = 0; i < size; i++) { + this.normalizationInfo[dataIndex].minValues[i] = Number.POSITIVE_INFINITY; + this.normalizationInfo[dataIndex].maxValues[i] = Number.NEGATIVE_INFINITY; + } + this.dataset[dataIndex].forEach(function (example) { + var inputValues = example.getValues(); + for (var k = 0; k < size; k++) { + _this.normalizationInfo[dataIndex].minValues[k] = Math.min(_this.normalizationInfo[dataIndex].minValues[k], inputValues[k]); + _this.normalizationInfo[dataIndex].maxValues[k] = Math.max(_this.normalizationInfo[dataIndex].maxValues[k], inputValues[k]); + } + }); + }; + InMemoryDataset.prototype.normalizeWithinBounds = function (dataIndex, lowerBound, upperBound) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + if (dataIndex >= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":24,"./util":88}],9:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":10,"./math/concat3d_util":17,"./math/conv_util":18,"./math/ndarray":24,"./util":88}],10:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":14}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":24,"./session":84}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":9,"./priority_queue":83}],13:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":7,"./dataset":8,"./graph":9,"./graph_runner":11,"./initializers":14,"./input_provider":15,"./math/conv_util":18,"./math/math":21,"./math/math_cpu":22,"./math/math_gpu":23,"./math/ndarray":24,"./math/webgl/gpgpu_context":37,"./math/webgl/gpgpu_util":38,"./math/webgl/render_ndarray_gpu_util":50,"./math/webgl/webgl_util":60,"./optimizer":82,"./session":84,"./sgd_optimizer":86,"./util":88}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":24}],15:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":24,"./util":88}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":24}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":88}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":88}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":24}],21:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":88,"./concat3d_util":17,"./copy2d_util":19,"./ndarray":24}],22:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":18,"../util":88,"./concat3d_util":17,"./copy2d_util":19,"./math":21,"./ndarray":24}],23:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":88,"./concat3d_util":17,"./conv_util":18,"./math":21,"./ndarray":24,"./webgl/addscaledmat_gpu":25,"./webgl/addsubmuldiv_gpu":26,"./webgl/argmaxequals_gpu":27,"./webgl/argminmax_gpu":28,"./webgl/avg_pool_gpu":29,"./webgl/batchnorm_gpu":30,"./webgl/concat3d_gpu":32,"./webgl/conv_backprop_gpu":33,"./webgl/conv_gpu":34,"./webgl/copy_gpu":35,"./webgl/exp_gpu":36,"./webgl/gpgpu_context":37,"./webgl/gpgpu_util":38,"./webgl/log_gpu":39,"./webgl/logsumexp_gpu":40,"./webgl/max_pool_backprop_gpu":41,"./webgl/max_pool_gpu":42,"./webgl/min_pool_gpu":43,"./webgl/minmax_gpu":44,"./webgl/mulmat_gpu":45,"./webgl/neg_gpu":46,"./webgl/pool_gpu":47,"./webgl/reducesum_gpu":48,"./webgl/relu_gpu":49,"./webgl/reshape_gpu":51,"./webgl/resize_bilinear_gpu":52,"./webgl/shader_compiler":53,"./webgl/sigmoid_gpu":54,"./webgl/step_gpu":55,"./webgl/texture_manager":57,"./webgl/trig_gpu":58,"./webgl/webgl_util":60}],24:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":88,"./webgl/webgl_util":60}],25:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":37}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":21,"./binaryop_gpu":31}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":28}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":60}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":47}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":37}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":18}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":18,"./conv_gpu":34}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":18}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":59}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":38,"./tex_util":56,"./webgl_util":60}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":56,"./webgl_util":60}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":59}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":37}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":18}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":47}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":47}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":60}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":21,"./shader_compiler":53}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":59}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":18,"./webgl_util":60}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":37}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":59}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":60}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":88}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":18}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":88}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":59}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":59}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":59}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":37}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":88}],61:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":9,"./graph_util":12,"./ops/add":62,"./ops/argmax":63,"./ops/argmaxequals":64,"./ops/concat3d":65,"./ops/convolution":66,"./ops/divide":67,"./ops/element_wise_activation":68,"./ops/element_wise_cost":69,"./ops/exp":70,"./ops/linear_combination":71,"./ops/log":72,"./ops/matmul":73,"./ops/max_pool":74,"./ops/multiply":75,"./ops/reduce_sum":77,"./ops/reshape":78,"./ops/softmax":79,"./ops/split":80,"./ops/subtract":81}],62:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":12,"../math/ndarray":24,"../util":88,"./op":76}],63:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":76}],64:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":76}],65:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":17,"./op":76}],66:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":18,"../util":88,"./op":76}],67:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":12,"../util":88,"./op":76}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":16,"./op":76}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":12,"../math/cost_functions":20,"../math/ndarray":24,"../util":88,"./op":76}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":12,"./op":76}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":12,"./op":76}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":12,"./op":76}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":12,"../math/math":21,"./op":76}],74:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":18,"../util":88,"./op":76}],75:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":12,"../util":88,"./op":76}],76:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],77:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":12,"../math/ndarray":24,"../util":88,"./op":76}],78:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":88,"./op":76}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":9,"../math/ndarray":24,"../util":88,"./op":76}],80:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":12,"../util":88,"./op":76}],81:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":12,"../math/ndarray":24,"../util":88,"./op":76}],82:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],83:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],84:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":24,"./operation_emitter":61,"./session_util":85,"./tensor_array_map":87,"./util":88}],85:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":9,"./graph_util":12,"./math/ndarray":24,"./util":88}],86:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":24,"./optimizer":82,"./session_util":85,"./tensor_array_map":87}],87:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],88:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[3]) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJkZW1vcy9kZW1vLWZvb3Rlci50cyIsImRlbW9zL2RlbW8taGVhZGVyLnRzIiwiZGVtb3MvZm9udC1lbWJlZGRpbmcvZm9udC1lbWJlZGRpbmcudHMiLCJkZW1vcy9sZWFybmpzLnRzIiwiZGVtb3MvbmRhcnJheS1pbWFnZS12aXN1YWxpemVyLnRzIiwiZGVtb3MvcG9seW1lci1zcGVjLnRzIiwic3JjL2NoZWNrcG9pbnRfbG9hZGVyLnRzIiwic3JjL2RhdGFzZXQudHMiLCJzcmMvZ3JhcGgudHMiLCJzcmMvZ3JhcGhfbGF5ZXJzLnRzIiwic3JjL2dyYXBoX3J1bm5lci50cyIsInNyYy9ncmFwaF91dGlsLnRzIiwic3JjL2luZGV4LnRzIiwic3JjL2luaXRpYWxpemVycy50cyIsInNyYy9pbnB1dF9wcm92aWRlci50cyIsInNyYy9tYXRoL2FjdGl2YXRpb25fZnVuY3Rpb25zLnRzIiwic3JjL21hdGgvY29uY2F0M2RfdXRpbC50cyIsInNyYy9tYXRoL2NvbnZfdXRpbC50cyIsInNyYy9tYXRoL2NvcHkyZF91dGlsLnRzIiwic3JjL21hdGgvY29zdF9mdW5jdGlvbnMudHMiLCJzcmMvbWF0aC9tYXRoLnRzIiwic3JjL21hdGgvbWF0aF9jcHUudHMiLCJzcmMvbWF0aC9tYXRoX2dwdS50cyIsInNyYy9tYXRoL25kYXJyYXkudHMiLCJzcmMvbWF0aC93ZWJnbC9hZGRzY2FsZWRtYXRfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYWRkc3VibXVsZGl2X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2FyZ21heGVxdWFsc19ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9hcmdtaW5tYXhfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYXZnX3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYmF0Y2hub3JtX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2JpbmFyeW9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2NvbmNhdDNkX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2NvbnZfYmFja3Byb3BfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvY29udl9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb3B5X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2V4cF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0LnRzIiwic3JjL21hdGgvd2ViZ2wvZ3BncHVfdXRpbC50cyIsInNyYy9tYXRoL3dlYmdsL2xvZ19ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9sb2dzdW1leHBfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbWF4X3Bvb2xfYmFja3Byb3BfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbWF4X3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbWluX3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbWlubWF4X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL211bG1hdF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9uZWdfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcG9vbF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9yZWR1Y2VzdW1fZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVsdV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9yZW5kZXJfbmRhcnJheV9ncHVfdXRpbC50cyIsInNyYy9tYXRoL3dlYmdsL3Jlc2hhcGVfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVzaXplX2JpbGluZWFyX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3NoYWRlcl9jb21waWxlci50cyIsInNyYy9tYXRoL3dlYmdsL3NpZ21vaWRfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvc3RlcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC90ZXhfdXRpbC50cyIsInNyYy9tYXRoL3dlYmdsL3RleHR1cmVfbWFuYWdlci50cyIsInNyYy9tYXRoL3dlYmdsL3RyaWdfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvdW5hcnlvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC93ZWJnbF91dGlsLnRzIiwic3JjL29wZXJhdGlvbl9lbWl0dGVyLnRzIiwic3JjL29wcy9hZGQudHMiLCJzcmMvb3BzL2FyZ21heC50cyIsInNyYy9vcHMvYXJnbWF4ZXF1YWxzLnRzIiwic3JjL29wcy9jb25jYXQzZC50cyIsInNyYy9vcHMvY29udm9sdXRpb24udHMiLCJzcmMvb3BzL2RpdmlkZS50cyIsInNyYy9vcHMvZWxlbWVudF93aXNlX2FjdGl2YXRpb24udHMiLCJzcmMvb3BzL2VsZW1lbnRfd2lzZV9jb3N0LnRzIiwic3JjL29wcy9leHAudHMiLCJzcmMvb3BzL2xpbmVhcl9jb21iaW5hdGlvbi50cyIsInNyYy9vcHMvbG9nLnRzIiwic3JjL29wcy9tYXRtdWwudHMiLCJzcmMvb3BzL21heF9wb29sLnRzIiwic3JjL29wcy9tdWx0aXBseS50cyIsInNyYy9vcHMvb3AudHMiLCJzcmMvb3BzL3JlZHVjZV9zdW0udHMiLCJzcmMvb3BzL3Jlc2hhcGUudHMiLCJzcmMvb3BzL3NvZnRtYXgudHMiLCJzcmMvb3BzL3NwbGl0LnRzIiwic3JjL29wcy9zdWJ0cmFjdC50cyIsInNyYy9vcHRpbWl6ZXIudHMiLCJzcmMvcHJpb3JpdHlfcXVldWUudHMiLCJzcmMvc2Vzc2lvbi50cyIsInNyYy9zZXNzaW9uX3V0aWwudHMiLCJzcmMvc2dkX29wdGltaXplci50cyIsInNyYy90ZW5zb3JfYXJyYXlfbWFwLnRzIiwic3JjL3V0aWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNjQSxPQUFPLENBQUMsRUFBQyxFQUFFLEVBQUUsYUFBYSxFQUFDLENBQUMsQ0FBQzs7O0FDQTdCLE9BQU8sQ0FBQyxFQUFDLEVBQUUsRUFBRSxhQUFhLEVBQUMsQ0FBQyxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7QUNDN0IsdUNBQXFDO0FBQ3JDLDBCQUF3QjtBQUN4QiwwQkFBd0I7QUFFeEIsc0NBQStJO0FBRy9JLGdEQUFtRTtBQUVuRSxJQUFNLHdCQUF3QixHQUMxQiw2REFBNkQsQ0FB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LENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO2dCQUM1RCxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxVQUFVLENBQUMsQ0FBQztnQkFFL0QsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7b0JBQ3ZDLElBQU0sUUFBUSxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUM7b0JBQzFCLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUM7b0JBQzVELElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsS0FBSyxHQUFHLFFBQVEsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxDQUFDO29CQUUvRCxJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7b0JBQ2hCLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7d0JBQ3RDLElBQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxVQUFVLEdBQUcsUUFBUSxDQUFDO3dCQUV0QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDOzRCQUN0QyxJQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsVUFBVSxHQUFHLFFBQVEsQ0FBQzs0QkFFdEMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxlQUFlLEVB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Q04sSUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQy9CLENBQUM7QUFDSCxDQUFDO0FBZEQsb0JBY0M7QUFFRCw2QkFBb0MsSUFBWTtJQUM5QyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDckQsRUFBRSxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ25CLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFDRCxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDbkIsQ0FBQztBQVBELGtEQU9DO0FBRUQsK0JBQXNDLENBQVM7SUFDN0MsSUFBTSxlQUFlLEdBQUcsSUFBSSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0MsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUMzQixlQUFlLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFDRCxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDekIsTUFBTSxDQUFDLGVBQWUsQ0FBQztBQUN6QixDQUFDO0FBUEQsc0RBT0MiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuUG9seW1lcih7aXM6ICdkZW1vLWZvb3Rlcid9KTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblBvbHltZXIoe2lzOiAnZGVtby1oZWFkZXInfSk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAnLi4vbmRhcnJheS1pbWFnZS12aXN1YWxpemVyJztcbmltcG9ydCAnLi4vZGVtby1oZWFkZXInO1xuaW1wb3J0ICcuLi9kZW1vLWZvb3Rlcic7XG5cbmltcG9ydCB7QXJyYXkxRCwgQ2hlY2twb2ludExvYWRlciwgR3JhcGgsIE5EQXJyYXksIE5EQXJyYXlJbml0aWFsaXplciwgTkRBcnJheU1hdGgsIE5EQXJyYXlNYXRoR1BVLCBTY2FsYXIsIFNlc3Npb24sIFRlbnNvcn0gZnJvbSAnLi4vbGVhcm5qcyc7XG5pbXBvcnQge05EQXJyYXlJbWFnZVZpc3VhbGl6ZXJ9IGZyb20gJy4uL25kYXJyYXktaW1hZ2UtdmlzdWFsaXplcic7XG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tdW51c2VkLXZhcmlhYmxlXG5pbXBvcnQge1BvbHltZXJFbGVtZW50LCBQb2x5bWVySFRNTEVsZW1lbnR9IGZyb20gJy4uL3BvbHltZXItc3BlYyc7XG5cbmNvbnN0IEdPT0dMRV9DTE9VRF9TVE9SQUdFX0RJUiA9XG4gICAgJ2h0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9sZWFybmpzLWRhdGEvY2hlY2twb2ludF96b28vJztcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOnZhcmlhYmxlLW5hbWVcbmV4cG9ydCBsZXQgRm9udEVtYmVkZGluZ1BvbHltZXIgPSBQb2x5bWVyRWxlbWVudCh7XG4gIGlzOiAnZm9udC1lbWJlZGRpbmcnLFxuICBwcm9wZXJ0aWVzOiB7XG4gICAgY2hhcjogU3RyaW5nLFxuICAgIGZvbnRJZDogU3RyaW5nLFxuICAgIGVtYmVkZGluZzogQXJyYXksXG4gICAgbnVtYmVyT2ZWYWxpZENoYXJzOiB7dHlwZTogTnVtYmVyLCB2YWx1ZTogNjIsIHJlYWRvbmx5OiB0cnVlfSxcbiAgfVxufSk7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRW1iZWRkaW5nRGltU2V0dGluZ3Mge1xuICB2YWw6IG51bWJlcjtcbiAgaW5pdDogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgRm9udEVtYmVkZGluZyBleHRlbmRzIEZvbnRFbWJlZGRpbmdQb2x5bWVyIHtcbiAgcHJpdmF0ZSB2YXJpYWJsZXM6IHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX07XG5cbiAgLy8gRW1iZWRkaW5nIGRpbWVuc2lvbiBzZXR0aW5ncyBmb3IgdGhlIHNsaWRlcnNcbiAgZW1iZWRkaW5nOiBFbWJlZGRpbmdEaW1TZXR0aW5nc1tdO1xuXG4gIC8vIE1hcHBpbmcgb2YgY2hhcmFjdGVycyB0byB0aGUgSUQgZm9yIHRoZSBvbmUtaG90IGluIHRoZSBtb2RlbC5cbiAgY2hhcklkTWFwOiB7W2NoYXI6IHN0cmluZ106IG51bWJlcn07XG5cbiAgLy8gRm9udCBJRCBzdHJpbmcgZnJvbSB0aGUgcGFwZXItaW5wdXQuXG4gIGZvbnRJZDogc3RyaW5nO1xuXG4gIC8vIENoYXJhY3RlciBzdHJpbmcgZnJvbSB0aGUgcGFwZXItaW5wdXQuXG4gIGNoYXI6IHN0cmluZztcblxuICBncmFwaDogR3JhcGg7XG4gIHNlc3Npb246IFNlc3Npb247XG4gIGlucHV0VGVuc29yOiBUZW5zb3I7XG4gIG91dHB1dFRlbnNvcjogVGVuc29yO1xuICBtYXRoOiBOREFycmF5TWF0aDtcbiAgdmlzOiBOREFycmF5SW1hZ2VWaXN1YWxpemVyO1xuICBudW1iZXJPZlZhbGlkQ2hhcnM6IG51bWJlcjtcblxuICByZWFkeSgpIHtcbiAgICBjb25zdCBjaGVja3BvaW50TG9hZGVyID1cbiAgICAgICAgbmV3IENoZWNrcG9pbnRMb2FkZXIoR09PR0xFX0NMT1VEX1NUT1JBR0VfRElSICsgJ2ZvbnRzLycpO1xuICAgIGNoZWNrcG9pbnRMb2FkZXIuZ2V0QWxsVmFyaWFibGVzKCkudGhlbih2YXJpYWJsZXMgPT4ge1xuICAgICAgdGhpcy52YXJpYWJsZXMgPSB2YXJpYWJsZXM7XG4gICAgICB0aGlzLmJ1aWxkTW9kZWwoKTtcbiAgICAgIHRoaXMuaW5mZXIodGhpcy5mb250SWQsIHRoaXMuY2hhcik7XG4gICAgfSk7XG5cbiAgICAvLyBTZXQgdXAgY2hhcmFjdGVyIElEIG1hcHBpbmcuXG4gICAgdGhpcy5jaGFySWRNYXAgPSB7fTtcbiAgICBmb3IgKGxldCBpID0gNjU7IGkgPCA5MTsgaSsrKSB7XG4gICAgICB0aGlzLmNoYXJJZE1hcFtTdHJpbmcuZnJvbUNoYXJDb2RlKGkpXSA9IGkgLSA2NTtcbiAgICB9XG4gICAgZm9yIChsZXQgaSA9IDk3OyBpIDwgMTIzOyBpKyspIHtcbiAgICAgIHRoaXMuY2hhcklkTWFwW1N0cmluZy5mcm9tQ2hhckNvZGUoaSldID0gaSAtIDk3ICsgMjY7XG4gICAgfVxuICAgIGZvciAobGV0IGkgPSA0ODsgaSA8IDU4OyBpKyspIHtcbiAgICAgIHRoaXMuY2hhcklkTWFwW1N0cmluZy5mcm9tQ2hhckNvZGUoaSldID0gaSAtIDQ4ICsgNTI7XG4gICAgfVxuICAgIHRoaXMuZm9udElkID0gJzAnO1xuICAgIHRoaXMuY2hhciA9ICdBJztcblxuICAgIGNvbnN0IGZvbnRJZEVsZW1lbnQgPSB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNmb250LWlkJykgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICBmb250SWRFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIChlKSA9PiB7XG4gICAgICB0aGlzLmZvbnRJZCA9IGZvbnRJZEVsZW1lbnQudmFsdWU7XG4gICAgICB0aGlzLm5ld0ZvbnRJZCh0aGlzLmZvbnRJZCk7XG4gICAgICB0aGlzLmluZmVyKHRoaXMuZm9udElkLCB0aGlzLmNoYXIpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgY2hhcklkRWxlbWVudCA9IHRoaXMucXVlcnlTZWxlY3RvcignI2NoYXItaWQnKSBhcyBIVE1MSW5wdXRFbGVtZW50O1xuICAgIGNoYXJJZEVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKGUpID0+IHtcbiAgICAgIHRoaXMuY2hhciA9IGNoYXJJZEVsZW1lbnQudmFsdWU7XG4gICAgICB0aGlzLmluZmVyKHRoaXMuZm9udElkLCB0aGlzLmNoYXIpO1xuICAgIH0pO1xuICB9XG5cbiAgbmV3Rm9udElkKGZvbnRJZDogc3RyaW5nKSB7XG4gICAgLy8gSWdub3JlIGludmFsaWQgSURzIGFuZCBuby1vcCBiZWZvcmUgd2VpZ2h0cyBsb2FkZWQuXG4gICAgaWYgKGZvbnRJZC5sZW5ndGggPT09IDAgfHwgK2ZvbnRJZCA8IDAgfHwgK2ZvbnRJZCA+IDU2NDQzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gTG9hZCB1cCBlbWJlZGRpbmdzIGZvciB0aGUgc2VsZWN0ZWQgZm9udC5cbiAgICBjb25zdCBlbWJlZGRpbmc6IEVtYmVkZGluZ0RpbVNldHRpbmdzW10gPSBbXTtcbiAgICBjb25zdCBlbWJlZGRpbmdOREFycmF5ID1cbiAgICAgICAgdGhpcy52YXJpYWJsZXNbJ2lucHV0X2Zyb21fZmVhdHVyZV9jb2x1bW5zL2ZvbnRfZW1iZWRkaW5nL3dlaWdodHMnXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVtYmVkZGluZ05EQXJyYXkuc2hhcGVbMV07IGkrKykge1xuICAgICAgY29uc3QgbnVtID0gZW1iZWRkaW5nTkRBcnJheS5nZXQoK2ZvbnRJZCwgaSk7XG4gICAgICBlbWJlZGRpbmcucHVzaCh7aW5pdDogbnVtLCB2YWw6IG51bX0pO1xuICAgIH1cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgKHRoaXMgYXMgYW55KS5zZXQoJ2VtYmVkZGluZycsIGVtYmVkZGluZyk7XG4gIH1cblxuICBzbGlkZXJDaGFuZ2UoZTogRXZlbnQpIHtcbiAgICBjb25zdCBlbGVtZW50ID0gZS50YXJnZXQgYXMgUG9seW1lckhUTUxFbGVtZW50O1xuICAgIGNvbnN0IGluZGV4ID0gcGFyc2VJbnQoZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtaW5kZXgnKSEsIDEwKTtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgdGhpcy5lbWJlZGRpbmdbaW5kZXhdLnZhbCA9IChlbGVtZW50IGFzIGFueSkuaW1tZWRpYXRlVmFsdWU7XG4gICAgLy8gQ2F1c2UgaW5mZXJlbmNlIG9uY2UgYSBzbGlkZXIgY2hhbmdlcy5cbiAgICB0aGlzLmluZmVyKHRoaXMuZm9udElkLCB0aGlzLmNoYXIpO1xuICB9XG5cbiAgYnVpbGRNb2RlbCgpIHtcbiAgICB0aGlzLnZpcyA9IHRoaXMuJC52aXMgYXMgTkRBcnJheUltYWdlVmlzdWFsaXplcjtcbiAgICB0aGlzLnZpcy5zZXRTaGFwZShbNjQsIDY0XSk7XG4gICAgdGhpcy52aXMuc2V0U2l6ZSgxMjgsIDEyOCk7XG5cbiAgICB0aGlzLm5ld0ZvbnRJZCh0aGlzLmZvbnRJZCk7XG5cbiAgICBjb25zdCBmYzF3TmFtZSA9ICdTdGFjay9mdWxseV9jb25uZWN0ZWRfMS93ZWlnaHRzJztcbiAgICBjb25zdCBmYzFiTmFtZSA9ICdTdGFjay9mdWxseV9jb25uZWN0ZWRfMS9iaWFzZXMnO1xuICAgIGNvbnN0IGZjMndOYW1lID0gJ1N0YWNrL2Z1bGx5X2Nvbm5lY3RlZF8yL3dlaWdodHMnO1xuICAgIGNvbnN0IGZjMmJOYW1lID0gJ1N0YWNrL2Z1bGx5X2Nvbm5lY3RlZF8yL2JpYXNlcyc7XG4gICAgY29uc3QgZmMzd05hbWUgPSAnU3RhY2svZnVsbHlfY29ubmVjdGVkXzMvd2VpZ2h0cyc7XG4gICAgY29uc3QgZmMzYk5hbWUgPSAnU3RhY2svZnVsbHlfY29ubmVjdGVkXzMvYmlhc2VzJztcbiAgICBjb25zdCBmYzR3TmFtZSA9ICdTdGFjay9mdWxseV9jb25uZWN0ZWRfNC93ZWlnaHRzJztcbiAgICBjb25zdCBmYzRiTmFtZSA9ICdTdGFjay9mdWxseV9jb25uZWN0ZWRfNC9iaWFzZXMnO1xuICAgIGNvbnN0IGZjNXdOYW1lID0gJ2Z1bGx5X2Nvbm5lY3RlZC93ZWlnaHRzJztcbiAgICBjb25zdCBmYzViTmFtZSA9ICdmdWxseV9jb25uZWN0ZWQvYmlhc2VzJztcblxuICAgIHRoaXMuZ3JhcGggPSBuZXcgR3JhcGgoKTtcbiAgICBjb25zdCBnID0gdGhpcy5ncmFwaDtcbiAgICB0aGlzLmlucHV0VGVuc29yID0gZy5wbGFjZWhvbGRlcignaW5wdXQnLCBbMTAyXSk7XG5cbiAgICBjb25zdCByZWx1MSA9IGcubGF5ZXJzLmRlbnNlKFxuICAgICAgICAnZmMxJywgdGhpcy5pbnB1dFRlbnNvciwgdGhpcy52YXJpYWJsZXNbZmMxYk5hbWVdLnNoYXBlWzFdLFxuICAgICAgICAoeCkgPT4gZy5yZWx1KHgpLCB0cnVlLFxuICAgICAgICBuZXcgTkRBcnJheUluaXRpYWxpemVyKHRoaXMudmFyaWFibGVzW2ZjMXdOYW1lXSksXG4gICAgICAgIG5ldyBOREFycmF5SW5pdGlhbGl6ZXIodGhpcy52YXJpYWJsZXNbZmMxYk5hbWVdKSk7XG4gICAgY29uc3QgcmVsdTIgPSBnLmxheWVycy5kZW5zZShcbiAgICAgICAgJ2ZjMicsIHJlbHUxLCB0aGlzLnZhcmlhYmxlc1tmYzJiTmFtZV0uc2hhcGVbMV0sICh4KSA9PiBnLnJlbHUoeCksIHRydWUsXG4gICAgICAgIG5ldyBOREFycmF5SW5pdGlhbGl6ZXIodGhpcy52YXJpYWJsZXNbZmMyd05hbWVdKSxcbiAgICAgICAgbmV3IE5EQXJyYXlJbml0aWFsaXplcih0aGlzLnZhcmlhYmxlc1tmYzJiTmFtZV0pKTtcbiAgICBjb25zdCByZWx1MyA9IGcubGF5ZXJzLmRlbnNlKFxuICAgICAgICAnZmMzJywgcmVsdTIsIHRoaXMudmFyaWFibGVzW2ZjM2JOYW1lXS5zaGFwZVsxXSwgKHgpID0+IGcucmVsdSh4KSwgdHJ1ZSxcbiAgICAgICAgbmV3IE5EQXJyYXlJbml0aWFsaXplcih0aGlzLnZhcmlhYmxlc1tmYzN3TmFtZV0pLFxuICAgICAgICBuZXcgTkRBcnJheUluaXRpYWxpemVyKHRoaXMudmFyaWFibGVzW2ZjM2JOYW1lXSkpO1xuICAgIGNvbnN0IHJlbHU0ID0gZy5sYXllcnMuZGVuc2UoXG4gICAgICAgICdmYzQnLCByZWx1MywgdGhpcy52YXJpYWJsZXNbZmM0Yk5hbWVdLnNoYXBlWzFdLCAoeCkgPT4gZy5yZWx1KHgpLCB0cnVlLFxuICAgICAgICBuZXcgTkRBcnJheUluaXRpYWxpemVyKHRoaXMudmFyaWFibGVzW2ZjNHdOYW1lXSksXG4gICAgICAgIG5ldyBOREFycmF5SW5pdGlhbGl6ZXIodGhpcy52YXJpYWJsZXNbZmM0Yk5hbWVdKSk7XG4gICAgdGhpcy5vdXRwdXRUZW5zb3IgPSBnLmxheWVycy5kZW5zZShcbiAgICAgICAgJ2ZjNScsIHJlbHU0LCB0aGlzLnZhcmlhYmxlc1tmYzViTmFtZV0uc2hhcGVbMV0sICh4KSA9PiBnLnNpZ21vaWQoeCksXG4gICAgICAgIHRydWUsIG5ldyBOREFycmF5SW5pdGlhbGl6ZXIodGhpcy52YXJpYWJsZXNbZmM1d05hbWVdKSxcbiAgICAgICAgbmV3IE5EQXJyYXlJbml0aWFsaXplcih0aGlzLnZhcmlhYmxlc1tmYzViTmFtZV0pKTtcblxuICAgIHRoaXMubWF0aCA9IG5ldyBOREFycmF5TWF0aEdQVSgpO1xuICAgIHRoaXMuc2Vzc2lvbiA9IG5ldyBTZXNzaW9uKGcsIHRoaXMubWF0aCk7XG4gIH1cblxuICBpbmZlcihmb250SWQ6IHN0cmluZywgY2hhcjogc3RyaW5nKSB7XG4gICAgaWYgKGZvbnRJZC5sZW5ndGggPT09IDAgfHwgK2ZvbnRJZCA8IDAgfHwgK2ZvbnRJZCA+IDU2NDQzIHx8XG4gICAgICAgIGNoYXIubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGNoYXJJZCA9IHRoaXMuY2hhcklkTWFwW2NoYXIuY2hhckF0KDApXTtcbiAgICBpZiAoY2hhcklkID09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBvbmVob3QgPSBBcnJheS5hcHBseShudWxsLCBBcnJheSh0aGlzLm51bWJlck9mVmFsaWRDaGFycykpXG4gICAgICAgICAgICAgICAgICAgICAgIC5tYXAoTnVtYmVyLnByb3RvdHlwZS52YWx1ZU9mLCAwKTtcbiAgICBvbmVob3RbY2hhcklkXSA9IDE7XG4gICAgLy8gVE9ETzogVXNlIHNsaWNlIGhlcmUgc28gd2UgZG9uJ3QgaGF2ZSB0byBrZWVwIHJldXBsb2FkaW5nIHRoZSBlbWJlZGRpbmcuXG4gICAgY29uc3QgZW1iZWRkaW5nID1cbiAgICAgICAgdGhpcy5lbWJlZGRpbmcubWFwKChzZXR0aW5nczogRW1iZWRkaW5nRGltU2V0dGluZ3MpID0+IHNldHRpbmdzLnZhbCk7XG5cbiAgICB0aGlzLm1hdGguc2NvcGUoKGtlZXAsIHRyYWNrKSA9PiB7XG4gICAgICBjb25zdCBpbnB1dERhdGEgPSB0cmFjayhBcnJheTFELm5ldyhlbWJlZGRpbmcuY29uY2F0KG9uZWhvdCkpKTtcblxuICAgICAgY29uc3QgaW5mZXIgPSB0aGlzLnNlc3Npb24uZXZhbChcbiAgICAgICAgICB0aGlzLm91dHB1dFRlbnNvciwgW3t0ZW5zb3I6IHRoaXMuaW5wdXRUZW5zb3IsIGRhdGE6IGlucHV0RGF0YX1dKTtcblxuICAgICAgLy8gQ29udmVydCB0aGUgaW5mZXJyZWQgdGVuc29yIHRvIHRoZSBwcm9wZXIgc2NhbGluZyBmb3IgZGlzcGxheSB0aGVuIGRyYXdcbiAgICAgIC8vIGl0LlxuICAgICAgY29uc3Qgc2NhbGFyID0gdHJhY2soU2NhbGFyLm5ldygyNTUpKTtcbiAgICAgIGNvbnN0IHNjYWxlZCA9IHRoaXMubWF0aC5zY2FsYXJUaW1lc0FycmF5KHNjYWxhciwgaW5mZXIpO1xuICAgICAgY29uc3QgYWRqdXN0ZWQgPSB0aGlzLm1hdGguc2NhbGFyTWludXNBcnJheShzY2FsYXIsIHNjYWxlZCk7XG4gICAgICB0aGlzLnZpcy5zYXZlSW1hZ2VEYXRhRnJvbU5EQXJyYXkoYWRqdXN0ZWQuYXMzRCg2NCwgNjQsIDEpKTtcbiAgICAgIHRoaXMudmlzLmRyYXcoKTtcbiAgICB9KTtcbiAgfVxufVxuZG9jdW1lbnQucmVnaXN0ZXJFbGVtZW50KEZvbnRFbWJlZGRpbmcucHJvdG90eXBlLmlzLCBGb250RW1iZWRkaW5nKTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuLy8gVGhpcyBmaWxlIGlzIGp1c3QgYW4gYWxpYXMgdGhhdCBwb2ludHMgdG8gdGhlIGN1cnJlbnQgbGVhcm5qcyB2ZXJzaW9uXG4vLyBhdCB0aGlzIGJyYW5jaCwgc28gZGVtb3MgY2FuIGltcG9ydCB0aGUgbGlicmFyeSBhcyAnLi4vbGVhcm5qcycuXG5leHBvcnQgKiBmcm9tICcuLi9zcmMvaW5kZXgnO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tdW51c2VkLXZhcmlhYmxlXG5pbXBvcnQge0FycmF5M0R9IGZyb20gJy4uL3NyYy9tYXRoL25kYXJyYXknO1xuXG5pbXBvcnQge1BvbHltZXJFbGVtZW50LCBQb2x5bWVySFRNTEVsZW1lbnR9IGZyb20gJy4vcG9seW1lci1zcGVjJztcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lXG5leHBvcnQgbGV0IE5EQXJyYXlJbWFnZVZpc3VhbGl6ZXJQb2x5bWVyID1cbiAgICBQb2x5bWVyRWxlbWVudCh7aXM6ICduZGFycmF5LWltYWdlLXZpc3VhbGl6ZXInLCBwcm9wZXJ0aWVzOiB7fX0pO1xuXG5leHBvcnQgY2xhc3MgTkRBcnJheUltYWdlVmlzdWFsaXplciBleHRlbmRzIE5EQXJyYXlJbWFnZVZpc3VhbGl6ZXJQb2x5bWVyIHtcbiAgcHJpdmF0ZSBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50O1xuICBwcml2YXRlIGNhbnZhc0NvbnRleHQ6IENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRDtcbiAgcHJpdmF0ZSBpbWFnZURhdGE6IEltYWdlRGF0YTtcblxuICByZWFkeSgpIHtcbiAgICB0aGlzLmNhbnZhcyA9IHRoaXMucXVlcnlTZWxlY3RvcignI2NhbnZhcycpIGFzIEhUTUxDYW52YXNFbGVtZW50O1xuICAgIHRoaXMuY2FudmFzLndpZHRoID0gMDtcbiAgICB0aGlzLmNhbnZhcy5oZWlnaHQgPSAwO1xuICAgIHRoaXMuY2FudmFzQ29udGV4dCA9XG4gICAgICAgIHRoaXMuY2FudmFzLmdldENvbnRleHQoJzJkJykgYXMgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEO1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gIH1cblxuICBzZXRTaGFwZShzaGFwZTogbnVtYmVyW10pIHtcbiAgICB0aGlzLmNhbnZhcy53aWR0aCA9IHNoYXBlWzFdO1xuICAgIHRoaXMuY2FudmFzLmhlaWdodCA9IHNoYXBlWzBdO1xuICB9XG5cbiAgc2V0U2l6ZSh3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLndpZHRoID0gd2lkdGggKyAncHgnO1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLmhlaWdodCA9IGhlaWdodCArICdweCc7XG4gIH1cblxuICBzYXZlSW1hZ2VEYXRhRnJvbU5EQXJyYXkobmRhcnJheTogQXJyYXkzRCkge1xuICAgIHRoaXMuaW1hZ2VEYXRhID0gdGhpcy5jYW52YXNDb250ZXh0LmNyZWF0ZUltYWdlRGF0YShcbiAgICAgICAgdGhpcy5jYW52YXMud2lkdGgsIHRoaXMuY2FudmFzLmhlaWdodCk7XG4gICAgaWYgKG5kYXJyYXkuc2hhcGVbMl0gPT09IDEpIHtcbiAgICAgIHRoaXMuZHJhd0dyYXlzY2FsZUltYWdlRGF0YShuZGFycmF5KTtcbiAgICB9IGVsc2UgaWYgKG5kYXJyYXkuc2hhcGVbMl0gPT09IDMpIHtcbiAgICAgIHRoaXMuZHJhd1JHQkltYWdlRGF0YShuZGFycmF5KTtcbiAgICB9XG4gIH1cblxuICBkcmF3UkdCSW1hZ2VEYXRhKG5kYXJyYXk6IEFycmF5M0QpIHtcbiAgICBsZXQgcGl4ZWxPZmZzZXQgPSAwO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmRhcnJheS5zaGFwZVswXTsgaSsrKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IG5kYXJyYXkuc2hhcGVbMV07IGorKykge1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gbmRhcnJheS5nZXQoaSwgaiwgMCk7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSBuZGFycmF5LmdldChpLCBqLCAxKTtcbiAgICAgICAgdGhpcy5pbWFnZURhdGEuZGF0YVtwaXhlbE9mZnNldCsrXSA9IG5kYXJyYXkuZ2V0KGksIGosIDIpO1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gMjU1O1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGRyYXdHcmF5c2NhbGVJbWFnZURhdGEobmRhcnJheTogQXJyYXkzRCkge1xuICAgIGxldCBwaXhlbE9mZnNldCA9IDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZGFycmF5LnNoYXBlWzBdOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgbmRhcnJheS5zaGFwZVsxXTsgaisrKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gbmRhcnJheS5nZXQoaSwgaiwgMCk7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSB2YWx1ZTtcbiAgICAgICAgdGhpcy5pbWFnZURhdGEuZGF0YVtwaXhlbE9mZnNldCsrXSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSAyNTU7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgZHJhdygpIHtcbiAgICB0aGlzLmNhbnZhcy5zdHlsZS5kaXNwbGF5ID0gJyc7XG4gICAgdGhpcy5jYW52YXNDb250ZXh0LnB1dEltYWdlRGF0YSh0aGlzLmltYWdlRGF0YSwgMCwgMCk7XG4gIH1cbn1cbmRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChcbiAgICBOREFycmF5SW1hZ2VWaXN1YWxpemVyLnByb3RvdHlwZS5pcywgTkRBcnJheUltYWdlVmlzdWFsaXplcik7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbi8qKlxuICogQGZpbGVvdmVydmlld1xuICpcbiAqIERlZmluZXMgYW4gaW50ZXJmYWNlIGZvciBjcmVhdGluZyBQb2x5bWVyIGVsZW1lbnRzIGluIFR5cGVzY3JpcHQgd2l0aCB0aGVcbiAqIGNvcnJlY3QgdHlwaW5ncy4gQSBQb2x5bWVyIGVsZW1lbnQgc2hvdWxkIGJlIGRlZmluZWQgbGlrZSB0aGlzOlxuICpcbiAqIGBgYFxuICogbGV0IE15RWxlbWVudFBvbHltZXIgPSBQb2x5bWVyRWxlbWVudCh7XG4gKiAgIGlzOiAnbXktcG9seW1lci1lbGVtZW50JyxcbiAqICAgcHJvcGVydGllczoge1xuICogICAgIGZvbzogc3RyaW5nLFxuICogICAgIGJhcjogQXJyYXlcbiAqICAgfVxuICogfSk7XG4gKlxuICogY2xhc3MgTXlFbGVtZW50IGV4dGVuZHMgTXlFbGVtZW50UG9seW1lciB7XG4gKiAgIGZvbzogc3RyaW5nO1xuICogICBiYXI6IG51bWJlcltdO1xuICpcbiAqICAgcmVhZHkoKSB7XG4gKiAgICAgY29uc29sZS5sb2coJ015RWxlbWVudCBpbml0aWFsaXplZCEnKTtcbiAqICAgfVxuICogfVxuICpcbiAqIGRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChNeUVsZW1lbnQucHJvdG90eXBlLmlzLCBNeUVsZW1lbnQpO1xuICogYGBgXG4gKi9cblxuZXhwb3J0IHR5cGUgU3BlYyA9IHtcbiAgaXM6IHN0cmluZzsgcHJvcGVydGllczoge1xuICAgIFtrZXk6IHN0cmluZ106IChGdW5jdGlvbnx7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICB0eXBlOiBGdW5jdGlvbiwgdmFsdWU/OiBhbnk7XG4gICAgICByZWZsZWN0VG9BdHRyaWJ1dGU/OiBib29sZWFuO1xuICAgICAgcmVhZG9ubHk/OiBib29sZWFuO1xuICAgICAgbm90aWZ5PzogYm9vbGVhbjtcbiAgICAgIGNvbXB1dGVkPzogc3RyaW5nO1xuICAgICAgb2JzZXJ2ZXI/OiBzdHJpbmc7XG4gICAgfSlcbiAgfTtcbiAgb2JzZXJ2ZXJzPzogc3RyaW5nW107XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gUG9seW1lckVsZW1lbnQoc3BlYzogU3BlYykge1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIHJldHVybiBQb2x5bWVyLkNsYXNzKHNwZWMgYXMgYW55KSBhcyB7bmV3ICgpOiBQb2x5bWVySFRNTEVsZW1lbnR9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFBvbHltZXJIVE1MRWxlbWVudCBleHRlbmRzIEhUTUxFbGVtZW50LCBwb2x5bWVyLkJhc2Uge31cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgaW50ZXJmYWNlIENoZWNrcG9pbnRWYXJpYWJsZSB7XG4gIGZpbGVuYW1lOiBzdHJpbmc7XG4gIHNoYXBlOiBudW1iZXJbXTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCB0eXBlIENoZWNrcG9pbnRNYW5pZmVzdCA9IHtcbiAgW3Zhck5hbWU6IHN0cmluZ106IENoZWNrcG9pbnRWYXJpYWJsZVxufTtcblxuY29uc3QgTUFOSUZFU1RfRklMRSA9ICdtYW5pZmVzdC5qc29uJztcblxuZXhwb3J0IGNsYXNzIENoZWNrcG9pbnRMb2FkZXIge1xuICBwcml2YXRlIGNoZWNrcG9pbnRNYW5pZmVzdDogQ2hlY2twb2ludE1hbmlmZXN0O1xuICBwcml2YXRlIHZhcmlhYmxlczoge1t2YXJOYW1lOiBzdHJpbmddOiBOREFycmF5fTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHVybFBhdGg6IHN0cmluZykge1xuICAgIGlmICh0aGlzLnVybFBhdGguY2hhckF0KHRoaXMudXJsUGF0aC5sZW5ndGggLSAxKSAhPT0gJy8nKSB7XG4gICAgICB0aGlzLnVybFBhdGggKz0gJy8nO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgbG9hZE1hbmlmZXN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICAgIHhoci5vcGVuKCdHRVQnLCB0aGlzLnVybFBhdGggKyBNQU5JRkVTVF9GSUxFKTtcblxuICAgICAgeGhyLm9ubG9hZCA9ICgpID0+IHtcbiAgICAgICAgdGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPSBKU09OLnBhcnNlKHhoci5yZXNwb25zZVRleHQpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9O1xuICAgICAgeGhyLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYCR7TUFOSUZFU1RfRklMRX0gbm90IGZvdW5kIGF0ICR7dGhpcy51cmxQYXRofS4gYCArIGVycm9yKTtcbiAgICAgIH07XG4gICAgICB4aHIuc2VuZCgpO1xuICAgIH0pO1xuICB9XG5cbiAgZ2V0Q2hlY2twb2ludE1hbmlmZXN0KCk6IFByb21pc2U8Q2hlY2twb2ludE1hbmlmZXN0PiB7XG4gICAgaWYgKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0ID09IG51bGwpIHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxDaGVja3BvaW50TWFuaWZlc3Q+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgdGhpcy5sb2FkTWFuaWZlc3QoKS50aGVuKCgpID0+IHtcbiAgICAgICAgICByZXNvbHZlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPENoZWNrcG9pbnRNYW5pZmVzdD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgcmVzb2x2ZSh0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRBbGxWYXJpYWJsZXMoKTogUHJvbWlzZTx7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9PiB7XG4gICAgaWYgKHRoaXMudmFyaWFibGVzICE9IG51bGwpIHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTx7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHJlc29sdmUodGhpcy52YXJpYWJsZXMpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMuZ2V0Q2hlY2twb2ludE1hbmlmZXN0KCkudGhlbihcbiAgICAgICAgICAoY2hlY2twb2ludERlZmluaXRpb246IENoZWNrcG9pbnRNYW5pZmVzdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmFyaWFibGVOYW1lcyA9IE9iamVjdC5rZXlzKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcblxuICAgICAgICAgICAgY29uc3QgdmFyaWFibGVQcm9taXNlczogQXJyYXk8UHJvbWlzZTxOREFycmF5Pj4gPSBbXTtcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFyaWFibGVOYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICB2YXJpYWJsZVByb21pc2VzLnB1c2godGhpcy5nZXRWYXJpYWJsZSh2YXJpYWJsZU5hbWVzW2ldKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFByb21pc2UuYWxsKHZhcmlhYmxlUHJvbWlzZXMpLnRoZW4odmFyaWFibGVzID0+IHtcbiAgICAgICAgICAgICAgdGhpcy52YXJpYWJsZXMgPSB7fTtcbiAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YXJpYWJsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLnZhcmlhYmxlc1t2YXJpYWJsZU5hbWVzW2ldXSA9IHZhcmlhYmxlc1tpXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICByZXNvbHZlKHRoaXMudmFyaWFibGVzKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgZ2V0VmFyaWFibGUodmFyTmFtZTogc3RyaW5nKTogUHJvbWlzZTxOREFycmF5PiB7XG4gICAgaWYgKCEodmFyTmFtZSBpbiB0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGxvYWQgbm9uLWV4aXN0YW50IHZhcmlhYmxlICcgKyB2YXJOYW1lKTtcbiAgICB9XG5cbiAgICBjb25zdCB2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kID1cbiAgICAgICAgKHJlc29sdmU6IChuZGFycmF5OiBOREFycmF5KSA9PiB2b2lkLCByZWplY3Q6ICgpID0+IHZvaWQpID0+IHtcbiAgICAgICAgICBjb25zdCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICAgICAgICB4aHIucmVzcG9uc2VUeXBlID0gJ2FycmF5YnVmZmVyJztcbiAgICAgICAgICBjb25zdCBmbmFtZSA9IHRoaXMuY2hlY2twb2ludE1hbmlmZXN0W3Zhck5hbWVdLmZpbGVuYW1lO1xuICAgICAgICAgIHhoci5vcGVuKCdHRVQnLCB0aGlzLnVybFBhdGggKyBmbmFtZSk7XG5cbiAgICAgICAgICB4aHIub25sb2FkID0gKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh4aHIucmVzcG9uc2UpO1xuICAgICAgICAgICAgY29uc3QgbmRhcnJheSA9XG4gICAgICAgICAgICAgICAgTkRBcnJheS5tYWtlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0W3Zhck5hbWVdLnNoYXBlLCB7dmFsdWVzfSk7XG4gICAgICAgICAgICByZXNvbHZlKG5kYXJyYXkpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgeGhyLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAnQ291bGQgbm90IGZldGNoIHZhcmlhYmxlICcgKyB2YXJOYW1lICsgJzogJyArIGVycm9yKTtcbiAgICAgICAgICB9O1xuICAgICAgICAgIHhoci5zZW5kKCk7XG4gICAgICAgIH07XG5cbiAgICBpZiAodGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPE5EQXJyYXk+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgdGhpcy5sb2FkTWFuaWZlc3QoKS50aGVuKCgpID0+IHtcbiAgICAgICAgICBuZXcgUHJvbWlzZTxOREFycmF5Pih2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kKS50aGVuKHJlc29sdmUpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFByb21pc2U8TkRBcnJheT4odmFyaWFibGVSZXF1ZXN0UHJvbWlzZU1ldGhvZCk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbmNvbnN0IFNUQVRTX1NBTVBMRV9QRVJDRU5UQUdFID0gMC4xO1xuXG5leHBvcnQgaW50ZXJmYWNlIERhdGFTdGF0cyB7XG4gIGV4YW1wbGVDb3VudDogbnVtYmVyO1xuICBpbnB1dE1pbjogbnVtYmVyO1xuICBpbnB1dE1heDogbnVtYmVyO1xuICBzaGFwZTogbnVtYmVyW107XG59XG5cbmludGVyZmFjZSBOb3JtYWxpemF0aW9uSW5mbyB7XG4gIGlzTm9ybWFsaXplZDogYm9vbGVhbjtcbiAgLy8gQm91bmRzIG9mIHRoZSBub3JtYWxpemF0aW9uIGlmIG5vcm1hbGl6ZWQuXG4gIGxvd2VyQm91bmQ/OiBudW1iZXI7XG4gIHVwcGVyQm91bmQ/OiBudW1iZXI7XG4gIC8vIE1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIGZvciBlYWNoIGRpbWVuc2lvbiBvZiB0aGUgb3JpZ2luYWwgZGF0YS4gVGhlc2VcbiAgLy8gYXJlIHRoZSBzYW1lIHNpemUgYXMgYW4gaW5wdXQgZXhhbXBsZS4gVGhlc2UgYXJlIGNvbXB1dGVkIGxhemlseSwgb25seSBpZlxuICAvLyBub3JtYWxpemF0aW9uIGlzIHJlcXVlc3RlZC4gSWYgdGhlIGRhdGEgaXMgdW4tbm9ybWFsaXplZCwgdGhlc2UgYXJlIGtlcHRcbiAgLy8gYXJvdW5kIHNvIHRoZXkgZG9uJ3QgaGF2ZSB0byBiZSByZWNvbXB1dGVkLlxuICBtaW5WYWx1ZXM6IEZsb2F0MzJBcnJheTtcbiAgbWF4VmFsdWVzOiBGbG9hdDMyQXJyYXk7XG59XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBJbk1lbW9yeURhdGFzZXQge1xuICBwcm90ZWN0ZWQgZGF0YXNldDogTkRBcnJheVtdW118bnVsbDtcblxuICAvLyBDb250YWlucyBpbmZvcm1hdGlvbiBuZWNlc3NhcnkgZm9yIHJlY29uc3RydWN0aW9uIG9mIHRoZSBvcmlnaW5hbCBkYXRhXG4gIC8vIGFmdGVyIG5vcm1hbGl6YXRpb24uXG4gIHByaXZhdGUgbm9ybWFsaXphdGlvbkluZm86IHtbZGF0YUluZGV4OiBudW1iZXJdOiBOb3JtYWxpemF0aW9uSW5mb307XG5cbiAgY29uc3RydWN0b3IocHJvdGVjdGVkIGRhdGFTaGFwZXM6IG51bWJlcltdW10pIHtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvID0ge307XG4gIH1cblxuICBnZXREYXRhU2hhcGUoZGF0YUluZGV4OiBudW1iZXIpOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YVNoYXBlc1tkYXRhSW5kZXhdO1xuICB9XG5cbiAgYWJzdHJhY3QgZmV0Y2hEYXRhKCk6IFByb21pc2U8dm9pZD47XG5cbiAgZ2V0RGF0YSgpOiBOREFycmF5W11bXXxudWxsIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhc2V0O1xuICB9XG5cbiAgZ2V0U3RhdHMoKTogRGF0YVN0YXRzW10ge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZGF0YXNldC5tYXAoZCA9PiB0aGlzLmdldFN0YXRzRm9yRGF0YShkKSk7XG4gIH1cblxuICAvLyBDb21wdXRlcyBzdGF0cyBhY3Jvc3MgYSBzYW1wbGVkIHBvcnRpb24gb2YgdGhlIGRhdGEuXG4gIHByaXZhdGUgZ2V0U3RhdHNGb3JEYXRhKGRhdGE6IE5EQXJyYXlbXSk6IERhdGFTdGF0cyB7XG4gICAgbGV0IGlucHV0TWluID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIGxldCBpbnB1dE1heCA9IE51bWJlci5ORUdBVElWRV9JTkZJTklUWTtcblxuICAgIGxldCBleGFtcGxlSW5kaWNlcyA9IGRhdGEubWFwKChleGFtcGxlLCBpKSA9PiBpKTtcbiAgICB1dGlsLnNodWZmbGUoZXhhbXBsZUluZGljZXMpO1xuICAgIGV4YW1wbGVJbmRpY2VzID1cbiAgICAgICAgZXhhbXBsZUluZGljZXMuc2xpY2UoZXhhbXBsZUluZGljZXMubGVuZ3RoICogU1RBVFNfU0FNUExFX1BFUkNFTlRBR0UpO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBleGFtcGxlSW5kaWNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaW5wdXRWYWx1ZXMgPSBkYXRhW2V4YW1wbGVJbmRpY2VzW2ldXS5nZXRWYWx1ZXMoKTtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgaW5wdXRWYWx1ZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgaW5wdXRNaW4gPSBNYXRoLm1pbihpbnB1dE1pbiwgaW5wdXRWYWx1ZXNbal0pO1xuICAgICAgICBpbnB1dE1heCA9IE1hdGgubWF4KGlucHV0TWF4LCBpbnB1dFZhbHVlc1tqXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlucHV0TWluLFxuICAgICAgaW5wdXRNYXgsXG4gICAgICBleGFtcGxlQ291bnQ6IGRhdGEubGVuZ3RoLFxuICAgICAgc2hhcGU6IGRhdGFbMF0uc2hhcGUsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0gZXhhbXBsZXMgTkRBcnJheXMgdG8gYmUgbm9ybWFsaXplZC5cbiAgICogQHBhcmFtIGN1ckxvd2VyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG1pbmltdW0gdmFsdWUgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uIG9yIGEgZml4ZWQgbWluaW11bSB2YWx1ZS5cbiAgICogQHBhcmFtIGN1clVwcGVyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG1heGltdW0gdmFsdWUgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uIG9yIGEgZml4ZWQgbWF4aW11bSB2YWx1ZS5cbiAgICogQHBhcmFtIG5ld0xvd2VyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgbmV3IG1pbmltdW0gdmFsdWVzIGZvciBlYWNoXG4gICAqIGRpbWVuc2lvbiwgb3IgYSBmaXhlZCBtaW51bXVtIHZhbHVlIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSB0by5cbiAgICogQHBhcmFtIG5ld1VwcGVyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgbmV3IG1heGltdW0gdmFsdWVzIGZvciBlYWNoXG4gICAqIGRpbWVuc2lvbiwgb3IgYSBmaXhlZCBtYXhpbXVtIHZhbHVlIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSB0by5cbiAgICovXG4gIHByaXZhdGUgbm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgZXhhbXBsZXM6IE5EQXJyYXlbXSwgY3VyTG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXIsXG4gICAgICBjdXJVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlciwgbmV3TG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXIsXG4gICAgICBuZXdVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcik6IE5EQXJyYXlbXSB7XG4gICAgY29uc3QgY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gPVxuICAgICAgICAoY3VyVXBwZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkgJiZcbiAgICAgICAgIGN1ckxvd2VyQm91bmRzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KTtcbiAgICBjb25zdCBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA9XG4gICAgICAgIChuZXdMb3dlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSAmJlxuICAgICAgICAgbmV3VXBwZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpO1xuXG4gICAgY29uc3QgaW5wdXRTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKGV4YW1wbGVzWzBdLnNoYXBlKTtcbiAgICBjb25zdCBuZXdFeGFtcGxlczogTkRBcnJheVtdID0gW107XG5cbiAgICBleGFtcGxlcy5mb3JFYWNoKGV4YW1wbGUgPT4ge1xuICAgICAgY29uc3QgaW5wdXRWYWx1ZXMgPSBleGFtcGxlLmdldFZhbHVlcygpO1xuICAgICAgY29uc3Qgbm9ybWFsaXplZFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoaW5wdXRTaXplKTtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgaW5wdXRTaXplOyBqKyspIHtcbiAgICAgICAgY29uc3QgY3VyTG93ZXJCb3VuZCA9IGN1ckJvdW5kc0lzUGVyRGltZW5zaW9uID9cbiAgICAgICAgICAgIChjdXJMb3dlckJvdW5kcyBhcyBGbG9hdDMyQXJyYXkpW2pdIDpcbiAgICAgICAgICAgIGN1ckxvd2VyQm91bmRzIGFzIG51bWJlcjtcbiAgICAgICAgY29uc3QgY3VyVXBwZXJCb3VuZCA9IGN1ckJvdW5kc0lzUGVyRGltZW5zaW9uID9cbiAgICAgICAgICAgIChjdXJVcHBlckJvdW5kcyBhcyBGbG9hdDMyQXJyYXkpW2pdIDpcbiAgICAgICAgICAgIGN1clVwcGVyQm91bmRzIGFzIG51bWJlcjtcbiAgICAgICAgY29uc3QgY3VyUmFuZ2UgPSBjdXJVcHBlckJvdW5kIC0gY3VyTG93ZXJCb3VuZDtcblxuICAgICAgICBjb25zdCBuZXdMb3dlckJvdW5kID0gbmV3Qm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKG5ld0xvd2VyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgbmV3TG93ZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBuZXdVcHBlckJvdW5kID0gbmV3Qm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKG5ld1VwcGVyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgbmV3VXBwZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBuZXdSYW5nZSA9IG5ld1VwcGVyQm91bmQgLSBuZXdMb3dlckJvdW5kO1xuXG4gICAgICAgIGlmIChjdXJSYW5nZSA9PT0gMCkge1xuICAgICAgICAgIG5vcm1hbGl6ZWRWYWx1ZXNbal0gPSBuZXdMb3dlckJvdW5kO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG5vcm1hbGl6ZWRWYWx1ZXNbal0gPSBuZXdMb3dlckJvdW5kICtcbiAgICAgICAgICAgICAgbmV3UmFuZ2UgKiAoaW5wdXRWYWx1ZXNbal0gLSBjdXJMb3dlckJvdW5kKSAvIGN1clJhbmdlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBuZXdFeGFtcGxlcy5wdXNoKE5EQXJyYXkubWFrZShleGFtcGxlLnNoYXBlLCB7dmFsdWVzOiBub3JtYWxpemVkVmFsdWVzfSkpO1xuICAgIH0pO1xuICAgIHJldHVybiBuZXdFeGFtcGxlcztcbiAgfVxuXG4gIHByaXZhdGUgY29tcHV0ZUJvdW5kcyhkYXRhSW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuXG4gICAgY29uc3Qgc2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLmRhdGFzZXRbZGF0YUluZGV4XVswXS5zaGFwZSk7XG5cbiAgICAvLyBDb21wdXRlIG1pbiBhbmQgbWF4IHZhbHVlcyBmb3IgZXZlcnkgZGltZW5zaW9uLlxuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XSA9IHtcbiAgICAgIGlzTm9ybWFsaXplZDogZmFsc2UsXG4gICAgICBtaW5WYWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSksXG4gICAgICBtYXhWYWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSlcbiAgICB9O1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNbaV0gPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWF4VmFsdWVzW2ldID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIH1cblxuICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdLmZvckVhY2goZXhhbXBsZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGV4YW1wbGUuZ2V0VmFsdWVzKCk7XG4gICAgICBmb3IgKGxldCBrID0gMDsgayA8IHNpemU7IGsrKykge1xuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzW2tdID0gTWF0aC5taW4oXG4gICAgICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzW2tdLCBpbnB1dFZhbHVlc1trXSk7XG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNba10gPSBNYXRoLm1heChcbiAgICAgICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNba10sIGlucHV0VmFsdWVzW2tdKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG5vcm1hbGl6ZVdpdGhpbkJvdW5kcyhcbiAgICAgIGRhdGFJbmRleDogbnVtYmVyLCBsb3dlckJvdW5kOiBudW1iZXIsIHVwcGVyQm91bmQ6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuICAgIGlmIChkYXRhSW5kZXggPj0gdGhpcy5kYXRhc2V0Lmxlbmd0aCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdkYXRhSW5kZXggb3V0IG9mIGJvdW5kcy4nKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdID09IG51bGwpIHtcbiAgICAgIHRoaXMuY29tcHV0ZUJvdW5kcyhkYXRhSW5kZXgpO1xuICAgIH1cblxuICAgIC8vIGN1ckxvd2VyL1VwcGVyQm91bmRzIG9mIHRoZSBjdXJyZW50IGRhdGEgc2V0IGNhbiBlaXRoZXIgYmUgZml4ZWQgbnVtYmVyc1xuICAgIC8vIGlmIHRoZSBkYXRhIGhhcyBhbHJlYWR5IGJlZW4gbm9ybWFsaXplZCwgb3IgY3VyTG93ZXIvVXBwZXIgZm9yIGVhY2hcbiAgICAvLyBkaW1lbnNpb24gaWYgaXQgaGFzbid0IGJlZW4gbm9ybWFsaXplZCB5ZXQuXG4gICAgbGV0IGN1ckxvd2VyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyO1xuICAgIGxldCBjdXJVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcjtcblxuICAgIGlmICh0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkKSB7XG4gICAgICBjdXJMb3dlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5sb3dlckJvdW5kITtcbiAgICAgIGN1clVwcGVyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQhO1xuICAgIH0gZWxzZSB7XG4gICAgICBjdXJMb3dlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXM7XG4gICAgICBjdXJVcHBlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXM7XG4gICAgfVxuXG4gICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0gPSB0aGlzLm5vcm1hbGl6ZUV4YW1wbGVzVG9SYW5nZShcbiAgICAgICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0sIGN1ckxvd2VyQm91bmRzLCBjdXJVcHBlckJvdW5kcywgbG93ZXJCb3VuZCxcbiAgICAgICAgdXBwZXJCb3VuZCk7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmlzTm9ybWFsaXplZCA9IHRydWU7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQgPSBsb3dlckJvdW5kO1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS51cHBlckJvdW5kID0gdXBwZXJCb3VuZDtcbiAgfVxuXG4gIHByaXZhdGUgaXNOb3JtYWxpemVkKGRhdGFJbmRleDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubm9ybWFsaXphdGlvbkluZm8gIT0gbnVsbCAmJlxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkO1xuICB9XG5cbiAgcmVtb3ZlTm9ybWFsaXphdGlvbihkYXRhSW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcmFpbmluZyBvciB0ZXN0IGRhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuaXNOb3JtYWxpemVkKGRhdGFJbmRleCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSA9IHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSwgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXMsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXMpO1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQgPSBmYWxzZTtcbiAgfVxuXG4gIHVubm9ybWFsaXplRXhhbXBsZXMoZXhhbXBsZXM6IE5EQXJyYXlbXSwgZGF0YUluZGV4OiBudW1iZXIpOiBOREFycmF5W10ge1xuICAgIGlmICghdGhpcy5pc05vcm1hbGl6ZWQoZGF0YUluZGV4KSkge1xuICAgICAgcmV0dXJuIGV4YW1wbGVzO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLm5vcm1hbGl6ZUV4YW1wbGVzVG9SYW5nZShcbiAgICAgICAgZXhhbXBsZXMsIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5sb3dlckJvdW5kISxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWF4VmFsdWVzKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmRhdGFzZXQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5kYXRhc2V0W2ldLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHRoaXMuZGF0YXNldFtpXVtqXS5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZGF0YXNldCA9IFtdO1xuICB9XG59XG5cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHcmFwaExheWVyc30gZnJvbSAnLi9ncmFwaF9sYXllcnMnO1xuaW1wb3J0ICogYXMgY29uY2F0M2RfdXRpbCBmcm9tICcuL21hdGgvY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi9tYXRoL2NvbnZfdXRpbCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEdyYXBoIGlzIHRoZSBwcmltYXJ5IGNvbnRhaW5lciBzdHJ1Y3R1cmUgZm9yIGxlYXJuLmpzIG9wZXJhdGlvbnMuIEdyYXBoXG4gKiBob2xkcyB0aGUgdG9wb2xvZ3kgb2Ygb3BlcmF0aW9uIG5vZGVzIGFuZCB0aGUgY29ubmVjdGl2aXR5IGJldHdlZW4gdGhlbS5cbiAqL1xuZXhwb3J0IGNsYXNzIEdyYXBoIHtcbiAgbGF5ZXJzOiBHcmFwaExheWVycztcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmxheWVycyA9IG5ldyBHcmFwaExheWVycyh0aGlzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmFtZWQgdmFyaWFibGUuIFZhcmlhYmxlcyBhcmUgdGVuc29ycyB0aGF0IG1haW50YWluIHN0YXRlIGFjcm9zc1xuICAgKiBzZXNzaW9uIGNhbGxzIGFuZCB3aG9zZSB2YWx1ZXMgYXJlIGFkanVzdGVkIGR1cmluZyBiYWNrcHJvcGFnYXRpb25cbiAgICogdHJhaW5pbmcuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgdmFyaWFibGUuXG4gICAqIEBwYXJhbSBkYXRhIFRoZSBOREFycmF5IHRvIGFzc29jaWF0ZSB3aXRoIHRoaXMgdmFyaWFibGUgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSB2YXJpYWJsZS5cbiAgICovXG4gIHZhcmlhYmxlKG5hbWU6IHN0cmluZywgZGF0YTogTkRBcnJheSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgVmFyaWFibGVOb2RlKHRoaXMsIG5hbWUsIGRhdGEpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnNlcnRzIGEgcGxhY2Vob2xkZXIgZm9yIGEgdGVuc29yIHRoYXQgd2lsbCBiZSBhbHdheXMgZmVkLiBQbGFjZWhvbGRlcnNcbiAgICogYXJlIGlucHV0IHRlbnNvcnMgd2hvc2UgdmFsdWVzIGFyZSBwcm92aWRlZCBieSB0aGUgY2xpZW50IHZpYSBmZWVkXG4gICAqIGRpY3Rpb25hcmllcy4gUGxhY2Vob2xkZXJzIGFyZSBub3QgdXBkYXRlZCBhcyBwYXJ0IG9mIHRyYWluaW5nOyB0aGV5IGFyZVxuICAgKiBvbmx5IHVzZWQgYXMgaW1tdXRhYmxlIGlucHV0LlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGlzIHBsYWNlaG9sZGVyLlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBwbGFjZWhvbGRlciB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHBsYWNlaG9sZGVyLlxuICAgKi9cbiAgcGxhY2Vob2xkZXIobmFtZTogc3RyaW5nLCBzaGFwZTogbnVtYmVyW10pOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFBsYWNlaG9sZGVyTm9kZSh0aGlzLCBuYW1lLCBzaGFwZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnN0YW50IHZhbHVlIHRoYXQgcGVyc2lzdHMgYWNyb3NzIHNlc3Npb24gY2FsbHMuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgdmFsdWUgdG8gcmV0dXJuLlxuICAgKiBAcmV0dXJuIEEgbm9kZSBvdXRwdXRpbmcgdGhlIGNvbnN0YW50IHZhbHVlLlxuICAgKi9cbiAgY29uc3RhbnQodmFsdWU6IEFycmF5RGF0YSk6IFRlbnNvciB7XG4gICAgbGV0IGZpbmFsVmFsdWU6IE5EQXJyYXk7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIGZpbmFsVmFsdWUgPSBTY2FsYXIubmV3KHZhbHVlKTtcbiAgICB9IGVsc2UgaWYgKHZhbHVlIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgZmluYWxWYWx1ZSA9IHZhbHVlO1xuICAgIH0gZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgY29uc3QgdmFscyA9IG5ldyBGbG9hdDMyQXJyYXkodXRpbC5mbGF0dGVuKHZhbHVlKSk7XG4gICAgICBmaW5hbFZhbHVlID0gTkRBcnJheS5tYWtlKHV0aWwuaW5mZXJTaGFwZSh2YWx1ZSksIHt2YWx1ZXM6IHZhbHN9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd1bmltcGxlbWVudGVkIGNvbnN0YW50IHR5cGUuJyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbnN0YW50Tm9kZSh0aGlzLCBmaW5hbFZhbHVlKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVzaGFwZSB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIGJlIHJlc2hhcGVkLlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBvdXRwdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSByZXNoYXBlIG9wZXJhdGlvbi5cbiAgICovXG4gIHJlc2hhcGUoeDogVGVuc29yLCBzaGFwZTogbnVtYmVyW10pOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBSZXNoYXBlTm9kZSh0aGlzLCAnUmVzaGFwZScsIHgsIHNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBmdXNlZCBsaW5lYXIgY29tYmluYXRpb24gb2YgdHdvIHRlbnNvcnMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuIFNhbWUgc2hhcGUgYXMgdDEuXG4gICAqIEBwYXJhbSBjMSBDb2VmZmljaWVudCBvZiB0MS4gTXVzdCBiZSBzaXplIDEuXG4gICAqIEBwYXJhbSBjMiBDb2VmZmljaWVudCBvZiB0Mi4gTXVzdCBiZSBzaXplIDEuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgYzEqdDErYzIqdDIuXG4gICAqL1xuICBmdXNlZExpbmVhckNvbWJpbmF0aW9uKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IsIGMxOiBUZW5zb3IsIGMyOiBUZW5zb3IpOlxuICAgICAgVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUodGhpcywgeDEsIHgyLCBjMSwgYzIpKTtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEFkZHMgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxK3QyLlxuICAgKi9cbiAgYWRkKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFkZE5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogU3VidHJhY3RzIHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MS10Mi5cbiAgICovXG4gIHN1YnRyYWN0KHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFN1YnRyYWN0Tm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNdWx0aXBseSB0d28gdGVuc29ycyAoZWxlbWVudHdpc2UpLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdDEqdDIuXG4gICAqL1xuICBtdWx0aXBseSh4MTogVGVuc29yLCB4MjogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBNdWx0aXBseU5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogRGl2aWRlIHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MSAvIHQyLlxuICAgKi9cbiAgZGl2aWRlKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IERpdmlkZU5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHN1bSBvZiBlbGVtZW50cyBpbiB0aGUgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yLlxuICAgKi9cbiAgcmVkdWNlU3VtKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUmVkdWNlU3VtTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uY2F0cyB0d28gM0QgdGVuc29ycyBhbG9uZyBhIGdpdmVuIGF4aXMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgY29uY2F0IG9mIHR3byB0ZW5zb3JzIGFsb25nIGF4aXMuXG4gICAqL1xuICBjb25jYXQzZCh4MTogVGVuc29yLCB4MjogVGVuc29yLCBheGlzOiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbmNhdDNETm9kZSh0aGlzLCB4MSwgeDIsIGF4aXMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3QgYmV0d2VlbiB0d28gbWF0cmljZXMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGRvdCBwcm9kdWN0IG9mIHgxIGFuZCB4Mi5cbiAgICovXG4gIG1hdG11bCh4MTogVGVuc29yLCB4MjogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBNYXRNdWxOb2RlKHRoaXMsIHgxLCB4MikpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgMkQgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHcgVGhlIHdlaWdodCB0ZW5zb3IgdXNlZCBieSB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYiBUaGUgYmlhcyB0ZW5zb3IgdXNlZCBieSB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBjb252b2x1dGlvbmFsIGtlcm5lbC5cbiAgICogQHBhcmFtIG91dHB1dERlcHRoIFRoZSBvdXRwdXQgZGVwdGggb2YgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSBhbW91bnQgb2YgemVybyBwYWRkaW5nIG9uIGFsbCBzaWRlcyBvZiB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqL1xuICBjb252MmQoXG4gICAgICB4OiBUZW5zb3IsIHc6IFRlbnNvciwgYjogVGVuc29yLCBmaWVsZFNpemU6IG51bWJlciwgb3V0cHV0RGVwdGg6IG51bWJlcixcbiAgICAgIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbnZvbHV0aW9uMkROb2RlKFxuICAgICAgICB0aGlzLCB4LCB3LCBiLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIDJEIG1heCBwb29sIG9mIHguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIG1heCBwb29sIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIGZpZWxkU2l6ZSBUaGUgc2l6ZSBvZiB0aGUgY29udm9sdXRpb25hbCBrZXJuZWwuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gemVyb1BhZCBUaGUgYW1vdW50IG9mIHplcm8gcGFkZGluZyBvbiBhbGwgc2lkZXMgb2YgdGhlIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbWF4IHBvb2wgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWF4UG9vbCh4OiBUZW5zb3IsIGZpZWxkU2l6ZTogbnVtYmVyLCBzdHJpZGUgPSAxLCB6ZXJvUGFkPzogbnVtYmVyKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgTWF4UG9vbE5vZGUodGhpcywgeCwgZmllbGRTaXplLCBzdHJpZGUsIHplcm9QYWQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBleHBvbmVudGlhbCBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgZXhwLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBlIF4geCBvcGVyYXRpb24uXG4gICAqL1xuICBleHAoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBFeHBOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBsb2cgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIGxvZy5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbG4oeCkgb3BlcmF0aW9uLlxuICAgKi9cbiAgbG9nKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgTG9nTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgUmVMVSBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgUmVMVS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgUmVMVSBvcGVyYXRpb24uXG4gICAqL1xuICByZWx1KHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUmVMVU5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIFRhbkggb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIFRhbkguXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIFRhbkggb3BlcmF0aW9uLlxuICAgKi9cbiAgdGFuaCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFRhbkhOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBTaWdtb2lkIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBzaWdtb2lkLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBzaWdtb2lkIG9wZXJhdGlvbi5cbiAgICovXG4gIHNpZ21vaWQoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTaWdtb2lkTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgc3F1YXJlIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBzcXVhcmUuXG4gICAqL1xuICBzcXVhcmUoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTcXVhcmVOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzb2Z0bWF4IHByb2JhYmlsaXRpZXMgZnJvbSBsb2dpdHMuXG4gICAqXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBsb2dpdHMuXG4gICAqIEByZXR1cm4gVGhlIHNvZnRtYXggcHJvYmFiaWxpdGllcy5cbiAgICovXG4gIHNvZnRtYXgoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTb2Z0bWF4Tm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gY2xhc3NpZnkuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvbi5cbiAgICovXG4gIHNvZnRtYXhDcm9zc0VudHJvcHlDb3N0KHg6IFRlbnNvciwgdGFyZ2V0OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUodGhpcywgeCwgdGFyZ2V0KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG1lYW4tc3F1YXJlZCBjb3N0IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSBsYWJlbCBUaGUgbGFiZWwgdGVuc29yLlxuICAgKiBAcGFyYW0gcHJlZGljdGlvbiBUaGUgcHJlZGljdGlvbiB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIG1lYW4tc3F1YXJlZCBjb3N0IG9wZXJhdGlvbi5cbiAgICovXG4gIG1lYW5TcXVhcmVkQ29zdChsYWJlbDogVGVuc29yLCBwcmVkaWN0aW9uOiBUZW5zb3IpIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgTWVhblNxdWFyZWRDb3N0Tm9kZSh0aGlzLCBsYWJlbCwgcHJlZGljdGlvbikpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWF4aW11bSBlbnRyeSBpbiB0aGUgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgdGVuc29yIHdpdGggdGhlIHZhbHVlLlxuICAgKiBAcmV0dXJuIEEgU2NhbGFyIHRlbnNvciB3aXRoIHRoZSBpbmRleCBvZiB0aGUgbWF4aW11bSBlbnRyeS5cbiAgICovXG4gIGFyZ21heCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFyZ01heE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYW4gYXJnbWF4IGVxdWFscyBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICAgKiBAcGFyYW0geDEgRmlyc3QgaW5wdXQgdGVuc29yIHRvIGNoZWNrIGFnYWluc3QuXG4gICAqIEBwYXJhbSB4MiBTZWNvbmQgaW5wdXQgdGVuc29yIHRvIGNoZWNrIGFnYWluc3QuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGFyZ21heCBlcXVhbHMgb3BlcmF0aW9uLlxuICAgKi9cbiAgYXJnbWF4RXF1YWxzKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFyZ01heEVxdWFsc05vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICBwcml2YXRlIGFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobm9kZTogTm9kZSk6IFRlbnNvciB7XG4gICAgdGhpcy5ub2Rlcy5wdXNoKG5vZGUpO1xuICAgIG5vZGUudmFsaWRhdGUoKTtcbiAgICByZXR1cm4gbm9kZS5vdXRwdXQ7XG4gIH1cblxuICBnZXROb2RlcygpOiBOb2RlW10ge1xuICAgIHJldHVybiB0aGlzLm5vZGVzO1xuICB9XG5cbiAgcHJpdmF0ZSBub2RlczogTm9kZVtdID0gW107XG59XG5cbi8qKlxuICogVGVuc29yIHJlcHJlc2VudHMgdGhlIG91dHB1dCBvZiBhbiBvcGVyYXRpb24gbm9kZSBpbiB0aGUgZ3JhcGguXG4gKiBUZW5zb3JzIGhhdmUgbm8gZGF0YSBhc3NvY2lhdGVkIHdpdGggdGhlbSwgYnV0IG1haW50YWluIGEgc2hhcGUgYXJyYXlcbiAqIHRvIGRldGVybWluZSBvcGVyYXRpb24gY29tcGF0aWJpbGl0eS4gQWxsIGdyYXBoIG1ldGhvZHMgdGhhdCBjcmVhdGUgZ3JhcGhcbiAqIG9wZXJhdGlvbnMgcmV0dXJuIFRlbnNvciBvYmplY3RzLCB3aGljaCBjYW4gYmUgdGhvdWdodCBvZiBhcyAnaGFuZGxlcycgdG9cbiAqIG9wZXJhdGlvbnMuXG4gKi9cbmV4cG9ydCBjbGFzcyBUZW5zb3Ige1xuICBub2RlOiBOb2RlO1xuICBpZDogbnVtYmVyO1xuICAvKipcbiAgICogQHBhcmFtIHNoYXBlIFRoZSBzaGFwZSBvZiB0aGlzIHRlbnNvciwgaW4gZGltZW5zaW9uIHNpemVzLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHVibGljIHNoYXBlOiBudW1iZXJbXSkge1xuICAgIHRoaXMuaWQgPSBUZW5zb3IubmV4dElEKys7XG4gIH1cbiAgcHJpdmF0ZSBzdGF0aWMgbmV4dElEID0gMDtcbn1cblxuLyoqXG4gKiBOb2RlIGlzIHRoZSBjb25jcmV0ZSBiYXNlIGNsYXNzIGZvciBhbGwgb3BlcmF0aW9ucyBpbiB0aGUgZ3JhcGguXG4gKiBVc2VycyBnZW5lcmFsbHkgZG9uJ3QgbmVlZCB0byBpbnRlcmFjdCBkaXJlY3RseSB3aXRoIE5vZGUgaW5zdGFuY2VzLCBidXQgdGhleVxuICogYXJlIHByb3ZpZGVkIGZvciBpbmZvcm1hdGlvbmFsIGFuZCBpbnRyb3NwZWN0aW9uIHB1cnBvc2VzLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE5vZGUge1xuICAvKipcbiAgICogQHBhcmFtIGdyYXBoIFRoZSBncmFwaCBjb250YWluaW5nIHRoaXMgbm9kZVxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGlzIG5vZGVcbiAgICogQHBhcmFtIGlucHV0cyBBIGRpY3Rpb25hcnkgb2YgbmFtZWQgVGVuc29ycyB0aGF0IGNvbXByaXNlIHRoaXMgbm9kZSdzXG4gICAqIGlucHV0cy5cbiAgICogQHBhcmFtIG91dHB1dCBUaGlzIG5vZGUncyBvdXRwdXQgVGVuc29yXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHB1YmxpYyBncmFwaDogR3JhcGgsIHB1YmxpYyBuYW1lOiBzdHJpbmcsXG4gICAgICBwdWJsaWMgaW5wdXRzOiB7W25hbWU6IHN0cmluZ106IFRlbnNvcn0sIHB1YmxpYyBvdXRwdXQ6IFRlbnNvcikge1xuICAgIHRoaXMuaWQgPSBOb2RlLm5leHRJRCsrO1xuICAgIG91dHB1dC5ub2RlID0gdGhpcztcbiAgfVxuICBhYnN0cmFjdCB2YWxpZGF0ZSgpOiB2b2lkO1xuICBpZDogbnVtYmVyO1xuICBwcml2YXRlIHN0YXRpYyBuZXh0SUQgPSAwO1xufVxuXG4vKipcbiAqIFZhcmlhYmxlTm9kZSByZXByZXNlbnRzIGEgdmFyaWFibGUsIGEgdXNlci1wcm92aWRlZCBOREFycmF5IHRoYXQnc1xuICogYWRqdXN0ZWQgZHVyaW5nIGJhY2twcm9wYWdhdGlvbiB0cmFpbmluZy5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBWYXJpYWJsZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBuYW1lOiBzdHJpbmcsIHB1YmxpYyBkYXRhOiBOREFycmF5KSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt9LCBuZXcgVGVuc29yKGRhdGEuc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5kYXRhICE9IG51bGwsXG4gICAgICAgICdFcnJvciBhZGRpbmcgdmFyaWFibGUgb3A6IERhdGEgZm9yIHZhcmlhYmxlIFxcJycgKyB0aGlzLm5hbWUgK1xuICAgICAgICAgICAgJ1xcJyBpcyBudWxsIG9yIHVuZGVmaW5lZCcpO1xuICB9XG59XG5cbi8qKlxuICogUGxhY2Vob2xkZXJOb2RlIHJlcHJlc2VudHMgYSBwbGFjZWhvbGRlciwgYSB1c2VyLXByb3ZpZGVkIE5EQXJyYXlcbiAqIHRoYXQncyB1c2VkIGFzIGltbXV0YWJsZSBpbnB1dCBkdXJpbmcgaW5mZXJlbmNlIGFuZCB0cmFpbmluZy5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBQbGFjZWhvbGRlck5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBuYW1lOiBzdHJpbmcsIHNoYXBlOiBudW1iZXJbXSkge1xuICAgIHN1cGVyKGdyYXBoLCBuYW1lLCB7fSwgbmV3IFRlbnNvcihzaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBDb25zdGFudE5vZGUgcmVwcmVzZW50cyBhIGNvbnN0YW50IHZhbHVlIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25zdGFudE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwdWJsaWMgZGF0YTogTkRBcnJheSkge1xuICAgIHN1cGVyKGdyYXBoLCAnQ29uc3RhbnQnLCB7fSwgbmV3IFRlbnNvcihkYXRhLnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuZGF0YSAhPSBudWxsLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnN0YW50OiBkYXRhIGZvciBwbGFjZWhvbGRlciBcXCcnICsgdGhpcy5uYW1lICtcbiAgICAgICAgICAgICdcXCcgaXMgbnVsbCBvciB1bmRlZmluZWQnKTtcbiAgfVxufVxuXG4vKipcbiAqIFJlc2hhcGVOb2RlIHJlcHJlc2VudHMgYSByZXNoYXBlIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVzaGFwZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwdWJsaWMgbmFtZTogc3RyaW5nLCBwcml2YXRlIHg6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt4fSwgbmV3IFRlbnNvcihzaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIGNvbnN0IHhTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMueC5zaGFwZSk7XG4gICAgY29uc3Qgc2hhcGVTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMuc2hhcGUpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4U2l6ZSA9PT0gc2hhcGVTaXplLFxuICAgICAgICAnRXJyb3IgbWFraW5nIHJlc2hhcGUgb3BlcmF0aW9uOiBpbnB1dCBUZW5zb3IgdG8gcmVzaGFwZSBcXCcnICtcbiAgICAgICAgICAgIHRoaXMubmFtZSArICdcXCcgb2Ygc2hhcGUgKCcgKyB0aGlzLnguc2hhcGUgK1xuICAgICAgICAgICAgJykgZG9lcyBub3QgbWF0Y2ggc2l6ZSBvZiByZXF1ZXN0ZWQgc2hhcGUgJyArIHRoaXMuc2hhcGUgKyAnLicpO1xuICB9XG59XG5cbi8qKlxuICogTGluZWFyQ29tYmluYXRpb25Ob2RlIHJlcHJlc2VudHMgYSBsaW5lYXIgY29tYmluYXRpb24gb2YgdHdvIHRlbnNvcnMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgVDEgPSAndDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgVDIgPSAndDInO1xuICBzdGF0aWMgcmVhZG9ubHkgQzEgPSAnYzEnO1xuICBzdGF0aWMgcmVhZG9ubHkgQzIgPSAnYzInO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IsIHByaXZhdGUgYzE6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgYzI6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnTGluZWFyIENvbWJpbmF0aW9uJywge3QxLCB0MiwgYzEsIGMyfSwgbmV3IFRlbnNvcih0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKTtcbiAgICBpZiAoIXV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLmMxLnNoYXBlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgZnVzZWRMaW5lYXJDb21iaW5hdGlvbjogYzEgaXMgbm90IGEgc2NhbGFyLCBnb3QgJyArXG4gICAgICAgICAgJ3NoYXBlOiAnICsgdGhpcy5jMS5zaGFwZSk7XG4gICAgfVxuICAgIGlmICghdXRpbC5pc1NjYWxhclNoYXBlKHRoaXMuYzIuc2hhcGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBmdXNlZExpbmVhckNvbWJpbmF0aW9uOiBjMiBpcyBub3QgYSBzY2FsYXIsIGdvdCAnICtcbiAgICAgICAgICAnc2hhcGU6ICcgKyB0aGlzLmMyLnNoYXBlKTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBZGROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0FkZCcsIHt0MSwgdDJ9LFxuICAgICAgICBuZXcgVGVuc29yKHV0aWwuc2l6ZUZyb21TaGFwZSh0MS5zaGFwZSkgPT09IDEgPyB0Mi5zaGFwZSA6IHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50Mi5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy50MS5zaGFwZSwgdGhpcy50Mi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgYWRkIG9wZXJhdGlvbiBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3VidHJhY3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ1N1YnRyYWN0Jywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBzdWJ0cmFjdCBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTXVsdGlwbHlOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ011bHRpcGx5Jywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBtdWx0aXBseSBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRGl2aWRlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgVDEgPSAndDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgVDIgPSAndDInO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdEaXZpZGUnLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGRpdmlkZSBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVkdWNlU3VtTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnUmVkdWNlU3VtJywge3h9LCBuZXcgVGVuc29yKFtdKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogQ29uY2F0M0ROb2RlIHJlcHJlc2VudHMgYSAzRCBjb25jYXRlbmF0aW9uIG9mIHR3byB0ZW5zb3JzIGFsb25nIGFuIGF4aXMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25jYXQzRE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFgxID0gJ3gxJztcbiAgc3RhdGljIHJlYWRvbmx5IFgyID0gJ3gyJztcbiAgc3RhdGljIHJlYWRvbmx5IEFYSVMgPSAnYXhpcyc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHgxOiBUZW5zb3IsIHByaXZhdGUgeDI6IFRlbnNvcixcbiAgICAgIHB1YmxpYyBheGlzOiBudW1iZXIpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdDb25jYXQzRCcsIHt4MSwgeDJ9LFxuICAgICAgICBuZXcgVGVuc29yKGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoXG4gICAgICAgICAgICB4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgY29uY2F0M2RfdXRpbC5hc3NlcnRDb25jYXQzRFNoYXBlc01hdGNoKFxuICAgICAgICB0aGlzLngxLnNoYXBlLCB0aGlzLngyLnNoYXBlLCB0aGlzLmF4aXMpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGdldE1hdE11bE91dHB1dFNoYXBlKHgxU2hhcGU6IG51bWJlcltdLCB4MlNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgaWYgKHgxU2hhcGUubGVuZ3RoID09PSAxICYmIHgyU2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgcmV0dXJuIFsxXTtcbiAgfSBlbHNlIGlmICh4MVNoYXBlLmxlbmd0aCA9PT0gMSAmJiB4MlNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgIHJldHVybiBbeDJTaGFwZVsxXV07XG4gIH0gZWxzZSBpZiAoeDFTaGFwZS5sZW5ndGggPT09IDIgJiYgeDJTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gW3gxU2hhcGVbMF1dO1xuICB9XG4gIHJldHVybiBbeDFTaGFwZVswXSwgeDJTaGFwZVsxXV07XG59XG5cbi8qKlxuICogTWF0TXVsTm9kZSByZXByZXNlbnRzIGEgZnVsbHkgY29ubmVjdGVkIGxheWVyIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1hdE11bE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFgxID0gJ3gxJztcbiAgc3RhdGljIHJlYWRvbmx5IFgyID0gJ3gyJztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHgxOiBUZW5zb3IsIHByaXZhdGUgeDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ01hdE11bCcsIHt4MSwgeDJ9LFxuICAgICAgICBuZXcgVGVuc29yKGdldE1hdE11bE91dHB1dFNoYXBlKHgxLnNoYXBlLCB4Mi5zaGFwZSkpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIGlmICh0aGlzLngxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB0aGlzLngyLnNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy54MS5zaGFwZVsxXSA9PT0gdGhpcy54Mi5zaGFwZVswXSxcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIG1hdG11bCBvcDogaW5uZXIgc2hhcGVzIG9mIG1hdHJpY2VzIHdpdGggc2hhcGVzICcgK1xuICAgICAgICAgICAgICB0aGlzLngxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMueDIuc2hhcGUgKyAnIG11c3QgbWF0Y2guJyk7XG4gICAgfSBlbHNlIGlmICh0aGlzLngxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB0aGlzLngyLnNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy54MS5zaGFwZVsxXSA9PT0gdGhpcy54Mi5zaGFwZVswXSxcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIG1hdG11bCBvcDogc2Vjb25kIGRpbWVuc2lvbiBvZiBtYXRyaXggd2l0aCBzaGFwZSAnICtcbiAgICAgICAgICAgICAgdGhpcy54MS5zaGFwZSArICcgbXVzdCBtYXRjaCBzaXplIG9mIHZlY3RvciB3aXRoIHNoYXBlICcgK1xuICAgICAgICAgICAgICB0aGlzLngyLnNoYXBlICsgJy4nKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAxICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzBdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBzaXplIG9mIHZlY3RvciB3aXRoIHNoYXBlICcgKyB0aGlzLngxLnNoYXBlICtcbiAgICAgICAgICAgICAgJyBtdXN0IG1hdGNoIGZpcnN0IGRpbWVuc2lvbiBvZiBtYXRyaXggd2l0aCAnICtcbiAgICAgICAgICAgICAgJ3NoYXBlICcgKyB0aGlzLngyLnNoYXBlICsgJy4nKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBpbnB1dHMgbXVzdCBiZSB2ZWN0b3JzIG9yIG1hdHJpY2VzLicpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIENvbnZvbHV0aW9uMkROb2RlIHJlcHJlc2VudHMgYSAyZCBjb252b2x1dGlvbiBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQ29udm9sdXRpb24yRE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIHN0YXRpYyByZWFkb25seSBXID0gJ3cnO1xuICBzdGF0aWMgcmVhZG9ubHkgQiA9ICdiJztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHByaXZhdGUgeDogVGVuc29yLCBwcml2YXRlIHc6IFRlbnNvciwgcHJpdmF0ZSBiOiBUZW5zb3IsXG4gICAgICBwdWJsaWMgZmllbGRTaXplOiBudW1iZXIsIHB1YmxpYyBvdXRwdXREZXB0aDogbnVtYmVyLCBwdWJsaWMgc3RyaWRlID0gMSxcbiAgICAgIHB1YmxpYyB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnQ29udm9sdXRpb24gMkQnLCB7eCwgdywgYn0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgICAgeC5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSxcbiAgICAgICAgICAgIHplcm9QYWQpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMueC5zaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBpbnB1dCBtdXN0IGJlIG9mIHJhbmsgMywgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLnguc2hhcGUgKyAnLicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLncuc2hhcGUubGVuZ3RoID09PSA0LFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnYyZCBvcDogd2VpZ2h0cyBtdXN0IGJlIG9mIHJhbmsgNCwgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLncuc2hhcGUgKyAnLicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLmIuc2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnYyZCBvcDogYmlhc2VzIG11c3QgYmUgb2YgcmFuayAxLCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMuYi5zaGFwZSArICcuJyk7XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlWzJdID09PSB0aGlzLncuc2hhcGVbMl0sXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBkZXB0aCBvZiBpbnB1dCAoJyArIHRoaXMueC5zaGFwZVsyXSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICgnICsgdGhpcy53LnNoYXBlWzJdICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBNYXhQb29sTm9kZSByZXByZXNlbnRzIGEgMmQgbWF4IHBvb2wgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1heFBvb2xOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHB1YmxpYyBmaWVsZFNpemU6IG51bWJlcixcbiAgICAgIHB1YmxpYyBzdHJpZGUgPSAxLCBwdWJsaWMgemVyb1BhZD86IG51bWJlcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ01heCBwb29sJywge3h9LFxuICAgICAgICBuZXcgVGVuc29yKGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgICAgIHguc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemUsIHguc2hhcGVbMl0sIHN0cmlkZSxcbiAgICAgICAgICAgIHplcm9QYWQpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMueC5zaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICAgICdFcnJvciBhZGRpbmcgbWF4UG9vbCBvcDogaW5wdXQgbXVzdCBiZSBvZiByYW5rIDMsIGJ1dCBnb3Qgc2hhcGU6ICcgK1xuICAgICAgICAgICAgdGhpcy54LnNoYXBlICsgJy4nKTtcbiAgfVxufVxuXG4vKipcbiAqIFJlTFVOb2RlIHJlcHJlc2VudHMgYSBSZUxVIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZUxVTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1JlTFUnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBFeHBOb2RlIHJlcHJlc2VudHMgYSBFeHBvbmVudGlhdGlvbiBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRXhwTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0V4cCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIExvZ05vZGUgcmVwcmVzZW50cyBhIEV4cG9uZW50aWF0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2dOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnTG9nJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogVGFuSE5vZGUgcmVwcmVzZW50cyBhIHRhbmggb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFRhbkhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnVGFuSCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNpZ21vaWROb2RlIHJlcHJlc2VudHMgYSBzaWdtb2lkIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaWdtb2lkTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NpZ21vaWQnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBTcXVhcmUgbm9kZSByZXByZXNlbnRzIGFuIGVsZW1lbnQtd2lzZSBzcXVhcmUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNxdWFyZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdTcXVhcmUnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUgcmVwcmVzZW50cyBhIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvblxuICogaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBzdGF0aWMgcmVhZG9ubHkgVEFSR0VUID0gJ3RhcmdldCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgdGFyZ2V0OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NvZnRtYXhDcm9zc0VudHJvcHlDb3N0Jywge3gsIHRhcmdldH0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnguc2hhcGUsIHRoaXMudGFyZ2V0LnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBzb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdCBvcDogeCBzaGFwZSAoJyArIHRoaXMueC5zaGFwZSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIHRhcmdldCBzaGFwZSAoJyArIHRoaXMudGFyZ2V0LnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdTb2Z0bWF4Jywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMSxcbiAgICAgICAgJ1RoZSBpbnB1dCB0byBhIHNvZnRtYXggbXVzdCBiZSBhIDEtRCB0ZW5zb3InKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlWzBdID49IDIsXG4gICAgICAgICdUaGUgaW5wdXQgdG8gYSBzb2Z0bWF4IG11c3QgaGF2ZSBhdCBsZWFzdCAyIHZhbHVlcycpO1xuICB9XG59XG5cbi8qKlxuICogTWVhblNxdWFyZWRDb3N0Tm9kZSByZXByZXNlbnRzIGEgbWVhbiBzcXVhcmVkIGNvc3Qgb3BlcmF0aW9uXG4gKiBpbiB0aGUgZ3JhcGguXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWVhblNxdWFyZWRDb3N0Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgTEFCRUwgPSAnbGFiZWwnO1xuICBzdGF0aWMgcmVhZG9ubHkgUFJFRElDVElPTiA9ICdwcmVkaWN0aW9uJztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIGxhYmVsOiBUZW5zb3IsIHByaXZhdGUgcHJlZGljdGlvbjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdNZWFuIFNxdWFyZWQgQ29zdCcsIHtsYWJlbCwgcHJlZGljdGlvbn0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLmxhYmVsLnNoYXBlLCB0aGlzLnByZWRpY3Rpb24uc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIG1lYW5TcXVhcmVkQ29zdCBvcDogbGFiZWwgc2hhcGUgKCcgKyB0aGlzLmxhYmVsLnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggcHJlZGljdGlvbiBzaGFwZSAoJyArIHRoaXMucHJlZGljdGlvbi5zaGFwZSArICcpLicpO1xuICB9XG59XG5cbi8qKlxuICogQXJnTWF4Tm9kZSByZXByZXNlbnRzIGFuIGFyZ21heCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwdWJsaWMgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdBcmdNYXgnLCB7eH0sIG5ldyBUZW5zb3IoWzFdKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnguc2hhcGUpID4gMCxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBhcmdtYXggb3A6IGlucHV0IHRlbnNvciBtdXN0IGhhdmUgYXQgbGVhc3Qgb25lIGVudHJ5LicpO1xuICB9XG59XG5cbi8qKlxuICogQXJnTWF4RXF1YWxzTm9kZSByZXByZXNlbnRzIGEgYXJnbWF4IGVxdWFscyBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4RXF1YWxzTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdBcmdNYXhFcXVhbHMnLCB7eDEsIHgyfSwgbmV3IFRlbnNvcihbMV0pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLngxLnNoYXBlLCB0aGlzLngyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBBcmdNYXhFcXVhbHMgb3A6IHgxIHNoYXBlICgnICsgdGhpcy54MS5zaGFwZSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIHgyIHNoYXBlICgnICsgdGhpcy54Mi5zaGFwZSArICcpLicpO1xuICB9XG59XG5cbi8qKlxuICogU3BsaXQgbm9kZXMgYXJlIHVzZWQgdG8gYWNjdW11bGF0ZSBiYWNrcHJvcCBkZXJpdmF0aXZlcyB3aGVuIGEgbm9kZSdzIG91dHB1dFxuICogdGVuc29yIGlzIGNvbnN1bWVkIGJ5IG11bHRpcGxlIG5vZGVzLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3BsaXROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIG91dHB1dHM6IFRlbnNvcltdID0gW107XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NwbGl0Tm9kZScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgbmV3IGNvbnN1bWVyIG9mIHRoaXMgc3BsaXQgbm9kZSwgaS5lLiBhIG5ldyBub2RlIHRoYXQgdXNlcyB0aGVcbiAgICogbm9kZSdzIG91dHB1dCB0ZW5zb3IuXG4gICAqL1xuICBnZXROZXdPdXRwdXRUZW5zb3IoKTogVGVuc29yIHtcbiAgICBjb25zdCBvdXRwdXQgPSBuZXcgVGVuc29yKHRoaXMuaW5wdXRzW1NwbGl0Tm9kZS5YXS5zaGFwZSk7XG4gICAgb3V0cHV0Lm5vZGUgPSB0aGlzO1xuICAgIHRoaXMub3V0cHV0cy5wdXNoKG91dHB1dCk7XG4gICAgcmV0dXJuIG91dHB1dDtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgdHlwZSBBcnJheURhdGEgPVxuICAgIE5EQXJyYXl8bnVtYmVyfG51bWJlcltdfG51bWJlcltdW118bnVtYmVyW11bXVtdfG51bWJlcltdW11bXVtdO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtJbml0aWFsaXplciwgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIsIFplcm9zSW5pdGlhbGl6ZXJ9IGZyb20gJy4vaW5pdGlhbGl6ZXJzJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIEEgbGF5ZXJzIHN1Z2FyIGNsYXNzIGFyb3VuZCB0aGUgZ3JhcGggdGhhdCBpbml0aWFsaXplcyB2YXJpYWJsZXNcbiAqIGF1dG9tYXRpY2FsbHkgZm9yIGxheWVycy5cbiAqL1xuZXhwb3J0IGNsYXNzIEdyYXBoTGF5ZXJzIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBnOiBHcmFwaCkge31cblxuICBkZW5zZShcbiAgICAgIG5hbWU6IHN0cmluZywgeDogVGVuc29yLCB1bml0czogbnVtYmVyLFxuICAgICAgYWN0aXZhdGlvbjogKCh4OiBUZW5zb3IpID0+IFRlbnNvcil8bnVsbCA9IG51bGwsIHVzZUJpYXMgPSB0cnVlLFxuICAgICAga2VybmVsSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyID0gbmV3IFZhcmlhbmNlU2NhbGluZ0luaXRpYWxpemVyKCksXG4gICAgICBiaWFzSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyID0gbmV3IFplcm9zSW5pdGlhbGl6ZXIoKSkge1xuICAgIGNvbnN0IHdlaWdodHMgPSB0aGlzLmcudmFyaWFibGUoXG4gICAgICAgIG5hbWUgKyAnLXdlaWdodHMnLFxuICAgICAgICBrZXJuZWxJbml0aWFsaXplci5pbml0aWFsaXplKFt4LnNoYXBlWzBdLCB1bml0c10sIHguc2hhcGVbMF0sIHVuaXRzKSk7XG5cbiAgICBsZXQgb3V0ID0gdGhpcy5nLm1hdG11bCh4LCB3ZWlnaHRzKTtcblxuICAgIGlmICh1c2VCaWFzKSB7XG4gICAgICBjb25zdCBiaWFzID0gdGhpcy5nLnZhcmlhYmxlKFxuICAgICAgICAgIG5hbWUgKyAnLWJpYXMnLFxuICAgICAgICAgIGJpYXNJbml0aWFsaXplci5pbml0aWFsaXplKFt1bml0c10sIHguc2hhcGVbMF0sIHVuaXRzKSk7XG4gICAgICBvdXQgPSB0aGlzLmcuYWRkKG91dCwgYmlhcyk7XG4gICAgfVxuXG4gICAgaWYgKGFjdGl2YXRpb24gIT0gbnVsbCkge1xuICAgICAgb3V0ID0gYWN0aXZhdGlvbihvdXQpO1xuICAgIH1cblxuICAgIHJldHVybiBvdXQ7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgZGF0YXNldCBmcm9tICcuL2RhdGFzZXQnO1xuaW1wb3J0IHtHcmFwaCwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXlNYXRoQ1BVfSBmcm9tICcuL21hdGgvbWF0aF9jcHUnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7T3B0aW1pemVyfSBmcm9tICcuL29wdGltaXplcic7XG5pbXBvcnQge0Nvc3RSZWR1Y3Rpb24sIEZlZWRFbnRyeSwgU2Vzc2lvbn0gZnJvbSAnLi9zZXNzaW9uJztcblxuY29uc3QgREVGQVVMVF9FVkFMX0lOVEVSVkFMX01TID0gMTUwMDtcbmNvbnN0IERFRkFVTFRfQ09TVF9JTlRFUlZBTF9NUyA9IDUwMDtcbmNvbnN0IERFRkFVTFRfSU5GRVJFTkNFX0VYQU1QTEVfSU5URVJWQUxfTVMgPSAzMDAwO1xuXG5leHBvcnQgaW50ZXJmYWNlIEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlciB7XG4gIGJhdGNoZXNUcmFpbmVkQ2FsbGJhY2s/OiAodG90YWxCYXRjaGVzVHJhaW5lZDogbnVtYmVyKSA9PiB2b2lkO1xuICBhdmdDb3N0Q2FsbGJhY2s/OiAoYXZnQ29zdDogU2NhbGFyKSA9PiB2b2lkO1xuICBtZXRyaWNDYWxsYmFjaz86IChtZXRyaWM6IE5EQXJyYXkpID0+IHZvaWQ7XG4gIGluZmVyZW5jZUV4YW1wbGVzQ2FsbGJhY2s/OlxuICAgICAgKGZlZWRzOiBGZWVkRW50cnlbXVtdLCBpbmZlcmVuY2VWYWx1ZXM6IE5EQXJyYXlbXSkgPT4gdm9pZDtcbiAgaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNDYWxsYmFjaz86IChleGFtcGxlc1BlclNlYzogbnVtYmVyKSA9PiB2b2lkO1xuICB0cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2s/OiAoZXhhbXBsZXNQZXJTZWM6IG51bWJlcikgPT4gdm9pZDtcbiAgdG90YWxUaW1lQ2FsbGJhY2s/OiAodG90YWxUaW1lU2VjOiBudW1iZXIpID0+IHZvaWQ7XG4gIGRvbmVUcmFpbmluZ0NhbGxiYWNrPzogKCkgPT4gdm9pZDtcbn1cblxuZXhwb3J0IGVudW0gTWV0cmljUmVkdWN0aW9uIHtcbiAgU1VNLFxuICBNRUFOXG59XG5cbi8qKlxuICogQSBjbGFzcyB0aGF0IGRyaXZlcyB0aGUgdHJhaW5pbmcgb2YgYSBncmFwaCBtb2RlbCBnaXZlbiBhIGRhdGFzZXQuIEl0IGFsbG93c1xuICogdGhlIHVzZXIgdG8gcHJvdmlkZSBhIHNldCBvZiBjYWxsYmFja3MgZm9yIG1lYXN1cmVtZW50cyBsaWtlIGNvc3QsIGFjY3VyYWN5LFxuICogYW5kIHNwZWVkIG9mIHRyYWluaW5nLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGhSdW5uZXIge1xuICBwcml2YXRlIGNvc3RUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSB0cmFpbkZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXTtcbiAgcHJpdmF0ZSBiYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBvcHRpbWl6ZXI6IE9wdGltaXplcjtcbiAgcHJpdmF0ZSBjdXJyZW50VHJhaW5Mb29wTnVtQmF0Y2hlczogbnVtYmVyfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBjb3N0SW50ZXJ2YWxNczogbnVtYmVyO1xuXG4gIHByaXZhdGUgbWV0cmljVGVuc29yOiBUZW5zb3J8dW5kZWZpbmVkO1xuICBwcml2YXRlIG1ldHJpY0ZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXXx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljQmF0Y2hTaXplOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIG1ldHJpY1JlZHVjdGlvbjogTWV0cmljUmVkdWN0aW9uO1xuICBwcml2YXRlIG1ldHJpY0ludGVydmFsTXM6IG51bWJlcjtcblxuICBwcml2YXRlIGluZmVyZW5jZVRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIGluZmVyZW5jZUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXXx1bmRlZmluZWQ7XG4gIHByaXZhdGUgaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXM6IG51bWJlcjtcbiAgcHJpdmF0ZSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ6IG51bWJlcjtcblxuICAvLyBSdW50aW1lIGluZm9ybWF0aW9uLlxuICBwcml2YXRlIGlzVHJhaW5pbmc6IGJvb2xlYW47XG4gIHByaXZhdGUgdG90YWxCYXRjaGVzVHJhaW5lZDogbnVtYmVyO1xuICBwcml2YXRlIGJhdGNoZXNUcmFpbmVkVGhpc1J1bjogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RDb21wdXRlZE1ldHJpYzogTkRBcnJheTtcblxuICBwcml2YXRlIGlzSW5mZXJyaW5nOiBib29sZWFuO1xuICBwcml2YXRlIGN1cnJlbnRJbmZlcmVuY2VMb29wTnVtUGFzc2VzOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIGluZmVyZW5jZVBhc3Nlc1RoaXNSdW46IG51bWJlcjtcblxuICBwcml2YXRlIHRyYWluU3RhcnRUaW1lc3RhbXA6IG51bWJlcjtcbiAgcHJpdmF0ZSBsYXN0Q29zdFRpbWVzdGFtcCA9IDA7XG4gIHByaXZhdGUgbGFzdEV2YWxUaW1lc3RhbXAgPSAwO1xuXG4gIHByaXZhdGUgbGFzdFN0b3BUaW1lc3RhbXA6IG51bWJlcnxudWxsO1xuICBwcml2YXRlIHRvdGFsSWRsZVRpbWVNcyA9IDA7XG5cbiAgcHJpdmF0ZSB6ZXJvU2NhbGFyOiBTY2FsYXI7XG4gIHByaXZhdGUgbWV0cmljQmF0Y2hTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIG1hdGg6IE5EQXJyYXlNYXRoLCBwcml2YXRlIHNlc3Npb246IFNlc3Npb24sXG4gICAgICBwcml2YXRlIGV2ZW50T2JzZXJ2ZXI6IEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlcikge1xuICAgIHRoaXMucmVzZXRTdGF0aXN0aWNzKCk7XG4gICAgdGhpcy56ZXJvU2NhbGFyID0gU2NhbGFyLm5ldygwKTtcbiAgfVxuXG4gIHJlc2V0U3RhdGlzdGljcygpIHtcbiAgICB0aGlzLnRvdGFsQmF0Y2hlc1RyYWluZWQgPSAwO1xuICAgIHRoaXMudG90YWxJZGxlVGltZU1zID0gMDtcbiAgICB0aGlzLmxhc3RTdG9wVGltZXN0YW1wID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCB0aGUgdHJhaW5pbmcgbG9vcCB3aXRoIGFuIG9wdGlvbmFsIG51bWJlciBvZiBiYXRjaGVzIHRvIHRyYWluIGZvci5cbiAgICogT3B0aW9uYWxseSB0YWtlcyBhIG1ldHJpYyB0ZW5zb3IgYW5kIGZlZWQgZW50cmllcyB0byBjb21wdXRlIHBlcmlvZGljYWxseS5cbiAgICogVGhpcyBjYW4gYmUgdXNlZCBmb3IgY29tcHV0aW5nIGFjY3VyYWN5LCBvciBhIHNpbWlsYXIgbWV0cmljLlxuICAgKi9cbiAgdHJhaW4oXG4gICAgICBjb3N0VGVuc29yOiBUZW5zb3IsIHRyYWluRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLCBiYXRjaFNpemU6IG51bWJlcixcbiAgICAgIG9wdGltaXplcjogT3B0aW1pemVyLCBudW1CYXRjaGVzPzogbnVtYmVyLCBtZXRyaWNUZW5zb3I/OiBUZW5zb3IsXG4gICAgICBtZXRyaWNGZWVkRW50cmllcz86IEZlZWRFbnRyeVtdLCBtZXRyaWNCYXRjaFNpemU/OiBudW1iZXIsXG4gICAgICBtZXRyaWNSZWR1Y3Rpb24gPSBNZXRyaWNSZWR1Y3Rpb24uTUVBTixcbiAgICAgIGV2YWxJbnRlcnZhbE1zID0gREVGQVVMVF9FVkFMX0lOVEVSVkFMX01TLFxuICAgICAgY29zdEludGVydmFsTXMgPSBERUZBVUxUX0NPU1RfSU5URVJWQUxfTVMpIHtcbiAgICB0aGlzLmNvc3RUZW5zb3IgPSBjb3N0VGVuc29yO1xuICAgIHRoaXMudHJhaW5GZWVkRW50cmllcyA9IHRyYWluRmVlZEVudHJpZXM7XG4gICAgdGhpcy5tZXRyaWNUZW5zb3IgPSBtZXRyaWNUZW5zb3I7XG4gICAgdGhpcy5tZXRyaWNGZWVkRW50cmllcyA9IG1ldHJpY0ZlZWRFbnRyaWVzO1xuICAgIGlmIChtZXRyaWNCYXRjaFNpemUgIT0gbnVsbCAmJiB0aGlzLm1ldHJpY0JhdGNoU2l6ZSAhPT0gbWV0cmljQmF0Y2hTaXplKSB7XG4gICAgICBpZiAodGhpcy5tZXRyaWNCYXRjaFNpemVTY2FsYXIgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhciA9IFNjYWxhci5uZXcobWV0cmljQmF0Y2hTaXplKTtcbiAgICB9XG4gICAgdGhpcy5tZXRyaWNCYXRjaFNpemUgPSBtZXRyaWNCYXRjaFNpemU7XG4gICAgdGhpcy5tZXRyaWNSZWR1Y3Rpb24gPSBtZXRyaWNSZWR1Y3Rpb247XG4gICAgdGhpcy5iYXRjaFNpemUgPSBiYXRjaFNpemU7XG4gICAgdGhpcy5vcHRpbWl6ZXIgPSBvcHRpbWl6ZXI7XG5cbiAgICB0aGlzLm1ldHJpY0ludGVydmFsTXMgPSBldmFsSW50ZXJ2YWxNcztcbiAgICB0aGlzLmNvc3RJbnRlcnZhbE1zID0gY29zdEludGVydmFsTXM7XG4gICAgdGhpcy5jdXJyZW50VHJhaW5Mb29wTnVtQmF0Y2hlcyA9IG51bUJhdGNoZXM7XG5cbiAgICB0aGlzLmJhdGNoZXNUcmFpbmVkVGhpc1J1biA9IDA7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnRyYWluU3RhcnRUaW1lc3RhbXAgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICB0aGlzLnRyYWluTmV0d29yaygpO1xuICB9XG5cbiAgc3RvcFRyYWluaW5nKCkge1xuICAgIHRoaXMuaXNUcmFpbmluZyA9IGZhbHNlO1xuICAgIHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgfVxuXG4gIHJlc3VtZVRyYWluaW5nKCkge1xuICAgIHRoaXMuaXNUcmFpbmluZyA9IHRydWU7XG4gICAgaWYgKHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgIT0gbnVsbCkge1xuICAgICAgdGhpcy50b3RhbElkbGVUaW1lTXMgKz0gcGVyZm9ybWFuY2Uubm93KCkgLSB0aGlzLmxhc3RTdG9wVGltZXN0YW1wO1xuICAgIH1cbiAgICB0aGlzLnRyYWluTmV0d29yaygpO1xuICB9XG5cbiAgcHJpdmF0ZSB0cmFpbk5ldHdvcmsoKSB7XG4gICAgaWYgKHRoaXMuYmF0Y2hlc1RyYWluZWRUaGlzUnVuID09PSB0aGlzLmN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzKSB7XG4gICAgICB0aGlzLnN0b3BUcmFpbmluZygpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc1RyYWluaW5nKSB7XG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmRvbmVUcmFpbmluZ0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmRvbmVUcmFpbmluZ0NhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICBjb25zdCBzaG91bGRDb21wdXRlQ29zdCA9IHRoaXMuZXZlbnRPYnNlcnZlci5hdmdDb3N0Q2FsbGJhY2sgIT0gbnVsbCAmJlxuICAgICAgICAoc3RhcnQgLSB0aGlzLmxhc3RDb3N0VGltZXN0YW1wID4gdGhpcy5jb3N0SW50ZXJ2YWxNcyk7XG4gICAgaWYgKHNob3VsZENvbXB1dGVDb3N0KSB7XG4gICAgICB0aGlzLmxhc3RDb3N0VGltZXN0YW1wID0gc3RhcnQ7XG4gICAgfVxuXG4gICAgY29uc3QgY29zdFJlZHVjdGlvbiA9XG4gICAgICAgIHNob3VsZENvbXB1dGVDb3N0ID8gQ29zdFJlZHVjdGlvbi5NRUFOIDogQ29zdFJlZHVjdGlvbi5OT05FO1xuXG4gICAgdGhpcy5tYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBhdmdDb3N0ID0gdGhpcy5zZXNzaW9uLnRyYWluKFxuICAgICAgICAgIHRoaXMuY29zdFRlbnNvciwgdGhpcy50cmFpbkZlZWRFbnRyaWVzLCB0aGlzLmJhdGNoU2l6ZSxcbiAgICAgICAgICB0aGlzLm9wdGltaXplciwgY29zdFJlZHVjdGlvbik7XG5cbiAgICAgIGlmIChzaG91bGRDb21wdXRlQ29zdCkge1xuICAgICAgICBjb25zdCB0cmFpblRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKSAtIHN0YXJ0O1xuXG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5hdmdDb3N0Q2FsbGJhY2shKGF2Z0Nvc3QpO1xuXG4gICAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIudHJhaW5FeGFtcGxlc1BlclNlY0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgICBjb25zdCBleGFtcGxlc1BlclNlYyA9ICh0aGlzLmJhdGNoU2l6ZSAqIDEwMDAgLyB0cmFpblRpbWUpO1xuICAgICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci50cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2soZXhhbXBsZXNQZXJTZWMpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIubWV0cmljQ2FsbGJhY2sgIT0gbnVsbCAmJlxuICAgICAgICAgIHRoaXMubWV0cmljRmVlZEVudHJpZXMgIT0gbnVsbCAmJlxuICAgICAgICAgIHN0YXJ0IC0gdGhpcy5sYXN0RXZhbFRpbWVzdGFtcCA+IHRoaXMubWV0cmljSW50ZXJ2YWxNcykge1xuICAgICAgICB0aGlzLmxhc3RFdmFsVGltZXN0YW1wID0gc3RhcnQ7XG5cbiAgICAgICAgaWYgKHRoaXMubGFzdENvbXB1dGVkTWV0cmljICE9IG51bGwpIHtcbiAgICAgICAgICB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYy5kaXNwb3NlKCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMgPSB0aGlzLmNvbXB1dGVNZXRyaWMoKTtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLm1ldHJpY0NhbGxiYWNrKHRoaXMubGFzdENvbXB1dGVkTWV0cmljKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci50b3RhbFRpbWVDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci50b3RhbFRpbWVDYWxsYmFjayhcbiAgICAgICAgICAgIChzdGFydCAtIHRoaXMudHJhaW5TdGFydFRpbWVzdGFtcCkgLyAxMDAwKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5iYXRjaGVzVHJhaW5lZFRoaXNSdW4rKztcbiAgICAgIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCsrO1xuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmJhdGNoZXNUcmFpbmVkQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIuYmF0Y2hlc1RyYWluZWRDYWxsYmFjayh0aGlzLnRvdGFsQmF0Y2hlc1RyYWluZWQpO1xuICAgICAgfVxuXG4gICAgfSk7XG4gICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnRyYWluTmV0d29yaygpKTtcbiAgfVxuXG4gIGluZmVyKFxuICAgICAgaW5mZXJlbmNlVGVuc29yOiBUZW5zb3IsIGluZmVyZW5jZUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSxcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVJbnRlcnZhbE1zID0gREVGQVVMVF9JTkZFUkVOQ0VfRVhBTVBMRV9JTlRFUlZBTF9NUyxcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVDb3VudCA9IDUsIG51bVBhc3Nlcz86IG51bWJlcikge1xuICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjayA9PSBudWxsICYmXG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrID09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnQ2Fubm90IHN0YXJ0IGluZmVyZW5jZSBsb29wLCBubyBpbmZlcmVuY2UgZXhhbXBsZSBvciAnICtcbiAgICAgICAgICAnZXhhbXBsZXMvc2VjIG9ic2VydmVyIHByb3ZpZGVkLicpO1xuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgZmVlZCB2YWx1ZXMgYXJlIHByb3ZpZGVycywgYW5kIG5vdCBOREFycmF5cy5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGluZmVyZW5jZUZlZWRFbnRyaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBmZWVkRW50cnkgPSBpbmZlcmVuY2VGZWVkRW50cmllc1tpXTtcblxuICAgICAgaWYgKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnQ2Fubm90IHN0YXJ0IGluZmVyZW5jZSBvbiB0aGUgbW9kZWwgcnVubmVyIHdpdGggZmVlZCBlbnRyaWVzIG9mICcgK1xuICAgICAgICAgICAgJ3R5cGUgTkRBcnJheS4gUGxlYXNlIHVzZSBJbnB1dFByb3ZpZGVycy4nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmluZmVyZW5jZUV4YW1wbGVJbnRlcnZhbE1zID0gaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXM7XG4gICAgdGhpcy5pbmZlcmVuY2VUZW5zb3IgPSBpbmZlcmVuY2VUZW5zb3I7XG4gICAgdGhpcy5pbmZlcmVuY2VGZWVkRW50cmllcyA9IGluZmVyZW5jZUZlZWRFbnRyaWVzO1xuICAgIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50ID0gaW5mZXJlbmNlRXhhbXBsZUNvdW50O1xuICAgIHRoaXMuY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXMgPSBudW1QYXNzZXM7XG4gICAgaWYgKCF0aGlzLmlzSW5mZXJyaW5nKSB7XG4gICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4gPSAwO1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmluZmVyTmV0d29yaygpKTtcbiAgICB9XG4gICAgdGhpcy5pc0luZmVycmluZyA9IHRydWU7XG4gIH1cblxuICBwcml2YXRlIGluZmVyTmV0d29yaygpIHtcbiAgICBpZiAoIXRoaXMuaXNJbmZlcnJpbmcgfHxcbiAgICAgICAgdGhpcy5pbmZlcmVuY2VQYXNzZXNUaGlzUnVuID09PSB0aGlzLmN1cnJlbnRJbmZlcmVuY2VMb29wTnVtUGFzc2VzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5tYXRoLnNjb3BlKChrZWVwLCB0cmFjaykgPT4ge1xuICAgICAgY29uc3QgZmVlZHM6IEZlZWRFbnRyeVtdW10gPSBbXTtcbiAgICAgIGNvbnN0IGluZmVyZW5jZVZhbHVlczogTkRBcnJheVtdID0gW107XG5cbiAgICAgIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50OyBpKyspIHtcbiAgICAgICAgLy8gUG9wdWxhdGUgYSBuZXcgRmVlZEVudHJ5W10gcG9wdWxhdGVkIHdpdGggTkRBcnJheXMuXG4gICAgICAgIGNvbnN0IG5kYXJyYXlGZWVkRW50cmllczogRmVlZEVudHJ5W10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmluZmVyZW5jZUZlZWRFbnRyaWVzIS5sZW5ndGg7IGorKykge1xuICAgICAgICAgIGNvbnN0IGZlZWRFbnRyeSA9IHRoaXMuaW5mZXJlbmNlRmVlZEVudHJpZXMhW2pdO1xuICAgICAgICAgIG5kYXJyYXlGZWVkRW50cmllcy5wdXNoKHtcbiAgICAgICAgICAgIHRlbnNvcjogZmVlZEVudHJ5LnRlbnNvcixcbiAgICAgICAgICAgIGRhdGE6XG4gICAgICAgICAgICAgICAgdHJhY2soKGZlZWRFbnRyeS5kYXRhIGFzIElucHV0UHJvdmlkZXIpLmdldE5leHRDb3B5KHRoaXMubWF0aCkpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZmVlZHMucHVzaChuZGFycmF5RmVlZEVudHJpZXMpO1xuXG4gICAgICAgIGluZmVyZW5jZVZhbHVlcy5wdXNoKFxuICAgICAgICAgICAgdGhpcy5zZXNzaW9uLmV2YWwodGhpcy5pbmZlcmVuY2VUZW5zb3IsIG5kYXJyYXlGZWVkRW50cmllcykpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICAvLyBGb3JjZSBhIEdQVSBkb3dubG9hZCwgc2luY2UgaW5mZXJlbmNlIHJlc3VsdHMgYXJlIGdlbmVyYWxseSBuZWVkZWQgb25cbiAgICAgICAgLy8gdGhlIENQVSBhbmQgaXQncyBtb3JlIGZhaXIgdG8gaW5jbHVkZSBibG9ja2luZyBvbiB0aGUgR1BVIHRvIGNvbXBsZXRlXG4gICAgICAgIC8vIGl0cyB3b3JrIGZvciB0aGUgaW5mZXJlbmNlIG1lYXN1cmVtZW50LlxuICAgICAgICBpbmZlcmVuY2VWYWx1ZXNbaW5mZXJlbmNlVmFsdWVzLmxlbmd0aCAtIDFdLmdldFZhbHVlcygpO1xuXG4gICAgICAgIGNvbnN0IGluZmVyZW5jZUV4YW1wbGVzUGVyU2VjVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQ7XG5cbiAgICAgICAgY29uc3QgZXhhbXBsZXNQZXJTZWMgPVxuICAgICAgICAgICAgKHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50ICogMTAwMCAvIGluZmVyZW5jZUV4YW1wbGVzUGVyU2VjVGltZSk7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrIShleGFtcGxlc1BlclNlYyk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrKGZlZWRzLCBpbmZlcmVuY2VWYWx1ZXMpO1xuICAgICAgfVxuICAgICAgdGhpcy5pbmZlcmVuY2VQYXNzZXNUaGlzUnVuKys7XG5cbiAgICB9KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuaW5mZXJOZXR3b3JrKCksIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMpO1xuICB9XG5cbiAgc3RvcEluZmVycmluZygpIHtcbiAgICB0aGlzLmlzSW5mZXJyaW5nID0gZmFsc2U7XG4gIH1cblxuICBpc0luZmVyZW5jZVJ1bm5pbmcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbmZlcnJpbmc7XG4gIH1cblxuICBjb21wdXRlTWV0cmljKCk6IFNjYWxhciB7XG4gICAgaWYgKHRoaXMubWV0cmljRmVlZEVudHJpZXMgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgY29tcHV0ZSBtZXRyaWMsIG5vIG1ldHJpYyBGZWVkRW50cmllcyBwcm92aWRlZC4nKTtcbiAgICB9XG5cbiAgICBsZXQgbWV0cmljID0gdGhpcy56ZXJvU2NhbGFyO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm1ldHJpY0JhdGNoU2l6ZSE7IGkrKykge1xuICAgICAgICBjb25zdCBtZXRyaWNWYWx1ZSA9XG4gICAgICAgICAgICB0aGlzLnNlc3Npb24uZXZhbCh0aGlzLm1ldHJpY1RlbnNvciEsIHRoaXMubWV0cmljRmVlZEVudHJpZXMhKTtcblxuICAgICAgICBtZXRyaWMgPSB0aGlzLm1hdGguYWRkKG1ldHJpYywgbWV0cmljVmFsdWUpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5tZXRyaWNSZWR1Y3Rpb24gPT09IE1ldHJpY1JlZHVjdGlvbi5NRUFOKSB7XG4gICAgICAgIG1ldHJpYyA9IHRoaXMubWF0aC5kaXZpZGUobWV0cmljLCB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhcik7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBtZXRyaWM7XG4gICAgfSk7XG4gIH1cblxuICBnZXRUb3RhbEJhdGNoZXNUcmFpbmVkKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZDtcbiAgfVxuXG4gIGdldExhc3RDb21wdXRlZE1ldHJpYygpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYztcbiAgfVxuXG4gIHNldE1hdGgobWF0aDogTkRBcnJheU1hdGgpIHtcbiAgICB0aGlzLm1hdGggPSBtYXRoO1xuICB9XG5cbiAgc2V0U2Vzc2lvbihzZXNzaW9uOiBTZXNzaW9uKSB7XG4gICAgdGhpcy5zZXNzaW9uID0gc2Vzc2lvbjtcbiAgfVxuXG4gIHNldEluZmVyZW5jZVRlbnNvcihpbmZlcmVuY2VUZW5zb3I6IFRlbnNvcikge1xuICAgIHRoaXMuaW5mZXJlbmNlVGVuc29yID0gaW5mZXJlbmNlVGVuc29yO1xuICB9XG5cbiAgc2V0SW5mZXJlbmNlRXhhbXBsZUNvdW50KGluZmVyZW5jZUV4YW1wbGVDb3VudDogbnVtYmVyKSB7XG4gICAgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgPSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtDb25zdGFudE5vZGUsIEdyYXBoLCBOb2RlLCBQbGFjZWhvbGRlck5vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIHByaW9yaXR5X3F1ZXVlIGZyb20gJy4vcHJpb3JpdHlfcXVldWUnO1xuaW1wb3J0IHtQcmlvcml0eVF1ZXVlfSBmcm9tICcuL3ByaW9yaXR5X3F1ZXVlJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5cbi8qKlxuICogR2l2ZW4gYSB0YXJnZXQgbm9kZSBpbiBhIGdyYXBoLCBhY2N1bXVsYXRlIHRoZSBzZXQgb2YgYWxsIG5vZGVzIHRoYXQgbmVlZCB0b1xuICogYmUgZXZhbHVhdGVkIGluIG9yZGVyIHRvIGV2YWx1YXRlIHRoZSB0YXJnZXQgZ3JhcGguIFRyYXZlcnNhbCBzdG9wcyBhbnl3aGVyZVxuICogYSBub2RlJ3MgdmFsdWVzIGFyZSBmZWQgaW4gZXh0ZXJuYWxseSB2aWEgXCJmZWVkIGRpY3RzXCIuXG4gKiBAcGFyYW0gbm9kZXMgVGhlIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSB0ZXJtaW5hdGluZ05vZGVzIFRoZSBzZXQgb2Ygbm9kZXMgdGhhdCBzdG9wIHRyYXZlcnNhbC5cbiAqIEByZXR1cm4gVGhlIHVub3JkZXJlZCBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvIGJlIGV2YWx1YXRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFVub3JkZXJlZEV2YWx1YXRpb25TZXQoXG4gICAgbm9kZXM6IE5vZGVbXSwgdGVybWluYXRpbmdOb2RlczogTm9kZVtdKTogTm9kZVtdIHtcbiAgY29uc3QgdGVybWluYXRpbmdOb2RlTWFwOiB7W2lkOiBudW1iZXJdOiBOb2RlfSA9IHt9O1xuICBjb25zdCBzZWVuOiB7W2lkOiBudW1iZXJdOiBOb2RlfSA9IHt9O1xuICBjb25zdCBzZXQ6IE5vZGVbXSA9IFtdO1xuICBjb25zdCB2aXNpdDogTm9kZVtdID0gbm9kZXMuc2xpY2UoKTtcbiAgdGVybWluYXRpbmdOb2Rlcy5mb3JFYWNoKG5vZGUgPT4gdGVybWluYXRpbmdOb2RlTWFwW25vZGUuaWRdID0gbm9kZSk7XG4gIC8qIEZsb29kIGZpbGw6IFdoaWxlIHRoZSAndG8gdmlzaXQnIHN0YWNrIGlzIG5vdCBlbXB0eSwgcG9wIGEgbm9kZSBvZmYgb2YgaXQuXG4gICAqIElmIHRoZSBub2RlIGhhcyBub3QgeWV0IGJlZW4gdmlzaXRlZCwgYWRkIGl0IHRvIHRoZSBzZXQsIG1hcmsgaXQgYXMgc2VlbixcbiAgICogYW5kIGVucXVldWUgYWxsIG9mIGl0cyBhbmNlc3RvciAoaW5wdXQpIG5vZGVzLiAqL1xuICB3aGlsZSAodmlzaXQubGVuZ3RoICE9PSAwKSB7XG4gICAgY29uc3QgY3VyID0gdmlzaXQucG9wKCkhO1xuICAgIGlmIChzZWVuW2N1ci5pZF0gPT0gbnVsbCkge1xuICAgICAgaWYgKHRlcm1pbmF0aW5nTm9kZU1hcFtjdXIuaWRdID09IG51bGwpIHtcbiAgICAgICAgT2JqZWN0LmtleXMoY3VyLmlucHV0cylcbiAgICAgICAgICAgIC5tYXAoaW5wdXROYW1lID0+IGN1ci5pbnB1dHNbaW5wdXROYW1lXSlcbiAgICAgICAgICAgIC5mb3JFYWNoKGlucHV0ID0+IHZpc2l0LnB1c2goaW5wdXQubm9kZSkpO1xuICAgICAgfVxuICAgICAgc2V0LnB1c2goY3VyKTtcbiAgICAgIHNlZW5bY3VyLmlkXSA9IGN1cjtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHNldDtcbn1cblxuLyoqXG4gKiBHaXZlbiBhIHNldCBvZiBub2RlcywgY29tcHV0ZSB0aGVpciBvcmRlciBzdWNoIHRoYXQgYWxsIGRlcGVuZGVudCBub2RlcyBhcmVcbiAqIGV2YWx1YXRlZCBhZnRlciB0aGVpciBkZXBlbmRlZXMuIFRoaXMgaXMgdGhlICdpbmZlcmVuY2Ugb3JkZXInIGZvciBub2RlcyBpblxuICogdGhlIG9wZXJhdGlvbiBncmFwaC5cbiAqIEBwYXJhbSB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0IFRoZSB1bm9yZGVyZWQgc2V0IG9mIG5vZGVzIHRoYXQgbmVlZCB0byBiZVxuICogZXZhbHVhdGVkLlxuICogQHJldHVybiBUaGUgaW5wdXQgbm9kZXMgaW4gZm9yd2FyZCBldmFsdWF0aW9uIG9yZGVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3JkZXJlZEV2YWx1YXRpb25TZXQodW5vcmRlcmVkRXZhbHVhdGlvblNldDogTm9kZVtdKTpcbiAgICBOb2RlW10ge1xuICAvKiBBIHByaW9yaXR5IHF1ZXVlIGlzIHVzZWQsIHdoZXJlIHRoZSBwcmlvcml0eSBpcyB0aGUgcmVtYWluaW5nIG51bWJlciBvZlxuICAgKiB1bmV2YWx1YXRlZCBub2RlcyB3aG9zZSBpbnB1dHMgY29tZSBmcm9tIHRoZSBlbGVtZW50IG5vZGUuIFRoaXMgZ3VhcmFudGVlc1xuICAgKiB0aGF0IGFsbCBkb3duc3RyZWFtIG5vZGVzIHdpbGwgYmUgZGVxdWV1ZWQgYmVmb3JlIHRoZWlyIGFuY2VzdG9ycy4gKi9cbiAgY29uc3Qgc2V0OiBOb2RlW10gPSBbXTtcbiAgY29uc3Qgbm9kZUluZGljZXM6IHtbaWQ6IG51bWJlcl06IG51bWJlcn0gPSB7fTtcbiAgY29uc3QgcGVuZGluZ0RlcGVuZGVuY2llczoge1tpZDogbnVtYmVyXTogbnVtYmVyfSA9IHt9O1xuXG4gIC8qIFRoZSBxdWV1ZSBwcmlvcml0eSBjYWxsYmFjayBsb29rcyBhdCB0aGUgbnVtYmVyIG9mIHBlbmRpbmcgZGVwZW5kZW5jaWVzIG9mXG4gICAqIGEgZ2l2ZW4gbm9kZS4gVGhlIHF1ZXVlIGluZGV4IG9ic2VydmVyIGNhbGxiYWNrIG1haW50YWlucyB0aGUgbG9jYXRpb24gb2ZcbiAgICogZWFjaCBub2RlIGluIHRoZSBhcnJheSwgZm9yIHByaW9yaXR5IHVwZGF0ZXMuICovXG4gIGNvbnN0IG5vZGVRdWV1ZSA9IG5ldyBQcmlvcml0eVF1ZXVlPE5vZGU+KFxuICAgICAgKGE6IE5vZGUsIGI6IE5vZGUpID0+IHByaW9yaXR5X3F1ZXVlLmRlZmF1bHRDb21wYXJlKFxuICAgICAgICAgIHBlbmRpbmdEZXBlbmRlbmNpZXNbYS5pZF0sIHBlbmRpbmdEZXBlbmRlbmNpZXNbYi5pZF0pLFxuICAgICAgKG5vZGU6IE5vZGUsIG5ld0luZGV4OiBudW1iZXIpID0+IG5vZGVJbmRpY2VzW25vZGUuaWRdID0gbmV3SW5kZXgpO1xuXG4gIHVub3JkZXJlZEV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHBlbmRpbmdEZXBlbmRlbmNpZXNbbm9kZS5pZF0gPSAwKTtcblxuICAvKiBGb3IgZXZlcnkgZGVzY2VuZGVudCBvZiBhIG5vZGUgKG91dHB1dCBvZiBhbmNlc3RvciBpcyBpbnB1dCB0byBkZXNjZW5kYW50KSxcbiAgICogaW5jcmVtZW50IHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBmb3IgdGhlIGFuY2VzdG9yLiBUaGlzIHByZXBhcmVzXG4gICAqIHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBhcyBhIHByaW9yaXR5IG1hcC4gKi9cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKFxuICAgICAgbm9kZSA9PiBPYmplY3Qua2V5cyhub2RlLmlucHV0cylcbiAgICAgICAgICAgICAgICAgIC5tYXAoa2V5ID0+IG5vZGUuaW5wdXRzW2tleV0pXG4gICAgICAgICAgICAgICAgICAuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmluZGV4T2YoaW5wdXQubm9kZSkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgcGVuZGluZ0RlcGVuZGVuY2llc1tpbnB1dC5ub2RlLmlkXSsrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9KSk7XG5cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4gbm9kZVF1ZXVlLmVucXVldWUobm9kZSkpO1xuXG4gIHdoaWxlICghbm9kZVF1ZXVlLmVtcHR5KCkpIHtcbiAgICBzZXQudW5zaGlmdChub2RlUXVldWUuZGVxdWV1ZSgpKTtcbiAgICAvKiBBcyBlYWNoIG5vZGUgaXMgdmlzaXRlZCwgZGVjcmVtZW50IHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBvZlxuICAgICAqIGVhY2ggYW5jZXN0b3IsIGFuZCB0ZWxsIHRoZSBwcmlvcml0eSBxdWV1ZSB0aGF0IHRoZSBwcmlvcml0eSBoYXMgY2hhbmdlZC5cbiAgICAgKi9cbiAgICBPYmplY3Qua2V5cyhzZXRbMF0uaW5wdXRzKS5tYXAoa2V5ID0+IHNldFswXS5pbnB1dHNba2V5XSkuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgICBpZiAodW5vcmRlcmVkRXZhbHVhdGlvblNldC5pbmRleE9mKGlucHV0Lm5vZGUpID09PSAtMSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBwZW5kaW5nRGVwZW5kZW5jaWVzW2lucHV0Lm5vZGUuaWRdLS07XG4gICAgICBub2RlUXVldWUudXBkYXRlKGlucHV0Lm5vZGUsIG5vZGVJbmRpY2VzW2lucHV0Lm5vZGUuaWRdKTtcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiBzZXQ7XG59XG5cbi8qKlxuICogQHJldHVybiBUcnVlIGlmZiB0aGUgbm9kZSBpcyBhbiBpbnB1dCBub2RlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNJbnB1dE5vZGUobm9kZTogTm9kZSk6IGJvb2xlYW4ge1xuICByZXR1cm4gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpLmxlbmd0aCA9PT0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNob3VsZEJhY2tQcm9wKHQ6IFRlbnNvcik6IGJvb2xlYW4ge1xuICByZXR1cm4gISh0Lm5vZGUgaW5zdGFuY2VvZiBDb25zdGFudE5vZGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNQYXNzdGhyb3VnaE5vZGUobm9kZTogTm9kZSwgbWFwOiBUZW5zb3JBcnJheU1hcCk6IGJvb2xlYW4ge1xuICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGtleXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBpbnB1dCA9IG5vZGUuaW5wdXRzW2tleXNbaV1dO1xuICAgIGlmIChtYXAuZ2V0KGlucHV0LCB0cnVlKSA9PT0gbWFwLmdldChub2RlLm91dHB1dCwgdHJ1ZSkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIGdwZ3B1X3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL2dwZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL3JlbmRlcl9uZGFycmF5X2dwdV91dGlsJztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL3dlYmdsX3V0aWwnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG5leHBvcnQge0NoZWNrcG9pbnRMb2FkZXJ9IGZyb20gJy4vY2hlY2twb2ludF9sb2FkZXInO1xuZXhwb3J0IHtEYXRhU3RhdHMsIEluTWVtb3J5RGF0YXNldH0gZnJvbSAnLi9kYXRhc2V0JztcbmV4cG9ydCB7R3JhcGgsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5leHBvcnQge0dyYXBoUnVubmVyLCBHcmFwaFJ1bm5lckV2ZW50T2JzZXJ2ZXIsIE1ldHJpY1JlZHVjdGlvbn0gZnJvbSAnLi9ncmFwaF9ydW5uZXInO1xuZXhwb3J0IHtDb25zdGFudEluaXRpYWxpemVyLCBJbml0aWFsaXplciwgTkRBcnJheUluaXRpYWxpemVyLCBPbmVzSW5pdGlhbGl6ZXIsIFJhbmRvbU5vcm1hbEluaXRpYWxpemVyLCBSYW5kb21UcnVuY2F0ZWROb3JtYWxJbml0aWFsaXplciwgUmFuZG9tVW5pZm9ybUluaXRpYWxpemVyLCBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciwgWmVyb3NJbml0aWFsaXplcn0gZnJvbSAnLi9pbml0aWFsaXplcnMnO1xuZXhwb3J0IHtJbkNQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIsIEluR1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciwgSW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5leHBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuZXhwb3J0IHtOREFycmF5TWF0aENQVX0gZnJvbSAnLi9tYXRoL21hdGhfY3B1JztcbmV4cG9ydCB7TkRBcnJheU1hdGhHUFV9IGZyb20gJy4vbWF0aC9tYXRoX2dwdSc7XG5leHBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuZXhwb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmV4cG9ydCB7T3B0aW1pemVyfSBmcm9tICcuL29wdGltaXplcic7XG5leHBvcnQge0Nvc3RSZWR1Y3Rpb24sIEZlZWRFbnRyeSwgU2Vzc2lvbn0gZnJvbSAnLi9zZXNzaW9uJztcbmV4cG9ydCB7U0dET3B0aW1pemVyfSBmcm9tICcuL3NnZF9vcHRpbWl6ZXInO1xuLy8gU2Vjb25kIGxldmVsIGV4cG9ydHMuXG5leHBvcnQge2NvbnZfdXRpbCwgZ3BncHVfdXRpbCwgcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwsIHV0aWwsIHdlYmdsX3V0aWx9O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcblxuLyoqXG4gKiBJbml0aWFsaXplciBpbnRlcmZhY2UsIGFsbCBpbml0aWFsaXplciBpbXBsZW1lbnQgdGhpcyBpbnRlcmZhY2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5pdGlhbGl6ZXIge1xuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5O1xufVxuXG5leHBvcnQgY2xhc3MgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBzY2FsZSA9IDEuMCxcbiAgICAgIHByaXZhdGUgbW9kZTogJ2Zhbl9pbid8J2Zhbl9vdXQnfCdmYW5fYXZnJyA9ICdmYW5faW4nLFxuICAgICAgcHJpdmF0ZSBkaXN0cmlidXRpb246ICd1bmlmb3JtJ3wnbm9ybWFsJyA9ICdub3JtYWwnKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIGxldCBuID0gMDtcbiAgICBpZiAodGhpcy5tb2RlID09PSAnZmFuX2luJykge1xuICAgICAgbiA9IGlucHV0VW5pdHM7XG4gICAgfSBlbHNlIGlmICh0aGlzLm1vZGUgPT09ICdmYW5fb3V0Jykge1xuICAgICAgbiA9IG91dHB1dFVuaXRzO1xuICAgIH0gZWxzZSBpZiAodGhpcy5tb2RlID09PSAnZmFuX2F2ZycpIHtcbiAgICAgIG4gPSAoaW5wdXRVbml0cyArIG91dHB1dFVuaXRzKSAvIDI7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnVW5leHBlY3RlZCBtb2RlIGZvciB2YXJpYW5jZSBzY2FsaW5nIGluaXRpYWxpemVyOiAnICsgdGhpcy5tb2RlKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5kaXN0cmlidXRpb24gPT09ICdub3JtYWwnKSB7XG4gICAgICByZXR1cm4gTkRBcnJheS5yYW5kVHJ1bmNhdGVkTm9ybWFsKFxuICAgICAgICAgIHdlaWdodHNTaGFwZSwgMC4wLCBNYXRoLnNxcnQodGhpcy5zY2FsZSAvIG4pKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuZGlzdHJpYnV0aW9uID09PSAndW5pZm9ybScpIHtcbiAgICAgIHJldHVybiBOREFycmF5LnJhbmRVbmlmb3JtKFxuICAgICAgICAgIHdlaWdodHNTaGFwZSwgMC4wLCBNYXRoLnNxcnQoMyAqIHRoaXMuc2NhbGUgLyBuKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnVW5leHBlY3RlZCBkaXN0cmlidXRpb24gZm9yIHZhcmlhbmNlIHNjYWxpbmcgaW5pdGlhbGl6ZXI6ICcgK1xuICAgICAgICAgIHRoaXMuZGlzdHJpYnV0aW9uKTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFplcm9zSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBPbmVzSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gICAgdmFsdWVzLmZpbGwoMSk7XG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQ29uc3RhbnRJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB2YWx1ZSA9IDApIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgY29uc3QgdmFsdWVzID0gTkRBcnJheS56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuICAgIHZhbHVlcy5maWxsKHRoaXMudmFsdWUpO1xuICAgIHJldHVybiB2YWx1ZXM7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBuZGFycmF5OiBOREFycmF5KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiB0aGlzLm5kYXJyYXk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFJhbmRvbU5vcm1hbEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1lYW4gPSAwLCBwcml2YXRlIHN0ZGV2ID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmROb3JtYWwod2VpZ2h0c1NoYXBlLCB0aGlzLm1lYW4sIHRoaXMuc3RkZXYpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSYW5kb21UcnVuY2F0ZWROb3JtYWxJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBtZWFuID0gMCwgcHJpdmF0ZSBzdGRldiA9IC4wNSkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICByZXR1cm4gTkRBcnJheS5yYW5kVHJ1bmNhdGVkTm9ybWFsKHdlaWdodHNTaGFwZSwgdGhpcy5tZWFuLCB0aGlzLnN0ZGV2KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmFuZG9tVW5pZm9ybUluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1pbnZhbCA9IC0uMDUsIHByaXZhdGUgbWF4dmFsID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmRVbmlmb3JtKHdlaWdodHNTaGFwZSwgdGhpcy5taW52YWwsIHRoaXMubWF4dmFsKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuLyoqXG4gKiBUaGUgaW50ZXJmYWNlIGZvciBpbnB1dCBwcm92aWRlcnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5wdXRQcm92aWRlciB7XG4gIC8qKlxuICAgKiBHZXQgdGhlIG5leHQgaW5wdXQgYXMgYSBjb3B5LiBUaGlzIGlzIGltcG9ydGFudCBiZWNhdXNlIHRoZSBkYXRhIG1pZ2h0XG4gICAqIGdldCB1cGxvYWRlZCB0byB0aGUgR1BVIGFuZCBtb2RpZnkgdGhlIG9yaWdpbmFsIGRhdGEuXG4gICAqIEBwYXJhbSBtYXRoIE5EQXJyYXlNYXRoXG4gICAqL1xuICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXk7XG4gIC8qKlxuICAgKiBEaXNwb3NlIHRoZSBpbnB1dCBjb3B5LlxuICAgKiBAcGFyYW0gbWF0aCBOREFycmF5TWF0aFxuICAgKiBAcGFyYW0gY29weSBUaGUgY29weSBwcm92aWRlZCBmcm9tIGdldE5leHRDb3B5XG4gICAqL1xuICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSk6IHZvaWQ7XG59XG5cbi8qKlxuICogQSBjb21tb24gaW50ZXJmYWNlIGZvciBzaHVmZmxlZCBpbnB1dCBwcm92aWRlciBidWlsZGVycy4gVGhpcyByZXR1cm5zXG4gKiBJbnB1dFByb3ZpZGVycyB0aGF0IGFyZSBzeW5jaHJvbml6ZWQuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXJzKCk6IElucHV0UHJvdmlkZXJbXTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgaW1wbGVtZW50c1xuICAgIFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBwcm90ZWN0ZWQgc2h1ZmZsZWRJbmRpY2VzOiBVaW50MzJBcnJheTtcbiAgcHJvdGVjdGVkIG51bUlucHV0czogbnVtYmVyO1xuXG4gIHByb3RlY3RlZCBpZHggPSAwO1xuICAvLyBDb3VudGVyIGZvciBob3cgbWFueSB0aW1lcyB0aGUgY3VycmVudCBpbmRleCBoYXMgYmVlbiBjYWxsZWQuIFJlc2V0cyB0byAwXG4gIC8vIHdoZW4gaXQgcmVhY2hlcyB0aGUgbnVtYmVyIG9mIGlucHV0cy5cbiAgcHJvdGVjdGVkIGlucHV0Q291bnRlciA9IDA7XG4gIHByb3RlY3RlZCBlcG9jaCA9IDA7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYW4gYEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyYC4gQWxsIG9mIHRoZSBpbnB1dHMgbXVzdCBiZVxuICAgKiBpbiBtZW1vcnkuXG4gICAqIEBwYXJhbSBpbnB1dHMgQWxsIG9mIHRoZSBpbnB1dHMsIHNpemU6IFtudW1iZXIgb2YgaW5wdXRzXVtudW1iZXIgb2ZcbiAgICogZXhhbXBsZXNdLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJvdGVjdGVkIGlucHV0czogTkRBcnJheVtdW10pIHtcbiAgICB0aGlzLnNodWZmbGVkSW5kaWNlcyA9IHV0aWwuY3JlYXRlU2h1ZmZsZWRJbmRpY2VzKGlucHV0c1swXS5sZW5ndGgpO1xuICAgIHRoaXMubnVtSW5wdXRzID0gaW5wdXRzLmxlbmd0aDtcblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgbnVtYmVyIG9mIGV4YW1wbGVzIGluIGVhY2ggaW5wdXQgbWF0Y2hlcy5cbiAgICBjb25zdCBudW1FeGFtcGxlcyA9IHRoaXMuaW5wdXRzWzBdLmxlbmd0aDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubnVtSW5wdXRzOyBpKyspIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMuaW5wdXRzW2ldLmxlbmd0aCA9PT0gbnVtRXhhbXBsZXMsXG4gICAgICAgICAgJ051bWJlciBvZiBleGFtcGxlcyBtdXN0IG1hdGNoIGFjcm9zcyBkaWZmZXJlbnQgaW5wdXRzLicpO1xuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgc2hhcGVzIHdpdGhpbiBpbnB1dHMgYWxsIG1hdGNoLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1JbnB1dHM7IGkrKykge1xuICAgICAgY29uc3QgaW5wdXRTaGFwZSA9IHRoaXMuaW5wdXRzW2ldWzBdLnNoYXBlO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0c1tpXS5sZW5ndGg7IGorKykge1xuICAgICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGlucHV0U2hhcGUsIHRoaXMuaW5wdXRzW2ldW2pdLnNoYXBlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0Q3VycmVudEV4YW1wbGVJbmRleCgpOiBudW1iZXIge1xuICAgIGNvbnN0IHJldHVybklkeCA9IHRoaXMuaWR4O1xuXG4gICAgdGhpcy5pbnB1dENvdW50ZXIrKztcbiAgICBpZiAodGhpcy5pbnB1dENvdW50ZXIgPj0gdGhpcy5udW1JbnB1dHMpIHtcbiAgICAgIHRoaXMuaWR4Kys7XG4gICAgICB0aGlzLmlucHV0Q291bnRlciA9IDA7XG5cbiAgICAgIGlmICh0aGlzLmlkeCA+PSB0aGlzLmlucHV0c1swXS5sZW5ndGgpIHtcbiAgICAgICAgdGhpcy5pZHggPSAwO1xuICAgICAgICB0aGlzLmVwb2NoKys7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXR1cm5JZHg7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0TmV4dElucHV0KGlucHV0SWQ6IG51bWJlcik6IE5EQXJyYXkge1xuICAgIGNvbnN0IGN1cnJlbnRFeGFtcGxlSW5kZXggPSB0aGlzLmdldEN1cnJlbnRFeGFtcGxlSW5kZXgoKTtcblxuICAgIHJldHVybiB0aGlzLmlucHV0c1tpbnB1dElkXVt0aGlzLnNodWZmbGVkSW5kaWNlc1tjdXJyZW50RXhhbXBsZUluZGV4XV07XG4gIH1cblxuICBnZXRFcG9jaCgpIHtcbiAgICByZXR1cm4gdGhpcy5lcG9jaDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGlucHV0IHByb3ZpZGVycyB3aGljaCBzaHVmZmxlIHRoZSBpbnB1dHMgYW5kIHN0YXkgaW4gc3luYy5cbiAgICovXG4gIGdldElucHV0UHJvdmlkZXJzKCk6IElucHV0UHJvdmlkZXJbXSB7XG4gICAgY29uc3QgaW5wdXRQcm92aWRlcnM6IElucHV0UHJvdmlkZXJbXSA9IFtdO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bUlucHV0czsgaSsrKSB7XG4gICAgICBpbnB1dFByb3ZpZGVycy5wdXNoKHRoaXMuZ2V0SW5wdXRQcm92aWRlcihpKSk7XG4gICAgfVxuICAgIHJldHVybiBpbnB1dFByb3ZpZGVycztcbiAgfVxuXG4gIGFic3RyYWN0IGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKTogSW5wdXRQcm92aWRlcjtcbn1cblxuLyoqXG4gKiBBbiBpbiBDUFUgbWVtb3J5IFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgdGhhdCBzaHVmZmxlcyBOREFycmF5cyBvbiB0aGVcbiAqIENQVSBhbmQga2VlcHMgdGhlbSBtdXR1YWxseSBpbiBzeW5jLlxuICovXG5leHBvcnQgY2xhc3MgSW5DUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIGV4dGVuZHNcbiAgICBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBnZXRJbnB1dFByb3ZpZGVyKGlucHV0SWQ6IG51bWJlcikge1xuICAgIGNvbnN0IHNodWZmbGVkSW5wdXRQcm92aWRlciA9IHRoaXM7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZ2V0TmV4dENvcHkobWF0aDogTkRBcnJheU1hdGgpOiBOREFycmF5IHtcbiAgICAgICAgcmV0dXJuIE5EQXJyYXkubGlrZShzaHVmZmxlZElucHV0UHJvdmlkZXIuZ2V0TmV4dElucHV0KGlucHV0SWQpKTtcbiAgICAgIH0sXG4gICAgICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSkge1xuICAgICAgICBjb3B5LmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG59XG5cbi8qKlxuICogQW4gaW4gR1BVIG1lbW9yeSBTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIHRoYXQgc2h1ZmZsZXMgTkRBcnJheXMgb24gdGhlXG4gKiBHUFUgYW5kIGtlZXBzIHRoZW0gbXV0dWFsbHkgaW4gc3luYy4gVGhpcyBpcyBtb3JlIHBlcmZvcm1hbnQgdGhhbiB0aGUgQ1BVXG4gKiB2ZXJzaW9uIGFzIHRleHR1cmVzIHdpbGwgc3RheSBpbiBtZW1vcnksIGhvd2V2ZXIgdGhpcyBpcyBtb3JlIEdQVSBtZW1vcnlcbiAqIGludGVuc2l2ZSBhcyBpdCBrZWVwcyB0ZXh0dXJlcyByZXNpZGVudCBpbiBHUFUgbWVtb3J5LlxuICovXG5leHBvcnQgY2xhc3MgSW5HUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIGV4dGVuZHNcbiAgICBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBnZXRJbnB1dFByb3ZpZGVyKGlucHV0SWQ6IG51bWJlcikge1xuICAgIGNvbnN0IHNodWZmbGVkSW5wdXRQcm92aWRlciA9IHRoaXM7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZ2V0TmV4dENvcHkobWF0aDogTkRBcnJheU1hdGgpOiBOREFycmF5IHtcbiAgICAgICAgcmV0dXJuIG1hdGguY2xvbmUoc2h1ZmZsZWRJbnB1dFByb3ZpZGVyLmdldE5leHRJbnB1dChpbnB1dElkKSk7XG4gICAgICB9LFxuICAgICAgZGlzcG9zZUNvcHkobWF0aDogTkRBcnJheU1hdGgsIGNvcHk6IE5EQXJyYXkpIHtcbiAgICAgICAgY29weS5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbi8qKiBBIG5vZGUncyBhY3RpdmF0aW9uIGZ1bmN0aW9uIGFuZCBpdHMgZGVyaXZhdGl2ZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0PFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgaW5wdXQ6IFQpOiBUO1xuICBkZXI8VCBleHRlbmRzIE5EQXJyYXk+KG1hdGg6IE5EQXJyYXlNYXRoLCBpbnB1dDogVCwgb3V0cHV0OiBUKTogVDtcbn1cblxuZXhwb3J0IGNsYXNzIFRhbkhGdW5jIGltcGxlbWVudHMgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0KG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGgudGFuaCh4KTtcbiAgICB9KTtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSwgeTogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIGNvbnN0IHlTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh5LCB5KTtcbiAgICAgIC8vIDEgLSB5XjIuXG4gICAgICByZXR1cm4gbWF0aC5zY2FsYXJNaW51c0FycmF5KFNjYWxhci5PTkUsIHlTcXVhcmVkKTtcbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmVMVUZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5yZWx1KHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGguc3RlcCh4KTtcbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2lnbW9pZEZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5zaWdtb2lkKHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgLy8geSAqICgxIC0geSkgPSB5IC0geV4yXG4gICAgICBjb25zdCB5U3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoeSwgeSk7XG4gICAgICByZXR1cm4gbWF0aC5zdWIoeSwgeVNxdWFyZWQpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTcXVhcmVGdW5jIGltcGxlbWVudHMgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0KG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGguZWxlbWVudFdpc2VNdWwoeCwgeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICAvLyBkeS9keCA9IDIqeC5cbiAgICAgIHJldHVybiBtYXRoLnNjYWxhclRpbWVzQXJyYXkoU2NhbGFyLlRXTywgeCk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgeDFTaGFwZTogbnVtYmVyW10sIHgyU2hhcGU6IG51bWJlcltdLCBheGlzOiBudW1iZXIsXG4gICAgZXJyb3JNZXNzYWdlUHJlZml4ID0gJycpIHtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB4MVNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArICdDb25jYXQzRCB4MSBzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuICB1dGlsLmFzc2VydChcbiAgICAgIHgyU2hhcGUubGVuZ3RoID09PSAzLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgJ0NvbmNhdDNEIHgyIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG5cbiAgdXRpbC5hc3NlcnQoXG4gICAgICBheGlzID49IDAgJiYgYXhpcyA8IDMsICdBeGlzIGZvciBjb25jYXQzRCBtdXN0IGJlIGJldHdlZW4gMCBhbmQgMi4nKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IDM7IGkrKykge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAoaSA9PT0gYXhpcykgfHwgKHgxU2hhcGVbaV0gPT09IHgyU2hhcGVbaV0pLFxuICAgICAgICBlcnJvck1lc3NhZ2VQcmVmaXggK1xuICAgICAgICAgICAgYFNoYXBlICgke3gxU2hhcGV9KSBkb2VzIG5vdCBtYXRjaCAoJHt4MlNoYXBlfSkgYWxvbmcgYCArXG4gICAgICAgICAgICBgbm9uLWNvbmNhdGVuYXRlZCBheGlzLmApO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZShcbiAgICB4MVNoYXBlOiBudW1iZXJbXSwgeDJTaGFwZTogbnVtYmVyW10sXG4gICAgYXhpczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgdXRpbC5hc3NlcnQoeDFTaGFwZS5sZW5ndGggPT09IDMsICdDb25jYXQzRCB4MSBzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuICB1dGlsLmFzc2VydCh4MlNoYXBlLmxlbmd0aCA9PT0gMywgJ0NvbmNhdDNEIHgyc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcblxuICBjb25zdCBvdXRwdXRTaGFwZSA9IHgxU2hhcGUuc2xpY2UoKTtcbiAgb3V0cHV0U2hhcGVbYXhpc10gKz0geDJTaGFwZVtheGlzXTtcbiAgcmV0dXJuIG91dHB1dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICBpbnB1dFNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplOiBudW1iZXIsXG4gICAgZGVwdGg6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHplcm9QYWQ/OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICBpZiAoemVyb1BhZCA9PSBudWxsKSB7XG4gICAgemVyb1BhZCA9IGNvbXB1dGVEZWZhdWx0UGFkKGlucHV0U2hhcGVSb3dDb2xEZXB0aCwgZmllbGRTaXplLCBzdHJpZGUpO1xuICB9XG4gIGNvbnN0IGlucHV0Um93cyA9IGlucHV0U2hhcGVSb3dDb2xEZXB0aFswXTtcbiAgY29uc3QgaW5wdXRDb2xzID0gaW5wdXRTaGFwZVJvd0NvbERlcHRoWzFdO1xuICBjb25zdCBvdXRwdXRSb3dzID0gKGlucHV0Um93cyAtIGZpZWxkU2l6ZSArIDIgKiB6ZXJvUGFkKSAvIHN0cmlkZSArIDE7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgdXRpbC5pc0ludChvdXRwdXRSb3dzKSxcbiAgICAgIGBUaGUgb3V0cHV0ICMgb2Ygcm93cyAoJHtvdXRwdXRSb3dzfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgIGBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcblxuICBjb25zdCBvdXRwdXRDb2xzID0gKGlucHV0Q29scyAtIGZpZWxkU2l6ZSArIDIgKiB6ZXJvUGFkKSAvIHN0cmlkZSArIDE7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgdXRpbC5pc0ludChvdXRwdXRDb2xzKSxcbiAgICAgIGBUaGUgb3V0cHV0ICMgb2YgY29sdW1ucyAoJHtvdXRwdXRDb2xzfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgYCArXG4gICAgICAgICAgYHRoZSBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcblxuICByZXR1cm4gW291dHB1dFJvd3MsIG91dHB1dENvbHMsIGRlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVEZWZhdWx0UGFkKFxuICAgIGlucHV0U2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplOiBudW1iZXIsXG4gICAgc3RyaWRlOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5mbG9vcigoaW5wdXRTaGFwZVswXSAqIChzdHJpZGUgLSAxKSAtIHN0cmlkZSArIGZpZWxkU2l6ZSkgLyAyKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVUZXhTaGFwZUZyb20zRChcbiAgICBzaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtzaGFwZVJvd0NvbERlcHRoWzBdLCBzaGFwZVJvd0NvbERlcHRoWzFdICogc2hhcGVSb3dDb2xEZXB0aFsyXV07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlV2VpZ2h0c1NoYXBlNEQoXG4gICAgaW5wdXREZXB0aDogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIGZTaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbZlNpemUsIGZTaXplLCBpbnB1dERlcHRoLCBvdXRwdXREZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlV2VpZ2h0c1RleFNoYXBlKFxuICAgIGlucHV0RGVwdGg6IG51bWJlciwgb3V0cHV0RGVwdGg6IG51bWJlcixcbiAgICBmaWVsZFNpemU6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2ZpZWxkU2l6ZSAqIGZpZWxkU2l6ZSAqIGlucHV0RGVwdGgsIG91dHB1dERlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVCaWFzZXNUZXhTaGFwZShvdXRwdXREZXB0aDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbMSwgb3V0cHV0RGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZURpbGF0ZWRSQyhcbiAgICByYzogW251bWJlciwgbnVtYmVyXSwgb3JpZ1N0cmlkZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGNvbnN0IHJvd3NEaWxhdGVkID0gKHJjWzBdIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgY29uc3QgY29sc0RpbGF0ZWQgPSAocmNbMV0gLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICByZXR1cm4gW3Jvd3NEaWxhdGVkLCBjb2xzRGlsYXRlZF07XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVNoYXBlcyhcbiAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBzcmNBcmVhID0gc291cmNlU2l6ZVswXSAqIHNvdXJjZVNpemVbMV07XG4gIGNvbnN0IGRzdEFyZWEgPSBkZXN0U2l6ZVswXSAqIGRlc3RTaXplWzFdO1xuICBpZiAoc3JjQXJlYSAhPT0gZHN0QXJlYSkge1xuICAgIGNvbnN0IHNyY1N0ciA9ICdbJyArIHNvdXJjZVNpemVbMF0gKyAnLCAnICsgc291cmNlU2l6ZVsxXSArICddJztcbiAgICBjb25zdCBkc3RTdHIgPSAnWycgKyBkZXN0U2l6ZVswXSArICcsICcgKyBkZXN0U2l6ZVsxXSArICddJztcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdjb3B5MkQgc2hhcGVzIGhhdmUgZGlmZmVyZW50IGFyZWFzOlxcbiAgc291cmNlU2l6ZSAnICsgc3JjU3RyICtcbiAgICAgICAgJywgYXJlYSAnICsgc3JjQXJlYSArICdcXG4gIGRlc3RTaXplICcgKyBkc3RTdHIgKyAnLCBhcmVhICcgKyBkc3RBcmVhKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbi8qKlxuICogQW4gZXJyb3IgZnVuY3Rpb24gYW5kIGl0cyBkZXJpdmF0aXZlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnRXaXNlQ29zdEZ1bmN0aW9uIHtcbiAgY29zdDxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIHgxOiBULCB4MjogVCk6IFQ7XG4gIGRlcjxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIHgxOiBULCB4MjogVCk6IFQ7XG4gIGRpc3Bvc2UoKTogdm9pZDtcbn1cblxuZXhwb3J0IGNsYXNzIFNxdWFyZUNvc3RGdW5jIGltcGxlbWVudHMgRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24ge1xuICBwcml2YXRlIGhhbGZPbmUgPSBTY2FsYXIubmV3KDAuNSk7XG5cbiAgY29zdChtYXRoOiBOREFycmF5TWF0aCwgeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogTkRBcnJheSB7XG4gICAgY29uc3QgZGlmZiA9IG1hdGguc3ViKHgxLCB4Mik7XG4gICAgY29uc3QgZGlmZlNxdWFyZWQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKGRpZmYsIGRpZmYpO1xuICAgIGNvbnN0IHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0aGlzLmhhbGZPbmUsIGRpZmZTcXVhcmVkKTtcblxuICAgIGRpZmYuZGlzcG9zZSgpO1xuICAgIGRpZmZTcXVhcmVkLmRpc3Bvc2UoKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IE5EQXJyYXkge1xuICAgIHJldHVybiBtYXRoLnN1Yih4MSwgeDIpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmhhbGZPbmUuZGlzcG9zZSgpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb3B5MmRfdXRpbCBmcm9tICcuL2NvcHkyZF91dGlsJztcblxuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbmV4cG9ydCB0eXBlIFNjb3BlUmVzdWx0ID0gTkRBcnJheVtdfE5EQXJyYXl8dm9pZDtcblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE5EQXJyYXlNYXRoIHtcbiAgcHJpdmF0ZSBuZGFycmF5U2NvcGVzOiBOREFycmF5W11bXSA9IFtdO1xuICBwcml2YXRlIGFjdGl2ZVNjb3BlOiBOREFycmF5W107XG5cbiAgcHJpdmF0ZSBuZGFycmF5c1RvS2VlcDogTkRBcnJheVtdW10gPSBbXTtcbiAgcHJpdmF0ZSBhY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwOiBOREFycmF5W10gPSBbXTtcblxuICAvKipcbiAgICogQHBhcmFtIHNhZmVNb2RlIEluIHNhZmUgbW9kZSwgeW91IG11c3QgdXNlIG1hdGggb3BlcmF0aW9ucyBpbnNpZGVcbiAgICogYSBtYXRoLnNjb3BlKCkgd2hpY2ggd2lsbCBhdXRvbWF0aWNhbGx5IGNsZWFuIHVwIGludGVybWVkaWF0ZSBOREFycmF5cy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgc2FmZU1vZGU6IGJvb2xlYW4pIHt9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBtYXRoIHNjb3BlLiBQdXQgY2hhaW5lZCBtYXRoIG9wZXJhdGlvbnMgaW5zaWRlIGEgc2NvcGVcbiAgICogZnVuY3Rpb24gY2xvc3VyZSBzbyB0aGF0IHRoZSBsaWJyYXJ5IGF1dG9tYXRpY2FsbHkgY2xlYW5zIHVwIE5EQXJyYXlzXG4gICAqIGZyb20gaW50ZXJtZWRpYXRlIG1hdGggb3BlcmF0aW9ucy4gWW91IG11c3QgY3JlYXRlIGEgc2NvcGUgaW4gc2FmZSBtb2RlXG4gICAqIHRvIGNhbGwgbWF0aCBvcGVyYXRpb25zLiBJZiBhIHJlc3VsdCBpcyByZXR1cm5lZCBmcm9tIHRoZSBzY29wZSwgaXQgd2lsbFxuICAgKiBhbHNvIGJlIHRyYWNrZWQsIHdoaWNoIG1lYW5zIHRoZXJlIG11c3QgYmUgeWV0IGFub3RoZXIgd3JhcHBpbmcgc2NvcGUuXG4gICAqIEBwYXJhbSBzY29wZUZuIFRoZSBmdW5jdGlvbiB0byBleGVjdXRlIHdpdGggY2hhaW5lZCBtYXRoIG9wZXJhdGlvbnMuXG4gICAqL1xuICBzY29wZTxUIGV4dGVuZHMgU2NvcGVSZXN1bHQ+KFxuICAgICAgc2NvcGVGbjpcbiAgICAgICAgICAoa2VlcDogPFQxIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVDEpID0+IFQxLFxuICAgICAgICAgICB0cmFjazogPFQyIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVDIpID0+IFQyKSA9PiBUKSB7XG4gICAgdGhpcy5zdGFydFNjb3BlKCk7XG5cbiAgICBjb25zdCBrZWVwRm4gPSA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUID0+IHRoaXMua2VlcChuZGFycmF5KTtcbiAgICBjb25zdCB0cmFja0ZuID0gPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCA9PiB0aGlzLnRyYWNrKG5kYXJyYXkpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHNjb3BlRm4oa2VlcEZuLCB0cmFja0ZuKTtcblxuICAgIHRoaXMuZW5kU2NvcGUocmVzdWx0KTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgYSBzY29wZS4gVXNlIHRoaXMgd2l0aCBlbmRTY29wZSgpIHRvIGFjaGlldmUgdGhlIHNhbWUgZnVuY3Rpb25hbGl0eVxuICAgKiBhcyBzY29wZSgpIHdpdGhvdXQgdGhlIG5lZWQgZm9yIGEgZnVuY3Rpb24gY2xvc3VyZS5cbiAgICovXG4gIHN0YXJ0U2NvcGUoKSB7XG4gICAgY29uc3QgbmV3U2NvcGU6IE5EQXJyYXlbXSA9IFtdO1xuICAgIHRoaXMubmRhcnJheVNjb3Blcy5wdXNoKG5ld1Njb3BlKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlID0gbmV3U2NvcGU7XG5cbiAgICBjb25zdCBuZXdOREFycmF5c1RvS2VlcDogTkRBcnJheVtdID0gW107XG4gICAgdGhpcy5uZGFycmF5c1RvS2VlcC5wdXNoKG5ld05EQXJyYXlzVG9LZWVwKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXAgPSBuZXdOREFycmF5c1RvS2VlcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmQgYSBzY29wZS4gVXNlIHRoaXMgd2l0aCBzdGFydFNjb3BlKCkgdG8gYWNoaWV2ZSB0aGUgc2FtZSBmdW5jdGlvbmFsaXR5XG4gICAqIGFzIHNjb3BlKCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgYSBmdW5jdGlvbiBjbG9zdXJlLlxuICAgKi9cbiAgZW5kU2NvcGUocmVzdWx0OiBTY29wZVJlc3VsdCkge1xuICAgIC8vIERpc3Bvc2UgdGhlIGN1cnJlbnQgc2NvcGUuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmFjdGl2ZVNjb3BlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBuZGFycmF5ID0gdGhpcy5hY3RpdmVTY29wZVtpXTtcblxuICAgICAgaWYgKHRoaXMuaXNOREFycmF5RGF0YUluTGlzdChuZGFycmF5LCB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXApIHx8XG4gICAgICAgICAgKHJlc3VsdCAhPSBudWxsICYmIHJlc3VsdCBpbnN0YW5jZW9mIE5EQXJyYXkgJiZcbiAgICAgICAgICAgbmRhcnJheS5nZXREYXRhKCkgPT09IChyZXN1bHQgYXMgTkRBcnJheSkuZ2V0RGF0YSgpKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIG5kYXJyYXkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIC8vIFBvcCB0aGUgY3VycmVudCBzY29wZS5cbiAgICB0aGlzLm5kYXJyYXlTY29wZXMucG9wKCk7XG4gICAgdGhpcy5hY3RpdmVTY29wZSA9IHRoaXMubmRhcnJheVNjb3Blcy5sZW5ndGggPT09IDAgP1xuICAgICAgICBudWxsISA6XG4gICAgICAgIHRoaXMubmRhcnJheVNjb3Blc1t0aGlzLm5kYXJyYXlTY29wZXMubGVuZ3RoIC0gMV07XG5cbiAgICAvLyBUcmFjayB0aGUgY3VycmVudCByZXN1bHQgaW4gdGhlIHBhcmVudCBzY29wZS5cbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgTkRBcnJheSAmJlxuICAgICAgICAhdGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KHJlc3VsdCwgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwKSkge1xuICAgICAgdGhpcy50cmFjayhyZXN1bHQpO1xuICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShyZXN1bHQpKSB7XG4gICAgICByZXN1bHQuZm9yRWFjaChyID0+IHtcbiAgICAgICAgaWYgKHIgaW5zdGFuY2VvZiBOREFycmF5ICYmXG4gICAgICAgICAgICAhdGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KHIsIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCkpIHtcbiAgICAgICAgICB0aGlzLnRyYWNrKHIpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLm5kYXJyYXlzVG9LZWVwLnBvcCgpO1xuICAgIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCA9IHRoaXMubmRhcnJheXNUb0tlZXAubGVuZ3RoID09PSAwID9cbiAgICAgICAgbnVsbCEgOlxuICAgICAgICB0aGlzLm5kYXJyYXlzVG9LZWVwW3RoaXMubmRhcnJheXNUb0tlZXAubGVuZ3RoIC0gMV07XG4gIH1cblxuICBwcml2YXRlIGlzTkRBcnJheURhdGFJbkxpc3QobmRhcnJheTogTkRBcnJheSwgbmRhcnJheUxpc3Q6IE5EQXJyYXlbXSkge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmRhcnJheUxpc3QubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChuZGFycmF5TGlzdFtpXS5nZXREYXRhKCkgPT09IG5kYXJyYXkuZ2V0RGF0YSgpKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogS2VlcHMgYW4gTkRBcnJheSBpbiB0aGUgY3VycmVudCBzY29wZSBmcm9tIGJlaW5nIGRpc3Bvc2VkIGF1dG9tYXRpY2FsbHkuXG4gICAqIEBwYXJhbSByZXN1bHQgVGhlIE5EQXJyYXkgdG8ga2VlcCBmcm9tIGJlaW5nIGRpc3Bvc2VkLlxuICAgKi9cbiAga2VlcDxUIGV4dGVuZHMgTkRBcnJheT4ocmVzdWx0OiBUKTogVCB7XG4gICAgaWYgKHRoaXMuYWN0aXZlU2NvcGUgPT0gbnVsbCkge1xuICAgICAgaWYgKHRoaXMuc2FmZU1vZGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgJ1lvdSBhcmUgdXNpbmcgbWF0aCBpbiBzYWZlIG1vZGUuIEVuY2xvc2UgYWxsICcgK1xuICAgICAgICAgICAgJ21hdGgubWV0aG9kKCkgY2FsbHMgaW5zaWRlIGEgc2NvcGU6ICcgK1xuICAgICAgICAgICAgJ21hdGguc2NvcGUoKCkgPT4ge21hdGgubWV0aG9kKCk7Li4ufSkgdG8gYXZvaWQgbWVtb3J5ICcgK1xuICAgICAgICAgICAgJ2xlYWtzLicpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwLnB1c2gocmVzdWx0KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWNrcyBhbiBOREFycmF5IGluIHRoZSBjdXJyZW50IHNjb3BlIHRvIGJlIGF1dG9tYXRpY2FsbHkgY2xlYW5lZCB1cCB3aGVuXG4gICAqIHRoZSBjdXJyZW50IHNjb3BlIGVuZHMsIGFuZCByZXR1cm5zIHRoZSB2YWx1ZS5cbiAgICogQHBhcmFtIHJlc3VsdCBUaGUgTkRBcnJheSB0byB0cmFjayBpbiB0aGUgY3VycmVudCBzY29wZS5cbiAgICovXG4gIHRyYWNrPFQgZXh0ZW5kcyBOREFycmF5PihyZXN1bHQ6IFQpOiBUIHtcbiAgICBpZiAodGhpcy5hY3RpdmVTY29wZSA9PSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5zYWZlTW9kZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnWW91IGFyZSB1c2luZyBtYXRoIGluIHNhZmUgbW9kZS4gRW5jbG9zZSBhbGwgJyArXG4gICAgICAgICAgICAnbWF0aC5tZXRob2QoKSBjYWxscyBpbnNpZGUgYSBzY29wZTogJyArXG4gICAgICAgICAgICAnbWF0aC5zY29wZSgoKSA9PiB7bWF0aC5tZXRob2QoKTsuLi59KSB0byBhdm9pZCBtZW1vcnkgJyArXG4gICAgICAgICAgICAnbGVha3MuJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmFjdGl2ZVNjb3BlLnB1c2gocmVzdWx0KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBkb3QgcHJvZHVjdCBvZiB0d28gbWF0cmljZXMsIEEgKiBCLiBUaGVzZSBtdXN0IGJlIG1hdHJpY2VzLFxuICAgKiB1c2UgbWF0cml4VGltZXNWZWN0b3IgYW5kIHZlY3RvclRpbWVzTWF0cml4LCBkb3RQcm9kdWN0LCBhbmQgb3V0ZXJQcm9kdWN0XG4gICAqIGluIG90aGVyIGNhc2VzLlxuICAgKiBAcGFyYW0gYSBGaXJzdCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYiBTZWNvbmQgbWF0cml4IGluIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIGFPcmllbnRhdGlvbiBUaGUgTWF0cml4T3JpZW50YXRpb24gb2YgQS4gSWYgdXNpbmcgVFJBTlNQT1NFRCwgd2lsbFxuICAgKiBjb21wdXRlIEFeVCAqIEIuXG4gICAqIEBwYXJhbSBiT3JpZW50YXRpb24gVGhlIE1hdHJpeE9yaWVudGF0aW9uIG9mIEIuIElmIHVzaW5nIFRSQU5TUE9TRUQsIHdpbGxcbiAgICogY29tcHV0ZSBBICogQl5ULlxuICAgKi9cbiAgbWF0TXVsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBBcnJheTJEIHtcbiAgICBjb25zdCBpbm5lclNoYXBlQSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG4gICAgY29uc3QgaW5uZXJTaGFwZUIgPVxuICAgICAgICAoYk9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGIuc2hhcGVbMF0gOiBiLnNoYXBlWzFdO1xuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gMiAmJiBiLnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiBtYXRNdWw6IGlucHV0cyBtdXN0IGJlIHJhbmsgMiwgZ290IHJhbmtzICR7YS5yYW5rfWAgK1xuICAgICAgICAgICAgYGFuZCAke2IucmFua30uYCk7XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgaW5uZXJTaGFwZUEgPT09IGlubmVyU2hhcGVCLFxuICAgICAgICBgRXJyb3IgaW4gbWF0TXVsOiBpbm5lciBzaGFwZXMgKCR7aW5uZXJTaGFwZUF9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7aW5uZXJTaGFwZUJ9KSBvZiBOREFycmF5cyB3aXRoIHNoYXBlcyAke2Euc2hhcGV9IGFuZCBgICtcbiAgICAgICAgICAgIGAke2Iuc2hhcGV9IGFuZCBvcmllbnRhdGlvbnMgJHtNYXRyaXhPcmllbnRhdGlvblthT3JpZW50YXRpb25dfWAgK1xuICAgICAgICAgICAgYCBhbmQgJHtNYXRyaXhPcmllbnRhdGlvbltiT3JpZW50YXRpb25dfSBtdXN0IG1hdGNoLmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXRNdWxJbnRlcm5hbChhLCBiLCBhT3JpZW50YXRpb24sIGJPcmllbnRhdGlvbikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXRNdWxJbnRlcm5hbChcbiAgICAgIGE6IEFycmF5MkQsIGI6IEFycmF5MkQsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sXG4gICAgICBiT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogQXJyYXkyRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIGEgdmVjdG9yIGFuZCBhIG1hdHJpeCwgdiAqIEIuXG4gICAqIEBwYXJhbSB2IFRoZSB2ZWN0b3IgaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gbWF0cml4IFRoZSBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgdmVjdG9yVGltZXNNYXRyaXgodjogQXJyYXkxRCwgbWF0cml4OiBBcnJheTJEKTogQXJyYXkxRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBmaXJzdCBpbnB1dCBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7di5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgbWF0cml4LnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2Vjb25kIGlucHV0IG11c3QgYmUgcmFuayAyLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHttYXRyaXgucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYuc2l6ZSA9PT0gbWF0cml4LnNoYXBlWzBdLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IHNpemUgb2YgZmlyc3QgcmFuayAxIGlucHV0ICgke3Yuc2l6ZX0pIGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggaW5uZXIgZGltZW5zaW9uIG9mIHNlY29uZCByYW5rIDIgaW5wdXQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bCh2LmFzMkQoMSwgdi5zaXplKSwgbWF0cml4KS5hczFEKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIGEgbWF0cml4IGFuZCB2ZWN0b3IsIEEgKiB2LlxuICAgKiBAcGFyYW0gbWF0cml4IFRoZSBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdiBUaGUgdmVjdG9yIGluIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIG1hdHJpeFRpbWVzVmVjdG9yKG1hdHJpeDogQXJyYXkyRCwgdjogQXJyYXkxRCk6IEFycmF5MUQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB2LnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2Vjb25kIGlucHV0IG11c3QgcmFuayAxLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHt2LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtYXRyaXgucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBmaXJzdCBpbnB1dCBtdXN0IGJlIGEgcmFuayAyLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHttYXRyaXgucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYuc2l6ZSA9PT0gbWF0cml4LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IHNpemUgb2YgZmlyc3QgcmFuayAxIGlucHV0ICR7di5zaXplfSBgICtcbiAgICAgICAgICAgIGBtdXN0IG1hdGNoIGlubmVyIGRpbWVuc2lvbiBvZiBzZWNvbmQgcmFuayAyIGlucHV0LCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHNoYXBlICR7bWF0cml4LnNoYXBlfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bChtYXRyaXgsIHYuYXMyRCh2LnNpemUsIDEpKS5hczFEKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byB2ZWN0b3JzLCB2MSAqIHYyLlxuICAgKiBAcGFyYW0gdjEgVGhlIGZpcnN0IHZlY3RvciBpbiB0aGUgZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdjIgVGhlIHNlY29uZCB2ZWN0b3IgaW4gdGhlIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIGRvdFByb2R1Y3QodjE6IEFycmF5MUQsIHYyOiBBcnJheTFEKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEucmFuayA9PT0gMSAmJiB2Mi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gZG90UHJvZHVjdDogaW5wdXRzIG11c3QgYmUgcmFuayAxLCBidXQgZ290IHJhbmtzIGAgK1xuICAgICAgICAgICAgYCR7djEucmFua30gYW5kICR7djIucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYxLnNpemUgPT09IHYyLnNpemUsXG4gICAgICAgIGBFcnJvciBpbiBkb3RQcm9kdWN0OiBzaXplIG9mIGlucHV0cyAoJHt2MS5zaXplfSkgYW5kIChgICtcbiAgICAgICAgICAgIGAke3YyLnNpemV9KSBtdXN0IG1hdGNoLmApO1xuICAgIHJldHVybiB0aGlzLm1hdE11bCh2MS5hczJEKDEsIHYxLnNpemUpLCB2Mi5hczJEKHYyLnNpemUsIDEpKS5hc1NjYWxhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBvdXRlciBwcm9kdWN0IG9mIHR3byB2ZWN0b3JzLCB2MSBhbmQgdjIuXG4gICAqIEBwYXJhbSB2MSBUaGUgZmlyc3QgdmVjdG9yIGluIHRoZSBvdXRlciBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHYyIFRoZSBzZWNvbmQgdmVjdG9yIGluIHRoZSBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqL1xuICBvdXRlclByb2R1Y3QodjE6IEFycmF5MUQsIHYyOiBBcnJheTFEKTogQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYxLnJhbmsgPT09IDEgJiYgdjIucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIG91dGVyUHJvZHVjdDogaW5wdXRzIG11c3QgYmUgcmFuayAxLCBidXQgZ290IHJhbmtzIGAgK1xuICAgICAgICAgICAgYCR7djEucmFua30gYW5kICR7djIucmFua30uYCk7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRNdWwodjEuYXMyRCh2MS5zaXplLCAxKSwgdjIuYXMyRCgxLCB2Mi5zaXplKSk7XG4gIH1cblxuICAvLy8vLy8vLy8vLy8vLy9cbiAgLy8gU2hhcGUgb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vL1xuXG4gIC8qKlxuICAgKiBDbG9uZXMgYW4gTkRBcnJheSBvZiBhbnkgc2hhcGUuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBOREFycmF5IHRvIGNsb25lLlxuICAgKi9cbiAgY2xvbmU8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNsb25lSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogUmVzaGFwZXMgYW4gTkRBcnJheSB0byBhIG5ldyBzaGFwZS4gVGhlIHNpemUgb2YgdGhlIGlucHV0IE5EQXJyYXkgbXVzdFxuICAgKiBtYXRjaCB0aGUgc2l6ZSBvZiB0aGUgcmVxdWVzdGVkIHNoYXBlLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIG5ld1NoYXBlIFRoZSBuZXcgc2hhcGUgdG8gcmVzaGFwZSB0aGUgTkRBcnJheSB0by4gTXVzdCBiZSB0aGUgc2FtZVxuICAgKiBzaXplIGFzIHRoZSBOREFycmF5LlxuICAgKi9cbiAgcmVzaGFwZTxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBuZGFycmF5LnNpemUgPT09IHV0aWwuc2l6ZUZyb21TaGFwZShuZXdTaGFwZSksXG4gICAgICAgIGBFcnJvciBpbiByZXNoYXBlOiBvbGQgc2l6ZSAke25kYXJyYXkuc2l6ZX0gbXVzdCBtYXRjaCBuZXcgc2l6ZSBgICtcbiAgICAgICAgICAgIGAke3V0aWwuc2l6ZUZyb21TaGFwZShuZXdTaGFwZSl9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMucmVzaGFwZUludGVybmFsPFQxLCBUMj4obmRhcnJheSwgbmV3U2hhcGUpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVzaGFwZUludGVybmFsPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMjtcblxuICAvKipcbiAgICogRXh0cmFjdHMgYSBzbGljZSBmcm9tIGEgbWF0cml4LiBUaGUgb3BlcmF0aW9uIGV4dHJhY2VzIGEgc2xpY2UgZnJvbSBpbnB1dFxuICAgKiB0aGF0IHN0YXJ0cyBhdCBjb29yZGluYXRlcyBgYmVnaW5gIGFuZCBpcyBvZiBzaXplIGBzaXplYC5cbiAgICogQHBhcmFtIGlucHV0IFRoZSBpbnB1dCBtYXRyaXggdG8gc2xpY2UgZnJvbS5cbiAgICogQHBhcmFtIGJlZ2luIFRoZSAyRCBjb29yZGluYXRlcyBpbiB0aGUgaW5wdXQgbWF0cml4IHRvIHN0YXJ0IHRoZSBzbGljZVxuICAgKiBmcm9tLlxuICAgKiBAcGFyYW0gc2l6ZSBUaGUgc2ljZSBvZiB0aGUgMkQgd2luZG93IHRvIHNsaWNlLlxuICAgKi9cbiAgc2xpY2UyRChpbnB1dDogQXJyYXkyRCwgYmVnaW46IFtudW1iZXIsIG51bWJlcl0sIHNpemU6IFtudW1iZXIsIG51bWJlcl0pOlxuICAgICAgQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGJlZ2luWzBdICsgc2l6ZVswXSA8PSBpbnB1dC5zaGFwZVswXSAmJlxuICAgICAgICAgICAgYmVnaW5bMV0gKyBzaXplWzFdIDw9IGlucHV0LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gc2xpY2UyRDogcmVxdWVzdGVkIHN0YXJ0IHBvc2l0aW9uICR7YmVnaW59IGFuZCBzaXplIGAgK1xuICAgICAgICAgICAgYCR7c2l6ZX0gd291bGQgb3ZlcmZsb3cgaW5wdXQgb2Ygc2hhcGUgJHtpbnB1dC5zaGFwZX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zbGljZTJESW50ZXJuYWwoaW5wdXQsIGJlZ2luLCBzaXplKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpbjogW251bWJlciwgbnVtYmVyXSwgc2l6ZTogW251bWJlciwgbnVtYmVyXSk6IEFycmF5MkQ7XG5cbiAgLyoqXG4gICAqIENvcGllcyBhIHdpbmRvdyBmcm9tIHRoZSBgc291cmNlYCBtYXRyaXggc3RhcnRpbmcgYXQgYHNvdXJjZUJlZ2luYCBhbmQgaXNcbiAgICogb2Ygc2l6ZSBgc291cmNlU2l6ZWAgdG8gYSB3aW5kb3cgaW4gdGhlIGBkZXN0YCBtYXRyaXggc3RhcnRpbmcgYXRcbiAgICogYGRlc3RCZWdpbmAgYW5kIGlzIG9mIHNpemUgYGRlc3RTaXplYC9cbiAgICogQHBhcmFtIHNvdXJjZSBUaGUgc291cmNlIG1hdHJpeCB0byBjb3B5IGZyb20uXG4gICAqIEBwYXJhbSBzb3VyY2VCZWdpbiBUaGUgY29vcmRpbmF0ZXMgdG8gc3RhcnQgdGhlIGNvcHkgZnJvbS5cbiAgICogQHBhcmFtIHNvdXJjZVNpemUgVGhlIHNpemUgb2YgdGhlIGNvcHkgd2luZG93LlxuICAgKiBAcGFyYW0gZGVzdCBUaGUgZGVzdGluYXRpb24gbWF0cml4IHRvIGNvcHkgdG8uXG4gICAqIEBwYXJhbSBkZXN0QmVnaW4gVGhlIGNvb3JkaW5hdGVzIGluIGBkZXN0YCB0byBjb3B5IHRvLlxuICAgKiBAcGFyYW0gZGVzdFNpemUgVGhlIHNpemUgb2YgdGhlIGRlc3RpbmF0aW9uIHdpbmRvdy5cbiAgICovXG4gIGNvcHkyRChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELCBkZXN0QmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBzb3VyY2VCZWdpblswXSArIHNvdXJjZVNpemVbMF0gPD0gc291cmNlLnNoYXBlWzBdICYmXG4gICAgICAgICAgICBzb3VyY2VCZWdpblsxXSArIHNvdXJjZVNpemVbMV0gPD0gc291cmNlLnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gY29weTJEOiByZXF1ZXN0ZWQgc291cmNlIHN0YXJ0IHBvc2l0aW9uICR7c291cmNlQmVnaW59IGAgK1xuICAgICAgICAgICAgYGFuZCBzb3VyY2Ugc2l6ZSAke3NvdXJjZVNpemV9IHdvdWxkIG92ZXJmbG93IHNvdXJjZSBOREFycmF5YCArXG4gICAgICAgICAgICBgb2Ygc2hhcGUgJHtzb3VyY2Uuc2hhcGV9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkZXN0QmVnaW5bMF0gKyBkZXN0U2l6ZVswXSA8PSBkZXN0LnNoYXBlWzBdICYmXG4gICAgICAgICAgICBkZXN0QmVnaW5bMV0gKyBkZXN0U2l6ZVsxXSA8PSBkZXN0LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gY29weTJEOiByZXF1ZXN0ZWQgZGVzdCBzdGFydCBwb3NpdGlvbiAke2Rlc3RCZWdpbn0gYCArXG4gICAgICAgICAgICBgYW5kIHNvdXJjZSBzaXplICR7ZGVzdFNpemV9IHdvdWxkIG92ZXJmbG93IGRlc3QgTkRBcnJheSBvZmAgK1xuICAgICAgICAgICAgYHNoYXBlICR7ZGVzdC5zaGFwZX0uYCk7XG4gICAgY29weTJkX3V0aWwudmFsaWRhdGVTaGFwZXMoc291cmNlU2l6ZSwgZGVzdFNpemUpO1xuXG4gICAgcmV0dXJuIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIHNvdXJjZSwgc291cmNlQmVnaW4sIHNvdXJjZVNpemUsIGRlc3QsIGRlc3RCZWdpbiwgZGVzdFNpemUpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELCBkZXN0QmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSk6IHZvaWQ7XG5cbiAgLyoqXG4gICAqIENvbmNhdGVuYXRlcyB0d28gM0QgbmRhcnJheXMgYWxvbmcgYSBnaXZlbiBheGlzLlxuICAgKlxuICAgKiBGb3IgZXhhbXBsZSwgaWY6XG4gICAqIEE6IHNoYXBlKDIsIDEsIDMpID0gfCByMSwgZzEsIGIxIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIgfFxuICAgKlxuICAgKiBCOiBzaGFwZSgyLCAxLCAzKSA9IHwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogQyA9IGNvbmNhdDNEKEEsIEIsIGF4aXMpXG4gICAqXG4gICAqIGlmIGF4aXMgPSAwOlxuICAgKiBDOiBzaGFwZSg0LCAxLCAzKSA9IHwgcjEsIGcxLCBiMSB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIzLCBnMywgYjMgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjQsIGc0LCBiNCB8XG4gICAqXG4gICAqIGlmIGF4aXMgPSAxOlxuICAgKiBDOiBzaGFwZSgyLCAyLCAzKSA9IHwgcjEsIGcxLCBiMSwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyLCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogaWYgYXhpcyA9IDI6XG4gICAqIEMgPSBzaGFwZSgyLCAxLCA2KSA9IHwgcjEsIGcxLCBiMSwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgIHwgcjIsIGcyLCBiMiwgcjQsIGc0LCBiNCB8XG4gICAqXG4gICAqIEBwYXJhbSBuZGFycmF5MSBUaGUgZmlyc3QgYXJyYXkgdG8gY29uY2F0LlxuICAgKiBAcGFyYW0gbmRhcnJheTIgVGhlIHNlY29uZCBhcnJheSB0byBjb25hdC5cbiAgICogQHBhcmFtIGF4aXMgVGhlIGF4aXMgdG8gY29uY2F0ZSBhbG9uZy5cbiAgICovXG4gIGNvbmNhdDNEKG5kYXJyYXkxOiBBcnJheTNELCBuZGFycmF5MjogQXJyYXkzRCwgYXhpczogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uY2F0M2RfdXRpbC5hc3NlcnRDb25jYXQzRFNoYXBlc01hdGNoKFxuICAgICAgICBuZGFycmF5MS5zaGFwZSwgbmRhcnJheTIuc2hhcGUsIGF4aXMsICdFcnJvciBpbiBjb25jYXQzZDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5jb25jYXQzREludGVybmFsKG5kYXJyYXkxLCBuZGFycmF5MiwgYXhpcykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb25jYXQzREludGVybmFsKFxuICAgICAgbmRhcnJheTE6IEFycmF5M0QsIG5kYXJyYXkyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gUmVkdWN0aW9uIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0aGUgbG9nKHN1bShlIF4geCkpIGZvciBlYWNoIHggaW4gdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5IHRvIGNvbXB1dGUgdGhlIGxvZ1N1bUV4cCBvdmVyLlxuICAgKi9cbiAgbG9nU3VtRXhwKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubG9nU3VtRXhwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBsb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc3VtIG9mIGFsbCB0aGUgZW50cmllcyBpbiB0aGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkgdG8gY29tcHV0ZSB0aGUgc3VtIG92ZXIuXG4gICAqL1xuICBzdW0obmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zdW1JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBmbGF0dGVuZWQgaW5kZXggb2YgdGhlIG1pbmltdW0gZWxlbWVudCBpbiB0aGUgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBhcmdNaW4obmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcmdNaW5JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBmbGF0dGVuZWQgaW5kZXggb2YgdGhlIG1heGltdW0gZWxlbWVudCBpbiB0aGUgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBhcmdNYXgobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcmdNYXhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYSAxIGlmIHRoZSBhcmdNYXggb2YgeDEgYW5kIHgyIGFyZSB0aGUgc2FtZSwgb3RoZXJ3aXNlIDAuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01heEVxdWFscyh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goeDEuc2hhcGUsIHgyLnNoYXBlLCAnRXJyb3IgaW4gYXJnTWF4RXF1YWxzOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01heEVxdWFsc0ludGVybmFsKHgxLCB4MikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhcmdNYXhFcXVhbHNJbnRlcm5hbCh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0b3AgSyB2YWx1ZXMgYW5kIGZsYXR0ZW5lZCBpbmRpY2VzLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIGsgSG93IG1hbnkgdG9wIHZhbHVlcyB0byBjb21wdXRlLlxuICAgKi9cbiAgdG9wSyhuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOiB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGsgPD0gbmRhcnJheS5zaXplLFxuICAgICAgICBgRXJyb3IgaW4gdG9wSzogayB2YWx1ZSAoJHtrfSkgbXVzdCBiZSBsZXNzIHRoYW4gc2l6ZSBvZiBpbnB1dCBgICtcbiAgICAgICAgICAgIGBuZGFycmF5LCBnb3Qgc2hhcGUgJHtuZGFycmF5LnNoYXBlfS5gKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnRvcEtJbnRlcm5hbChuZGFycmF5LCBrKTtcbiAgICB0aGlzLnRyYWNrKHJlc3VsdC52YWx1ZXMpO1xuICAgIHRoaXMudHJhY2socmVzdWx0LmluZGljZXMpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH07XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBtaW5pbXVtIHZhbHVlIGZyb20gdGhlIGlucHV0LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIG1pbihuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1pbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG1heGltdW0gdmFsdWUgZnJvbSB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbWF4KG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4SW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc29mdG1heCBub3JtYWxpemVkIHZlY3RvciBmcm9tIHRoZSBpbnB1dCB2ZWN0b3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB2ZWN0b3IuXG4gICAqL1xuICBzb2Z0bWF4KHg6IEFycmF5MUQpOiBBcnJheTFEIHtcbiAgICByZXR1cm4gdGhpcy5zY29wZSgoKSA9PiB7XG4gICAgICAvLyBEbyBpdCBpbiBsb2cgc3BhY2UgZm9yIG51bWVyaWNhbCBzdGFiaWxpdHkuXG4gICAgICAvLyBleHAoWCAtIGxvZ1N1bUV4cChYKSlcbiAgICAgIGNvbnN0IGxzZSA9IHRoaXMubG9nU3VtRXhwKHgpO1xuICAgICAgY29uc3QgbG9nUmVzdWx0ID0gdGhpcy5hcnJheU1pbnVzU2NhbGFyKHgsIGxzZSk7XG4gICAgICByZXR1cm4gdGhpcy5leHAobG9nUmVzdWx0KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gRWxlbWVudC13aXNlIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIFN3aXRjaGVzIGRpbWVuc2lvbnMgb2YgdGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBhIFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmV3RGltIFRoZSBuZXcgaW5kaWNlcyB0aGF0IGRlZmluZSB3aGljaCBzaGFwZXMgdmFsdWVzIHRvIHN3aXRjaC5cbiAgICovXG4gIHN3aXRjaERpbTxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBhLnJhbmsgPT09IG5ld0RpbS5sZW5ndGgsXG4gICAgICAgIGBFcnJvciBpbiBzd2l0Y2hEaW06IGxlbmd0aCBvZiBpbnB1dCBzaGFwZSAke2Euc2hhcGV9IGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggc2l6ZSBvZiBuZXdEaW0gYXJyYXkgJHtuZXdEaW19LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc3dpdGNoRGltSW50ZXJuYWwoYSwgbmV3RGltKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN3aXRjaERpbUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGE6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBwbHVzIE5EQXJyYXksIGMgKyBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gYyArIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gYyArIEEuXG4gICAqL1xuICBzY2FsYXJQbHVzQXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsYXJQbHVzQXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyUGx1c0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJQbHVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjOiBTY2FsYXIsIGE6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBtaW51cyBOREFycmF5LCBjIC0gQS5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciBjIGluIGMgLSBBLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSBBIGluIGMgLSBBLlxuICAgKi9cbiAgc2NhbGFyTWludXNBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxhck1pbnVzQXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyTWludXNBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIG1pbnVzIE5EQXJyYXksIEEgLSBjLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSBBIGluIEEgLSBjLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gQSAtIGMuXG4gICAqL1xuICBhcnJheU1pbnVzU2NhbGFyPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlNaW51c1NjYWxhcjogc2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJyYXlNaW51c1NjYWxhckludGVybmFsKGEsIGMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGE6IFQsIGM6IFNjYWxhcik6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIC0xICogQSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBhIFRoZSBpbnB1dCBhcnJheS5cbiAgICovXG4gIG5lZzxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubmVnSW50ZXJuYWwoYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBuZWdJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIEFkZHMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSwgQSArIEIuIElucHV0cyBtdXN0IGJlIHRoZSBzYW1lIHNoYXBlLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSB0byBhZGQgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gYWRkIGVsZW1lbnQtd2lzZS5cbiAgICovXG4gIGFkZDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSwgJ0Vycm9yIGluIGFkZDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hZGRJbnRlcm5hbChhLCBiKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVDtcblxuICAvKipcbiAgICogU3VidHJhY3RzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UsIEEgLSBCLiBJbnB1dHMgbXVzdCBiZSB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gc3VidHJhY3QgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gc3VidHJhY3QgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgc3ViPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gc3ViOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN1YkludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBNdWx0aXBsaWVzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UgKGhhZGFtYXJkIHByb2R1Y3QpLCBBICogQi4gSW5wdXRzIG11c3RcbiAgICogYmUgdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIG11bHRpcGx5IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGIgVGhlIHNlY29uZCBOREFycmF5IHRvIG11bHRpcGx5IGVsZW1lbnQtd2lzZS5cbiAgICovXG4gIGVsZW1lbnRXaXNlTXVsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gZWxlbWVudFdpc2VNdWw6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZWxlbWVudFdpc2VNdWxJbnRlcm5hbChhLCBiKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBEaXZpZGVzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UgKGhhZGFtYXJkIHByb2R1Y3QpLCBBIC8gQi4gSW5wdXRzIG11c3QgYmVcbiAgICogdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIGRpdmlkZSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBkaXZpZGUgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgZGl2aWRlPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gZGl2aWRlOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmRpdmlkZUludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZGl2aWRlSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBkaXZpZGVkIGJ5IGFuIE5EQXJyYXksIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIGMgL1xuICAgKiBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIHZhbHVlIGluIGMgLyBBLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSB2YWx1ZSBpbiBjIC8gQS5cbiAgICovXG4gIHNjYWxhckRpdmlkZWRCeUFycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGFyRGl2aWRlZEJ5QXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IE5EQXJyYXkgb2YgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJEaXZpZGVkQnlBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjOiBTY2FsYXIsIGE6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhbiBOREFycmF5IGRpdmlkZWQgYnkgYSBzY2FsYXIsIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIEEgL1xuICAgKiBjLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSB2YWx1ZSBpbiBBIC8gYy5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciB2YWx1ZSBpbiBBIC8gYy5cbiAgICovXG4gIGFycmF5RGl2aWRlZEJ5U2NhbGFyPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlEaXZpZGVkQnlTY2FsYXI6IHNlY29uZCBhcmd1bWVudCBtdXN0IGJlIHJhbmsgMCwgYCArXG4gICAgICAgICAgICBgYnV0IGdvdCBOREFycmF5IG9mIHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbChhLCBjKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFycmF5RGl2aWRlZEJ5U2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgYzogU2NhbGFyKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgZXhwb25lbnRpYWwgb2YgdGhlIGlucHV0IE5EQXJyYXkgZWxlbWVudC13aXNlLiB5ID0gZSBeIHhcbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBleHA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmV4cEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBuYXR1cmFsIGxvZ2FyaXRobSBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuIHkgPSBsbih4KVxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGxvZzxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubG9nSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBsb2dJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHJlY3RpZmllZCBsaW5lYXIgZWxlbWVudC13aXNlLCBtYXgoeCwgMCkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgcmVsdTxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMucmVsdUludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVsdUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc2lnbW9pZCBlbGVtZW50LXdpc2UsIHkgPSAxIC8gKDEgKyBleHAoLXgpKS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzaWdtb2lkPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zaWdtb2lkSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzaWdtb2lkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBoeXBlcmJvbGljIHRhbmdlbnQgb2YgdGhlIGlucHV0IE5EQXJyYXkgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHRhbmg8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnRhbmhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHRhbmhJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHNpbiBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UsIHkgPSBzaW4oeCkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgc2luPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zaW5JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc3RlcCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UsIHkgPSAxIGlmIHggPiAwIHwgMCBpZiB4IDw9XG4gICAqIDBcbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzdGVwPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zdGVwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzdGVwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxlZCBhcnJheSBhZGQgb3BlcmF0aW9uLCBjMSAqIEEgKyBjMiAqIEIuXG4gICAqIEBwYXJhbSBjMSBUaGUgZmlyc3Qgc2NhbGFyIGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSBpbiB0aGUgc2NhbGVkIGFycmF5IGFkZCBjb21wdXRhdGlvbi5cbiAgICogQHBhcmFtIGMyIFRoZSBzZWNvbmQgc2NhbGFyIGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gY2IgVGhlIHNlY29uZCBOREFycmF5IGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKi9cbiAgc2NhbGVkQXJyYXlBZGQ8VCBleHRlbmRzIE5EQXJyYXk+KGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYzEuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxlZEFycmF5QWRkOiBmaXJzdCBhcmd1bWVudCBtdXN0IHJhbmsgMCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGAgcmFuayAke2MxLnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjMi5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6IHRoaXJkIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYE5EQXJyYXkgb2YgcmFuayAke2MyLnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSwgJ0Vycm9yIGluIHNjYWxlZEFycmF5QWRkOiAnKTtcblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGVkQXJyYXlBZGRJbnRlcm5hbChjMSwgYSwgYzIsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjMTogU2NhbGFyLCBhOiBULCBjMjogU2NhbGFyLCBiOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBzY2FsYXIgdGltZXMgYXJyYXkgb3BlcmF0aW9uIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIGMgKlxuICAgKiBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGluIHRoZSBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBBIHRoZSBOREFycmF5IGluIHRoZSBvcGVyYXRpb24gdGhhdCB3aWxsIGJlIGJyb2FkY2FzdGVkIG92ZXIuXG4gICAqL1xuICBzY2FsYXJUaW1lc0FycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlEaXZpZGVkQnlTY2FsYXI6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyVGltZXNBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGFuIGVsZW1lbnQtd2lzZSBicm9hZGNhc3RlZCBtdWx0aXBsaWNhdGlvbiBvZiB0d28gbWF0cmljZXMgQSBhbmRcbiAgICogQi4gV2lsbCByZXR1cm4gYSBuZXcgbWF0cml4IHRoYXQgaXMgdGhlIG1heCBvZiBBIGFuZCBCLCB3aGVyZSB0aGUgc21hbGxlclxuICAgKiBtYXRyaXggd2lsbCBicm9hZGNhc3Qgb3ZlciB0aGUgbGFyZ2VyIG1hdHJpeC5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciBpbiB0aGUgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gQSB0aGUgTkRBcnJheSBpbiB0aGUgb3BlcmF0aW9uIHRoYXQgd2lsbCBiZSBicm9hZGNhc3RlZCBvdmVyLlxuICAgKi9cbiAgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3QoYTogQXJyYXkyRCwgYjogQXJyYXkyRCk6IEFycmF5MkQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBhLnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiBlbGVtZW50V2lzZU11bEJyb2FkY2FzdDogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSBgICtcbiAgICAgICAgICAgIGByYW5rIDIsIGJ1dCBnb3QgcmFuayAke2EucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGIucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIGVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0OiBzZWNvbmQgYXJndW1lbnQgbXVzdCBiZSBgICtcbiAgICAgICAgICAgIGByYW5rIDIsIGJ1dCBnb3QgcmFuayAke2IucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5lbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTpcbiAgICAgIEFycmF5MkQ7XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vIENvbnZvbHV0aW9uIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSAyRCBjb252b2x1dGlvbiBvdmVyIHRoZSBpbnB1dCB4LlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbcm93cywgY29scywgZGVwdGgxXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBiaWFzZXMgT3B0aW9uYWwgYmlhc2VzIE5EQXJyYXksIG11c3QgYmUgcmFuayAxIG9mIHNoYXBlIFtkZXB0aDJdLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0gemVyb1BhZCBUaGUgemVybyBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWRcbiAgICogZXF1YWxseSBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBjb252MmQoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzLnJhbmsgPT09IDQsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3dlaWdodHMucmFua30uYCk7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBiaWFzZXMucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gY29udjJkOiBiaWFzZXMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgICAgYCR7Ymlhc2VzLnJhbmt9LmApO1xuICAgIH1cblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzJdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkOiBkZXB0aCBvZiBpbnB1dCAoJHt4LnNoYXBlWzJdfSkgbXVzdCBtYXRjaCAgYCArXG4gICAgICAgICAgICBgaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgJHt3ZWlnaHRzLnNoYXBlWzJdfS5gKTtcblxuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5jb252MmRJbnRlcm5hbCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBiYWNrcHJvcCBvZiBhIDJEIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbeHJvd3MsIHhjb2xzLCBkZXB0aDFdLlxuICAgKiBAcGFyYW0gZHkgVGhlIGR5IGltYWdlLCBtdXN0IGJlIHJhbmsgMywgb2Ygc2hhcGUgW3lyb3dzLCB5Y29scywgZGVwdGgyXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgb3JpZ2luYWwgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgdGhlIG9yaWdpbmFsIGNvbnZvbHV0aW9uLlxuICAgKi9cbiAgY29udjJkQmFja1Byb3AoXG4gICAgICB4OiBBcnJheTNELCBkeTogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IHtkeDogQXJyYXkzRCwgZHc6IEFycmF5NEQsIGRiOiBBcnJheTFEfSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7eC5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRCYWNrUHJvcDogZHkgbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHtkeS5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiB3ZWlnaHRzIG11c3QgYmUgcmFuayA0LCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7d2VpZ2h0cy5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbMl0sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRCYWNrUHJvcDogZGVwdGggb2YgeCAke3guc2hhcGVbMl19KSBtdXN0IGAgK1xuICAgICAgICAgICAgYG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICgke3dlaWdodHMuc2hhcGVbMl19LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkeS5zaGFwZVsyXSA9PT0gd2VpZ2h0cy5zaGFwZVszXSxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkZXB0aCBvZiBkeSAoJHtkeS5zaGFwZVsyXX0pIG11c3QgYCArXG4gICAgICAgICAgICBgbWF0Y2ggb3V0cHV0IGRlcHRoIGZvciB3ZWlnaHRzICgke3dlaWdodHMuc2hhcGVbM119KS5gKTtcblxuICAgIGNvbnN0IGJhY2twcm9wUmVzdWx0ID1cbiAgICAgICAgdGhpcy5jb252MmRCYWNrUHJvcEludGVybmFsKHgsIGR5LCB3ZWlnaHRzLCBzdHJpZGUsIHBhZCk7XG5cbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmRiKTtcbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmR3KTtcbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmR4KTtcblxuICAgIHJldHVybiBiYWNrcHJvcFJlc3VsdDtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9O1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgdHJhbnNwb3NlZCAyRCBjb252b2x1dGlvbiBvZiBhbiBpbWFnZSwgYWxzbyBrbm93biBhcyBhXG4gICAqIGRlY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFt4cm93cywgeGNvbHMsIGRlcHRoMV0uXG4gICAqIEBwYXJhbSB3ZWlnaHRzIFRoZSB3ZWlnaHRzIE5EQXJyYXksIG11c3QgYmUgcmFuayA0LCBvZiBzaGFwZSBbZiwgZiwgZGVwdGgxLFxuICAgKiBkZXB0aDJdLlxuICAgKiBAcGFyYW0gYmlhc2VzIE9wdGlvbmFsIGJpYXNlcyBOREFycmF5LCBtdXN0IGJlIHJhbmsgMSBvZiBzaGFwZSBbZGVwdGgyXS5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgY29udjJkVHJhbnNwb3NlKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkVHJhbnNwb3NlOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzLnJhbmsgPT09IDQsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke3dlaWdodHMucmFua31gKTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGJpYXNlcy5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IGJpYXNlcyBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCAnICtcbiAgICAgICAgICAgICAgJ3JhbmsgJHtiaWFzZXMucmFua30uYCk7XG4gICAgfVxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzNdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkVHJhbnNwb3NlOiBkZXB0aCBvZiBpbnB1dCAoJHt4LnNoYXBlWzJdfSkgbXVzdCBgICtcbiAgICAgICAgICAgIGBtYXRjaCBpbnB1dCBkZXB0aCBmb3Igd2VpZ2h0cyAke3dlaWdodHMuc2hhcGVbM119LmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2soXG4gICAgICAgIHRoaXMuY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoeCwgd2VpZ2h0cywgYmlhc2VzLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIG1heCBwb29saW5nIG9mIGFuIGltYWdlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLlxuICAgKiBAcGFyYW0gZlNpemUgVGhlIGZpZWxkIHNpemUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBtYXhQb29sKHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgICdFcnJvciBpbiBtYXhQb29sOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayAnICsgeC5yYW5rICsgJy4nKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1heFBvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGJhY2twcm9wIG9mIGEgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBkeSBUaGUgZHkgZXJyb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1heFBvb2xCYWNrcHJvcChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtYXhQb29sQmFja3Byb3A6IGR5IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke2R5LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtYXhQb29sQmFja3Byb3A6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7eC5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoZHksIHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhQb29sQmFja3Byb3BJbnRlcm5hbChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIG1pbiBwb29saW5nIG9mIGFuIGltYWdlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLlxuICAgKiBAcGFyYW0gZlNpemUgVGhlIGZpZWxkIHNpemUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBtaW5Qb29sKHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtaW5Qb29sOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayAke3gucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5taW5Qb29sSW50ZXJuYWwoeCwgZlNpemUsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1pblBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSAyRCBhdmVyYWdlIHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIGF2Z1Bvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGF2Z1Bvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmF2Z1Bvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXZnUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKlxuICAgKiBCaWxpbmVhciByZXNpemUgYSAzRCBhcnJheSBwZXIgZWFjaCBjaGFubmVsIHRvIGEgbmV3IDJEIHNoYXBlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgQXJyYXkzRC5cbiAgICogQHBhcmFtIG5ld1NoYXBlMkQgVGhlIG5ldyBzaGFwZSB0byByZXNpemUgdGhlIEFycmF5M0QgdG8uIEVhY2ggY2hhbm5lbCBpc1xuICAgKiByZXNpemVkIGluZGl2aWR1YWxseS5cbiAgICogQHBhcmFtIGFsaWduQ29ybmVycyBBbiBvcHRpb25hbCBib29sLiBEZWZhdWx0cyB0byBGYWxzZS4gSWYgdHJ1ZSwgcmVzY2FsZVxuICAgKiBpbnB1dCBieSAobmV3X2hlaWdodCAtIDEpIC8gKGhlaWdodCAtIDEpLCB3aGljaCBleGFjdGx5IGFsaWducyB0aGUgNFxuICAgKiBjb3JuZXJzIG9mIGltYWdlcyBhbmQgcmVzaXplZCBpbWFnZXMuIElmIGZhbHNlLCByZXNjYWxlIGJ5IG5ld19oZWlnaHQgL1xuICAgKiBoZWlnaHQuIFRyZWF0IHNpbWlsYXJseSB0aGUgd2lkdGggZGltZW5zaW9uLlxuICAgKi9cbiAgcmVzaXplQmlsaW5lYXIzRChcbiAgICAgIHg6IEFycmF5M0QsIG5ld1NoYXBlMkQ6IFtudW1iZXIsIG51bWJlcl0sIGFsaWduQ29ybmVycyA9IGZhbHNlKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIHJlc2l6ZUJpbGluZWFyM0Q6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgbmV3U2hhcGUyRC5sZW5ndGggPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiByZXNpemVCaWxpbmVhcjNEOiBuZXcgc2hhcGUgbXVzdCAyRCwgYnV0IGdvdCBzaGFwZSBgICtcbiAgICAgICAgICAgIGAke25ld1NoYXBlMkR9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKFxuICAgICAgICB0aGlzLnJlc2l6ZUJpbGluZWFyM0RJbnRlcm5hbCh4LCBuZXdTaGFwZTJELCBhbGlnbkNvcm5lcnMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQmF0Y2ggbm9ybWFsaXphdGlvbiAzRC4gTWVhbiwgdmFyaWFuY2UsIHNjYWxlLCBhbmQgb2Zmc2V0IGNhbiBiZSBvZiB0d29cbiAgICogc2hhcGVzOiAxKSBUaGUgc2FtZSBzaGFwZSBhcyB0aGUgaW5wdXQ6IGFuIEFycmF5M0QuIDIpIEluIHRoZSBjb21tb24gY2FzZSxcbiAgICogdGhlIGRlcHRoIGRpbWVuc2lvbiBpcyB0aGUgbGFzdCBkaW1lbnNpb24gb2YgeCwgc28gdGhlIHZhbHVlcyB3b3VsZCBiZSBhblxuICAgKiBBcnJheTFEIG9mIHNoYXBlIFtkZXB0aF0uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbWVhbiBBIG1lYW4gTkRBcnJheS5cbiAgICogQHBhcmFtIHZhcmlhbmNlIEEgdmFyaWFuY2UgTkRBcnJheS5cbiAgICogQHBhcmFtIHZhcmlhbmNlRXBzaWxvbiBBIHNtYWxsIGZsb2F0IG51bWJlciB0byBhdm9pZCBkaXZpZGluZyBieSAwLlxuICAgKiBAcGFyYW0gc2NhbGUgQSBzY2FsZSBOREFycmF5LlxuICAgKiBAcGFyYW0gb2Zmc2V0IEFuIG9mZnNldCBOREFycmF5LlxuICAgKi9cbiAgYmF0Y2hOb3JtYWxpemF0aW9uM0QoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb24gPSAuMDAxLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtZWFuLnJhbmsgPT09IDMgfHwgbWVhbi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IG1lYW4gbXVzdCBiZSByYW5rIDMgb3IgcmFuayAxIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke21lYW4ucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHZhcmlhbmNlLnJhbmsgPT09IDMgfHwgdmFyaWFuY2UucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIGJhdGNoTm9ybWFsaXphdGlvbjNEOiB2YXJpYW5jZSBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYCArXG4gICAgICAgICAgICBgYnV0IGdvdCByYW5rICR7dmFyaWFuY2UucmFua30uYCk7XG4gICAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHNjYWxlLnJhbmsgPT09IDMgfHwgc2NhbGUucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IHNjYWxlIG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgICAgYGJ1dCBnb3QgcmFuayAke3NjYWxlIS5yYW5rfS5gKTtcbiAgICB9XG4gICAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBvZmZzZXQucmFuayA9PT0gMyB8fCBvZmZzZXQucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IG9mZnNldCBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYCArXG4gICAgICAgICAgICAgIGBidXQgZ290IHJhbmsgJHtvZmZzZXQhLnJhbmt9LmApO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYmF0Y2hOb3JtYWxpemF0aW9uM0RJbnRlcm5hbChcbiAgICAgICAgeCwgbWVhbiwgdmFyaWFuY2UsIHZhcmlhbmNlRXBzaWxvbiwgc2NhbGUsIG9mZnNldCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBiYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbWVhbjogQXJyYXkzRHxBcnJheTFELCB2YXJpYW5jZTogQXJyYXkzRHxBcnJheTFELFxuICAgICAgdmFyaWFuY2VFcHNpbG9uOiBudW1iZXIsIHNjYWxlPzogQXJyYXkzRHxBcnJheTFELFxuICAgICAgb2Zmc2V0PzogQXJyYXkzRHxBcnJheTFEKTogQXJyYXkzRDtcbn1cblxuZXhwb3J0IGVudW0gTWF0cml4T3JpZW50YXRpb24ge1xuICBSRUdVTEFSLFxuICBUUkFOU1BPU0VEXG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9tYXRoL2NvbnZfdXRpbCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb3B5MkRfdXRpbCBmcm9tICcuL2NvcHkyZF91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbmV4cG9ydCBjbGFzcyBOREFycmF5TWF0aENQVSBleHRlbmRzIE5EQXJyYXlNYXRoIHtcbiAgY29uc3RydWN0b3Ioc2FmZU1vZGUgPSBmYWxzZSkge1xuICAgIHN1cGVyKHNhZmVNb2RlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3ZhbHVlczogbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LmdldFZhbHVlcygpKX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2hhcGVJbnRlcm5hbDxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIHJldHVybiB0aGlzLmNsb25lSW50ZXJuYWwobmRhcnJheSkucmVzaGFwZTxUMj4obmV3U2hhcGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICBjb25zdCByZXN1bHQgPSBBcnJheTJELnplcm9zKHNpemVSb3dDb2wpO1xuICAgIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIGlucHV0LCBiZWdpblJvd0NvbCwgc2l6ZVJvd0NvbCwgcmVzdWx0LCBbMCwgMF0sIHNpemVSb3dDb2wpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29weTJESW50ZXJuYWwoXG4gICAgICBzb3VyY2U6IEFycmF5MkQsIHNvdXJjZUJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogQXJyYXkyRCxcbiAgICAgIGRlc3RCZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZCB7XG4gICAgY29weTJEX3V0aWwudmFsaWRhdGVTaGFwZXMoc291cmNlU2l6ZVJvd0NvbCwgZGVzdFNpemVSb3dDb2wpO1xuICAgIGNvbnN0IHNyY1ZhbHVlcyA9IHNvdXJjZS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBkc3RWYWx1ZXMgPSBkZXN0LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG4gPSBzb3VyY2VTaXplUm93Q29sWzBdICogc291cmNlU2l6ZVJvd0NvbFsxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47ICsraSkge1xuICAgICAgY29uc3Qgc3JjUm93ID0gc291cmNlQmVnaW5Sb3dDb2xbMF0gKyBNYXRoLmZsb29yKGkgLyBzb3VyY2VTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IHNyY0NvbCA9IHNvdXJjZUJlZ2luUm93Q29sWzFdICsgKGkgJSBzb3VyY2VTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IHNyY09mZiA9IHNyY1JvdyAqIHNvdXJjZS5zaGFwZVsxXSArIHNyY0NvbDtcbiAgICAgIGNvbnN0IGRzdFJvdyA9IGRlc3RCZWdpblJvd0NvbFswXSArIE1hdGguZmxvb3IoaSAvIGRlc3RTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IGRzdENvbCA9IGRlc3RCZWdpblJvd0NvbFsxXSArIChpICUgZGVzdFNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3QgZHN0T2ZmID0gZHN0Um93ICogZGVzdC5zaGFwZVsxXSArIGRzdENvbDtcbiAgICAgIGRzdFZhbHVlc1tkc3RPZmZdID0gc3JjVmFsdWVzW3NyY09mZl07XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNvbmNhdDNESW50ZXJuYWwoeDE6IEFycmF5M0QsIHgyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9XG4gICAgICAgIGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlLCBheGlzKTtcblxuICAgIGNvbnN0IHZhbHVlcyA9IE5EQXJyYXkuemVyb3M8QXJyYXkzRD4ob3V0cHV0U2hhcGUpO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvdXRwdXRTaGFwZVswXTsgaSsrKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IG91dHB1dFNoYXBlWzFdOyBqKyspIHtcbiAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCBvdXRwdXRTaGFwZVsyXTsgaysrKSB7XG4gICAgICAgICAgLy8gU2hhZGVyIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCBpbmRleDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW2ksIGosIGtdO1xuICAgICAgICAgIGxldCB2YWx1ZTogbnVtYmVyO1xuICAgICAgICAgIGlmIChpbmRleFtheGlzXSA8IHgxLnNoYXBlW2F4aXNdKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IHgxLmdldChpLCBqLCBrKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW5kZXhbYXhpc10gLT0geDEuc2hhcGVbYXhpc107XG4gICAgICAgICAgICBjb25zdCBbaTIsIGoyLCBrMl0gPSBpbmRleDtcbiAgICAgICAgICAgIHZhbHVlID0geDIuZ2V0KGkyLCBqMiwgazIpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHZhbHVlcy5zZXQodmFsdWUsIGksIGosIGspO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJQbHVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWwgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcmVzdWx0VmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBjVmFsICsgYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpIHtcbiAgICBjb25zdCBjVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGMxVmFsID0gYzEuZ2V0KCk7XG4gICAgY29uc3QgYzJWYWwgPSBjMi5nZXQoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNWYWx1ZXNbaV0gPSBjMVZhbCAqIGFWYWx1ZXNbaV0gKyBjMlZhbCAqIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogY1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWwgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gY1ZhbCAqIGFWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCBuZWdBID0gdGhpcy5uZWdJbnRlcm5hbChhKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnNjYWxhclBsdXNBcnJheUludGVybmFsKGMsIG5lZ0EpO1xuXG4gICAgbmVnQS5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGFycmF5TWludXNTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTogVCB7XG4gICAgY29uc3QgbmVnQyA9IHRoaXMubmVnSW50ZXJuYWwoYyk7XG4gICAgY29uc3QgcmVzdWx0ID0gdGhpcy5zY2FsYXJQbHVzQXJyYXlJbnRlcm5hbChuZWdDLCBhKTtcblxuICAgIG5lZ0MuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBuZWdJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbChTY2FsYXIuTkVHX09ORSwgYSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsPFQ+KFNjYWxhci5PTkUsIGEsIFNjYWxhci5PTkUsIGIpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1YkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUPihTY2FsYXIuT05FLCBhLCBTY2FsYXIuTkVHX09ORSwgYik7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF0TXVsSW50ZXJuYWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEFycmF5MkQge1xuICAgIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG5cbiAgICBjb25zdCBsZWZ0RGltID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzBdIDogYS5zaGFwZVsxXTtcbiAgICBjb25zdCByaWdodERpbSA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVsxXSA6IGIuc2hhcGVbMF07XG5cbiAgICBjb25zdCBub3JtYWxHZXR0ZXIgPSAobWF0cml4OiBBcnJheTJELCBpOiBudW1iZXIsIGo6IG51bWJlcikgPT5cbiAgICAgICAgbWF0cml4LmdldChpLCBqKTtcbiAgICBjb25zdCB0cmFuc3Bvc2VkR2V0dGVyID0gKG1hdHJpeDogQXJyYXkyRCwgaTogbnVtYmVyLCBqOiBudW1iZXIpID0+XG4gICAgICAgIG1hdHJpeC5nZXQoaiwgaSk7XG5cbiAgICBjb25zdCBhR2V0dGVyID0gKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgP1xuICAgICAgICBub3JtYWxHZXR0ZXIgOlxuICAgICAgICB0cmFuc3Bvc2VkR2V0dGVyO1xuICAgIGNvbnN0IGJHZXR0ZXIgPSAoYk9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/XG4gICAgICAgIG5vcm1hbEdldHRlciA6XG4gICAgICAgIHRyYW5zcG9zZWRHZXR0ZXI7XG4gICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShsZWZ0RGltICogcmlnaHREaW0pO1xuICAgIGxldCBpbmRleCA9IDA7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlZnREaW07ICsraSkge1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCByaWdodERpbTsgKytqKSB7XG4gICAgICAgIGxldCBzdW0gPSAwO1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHNoYXJlZERpbTsgKytrKSB7XG4gICAgICAgICAgLy8gVE9ETzogb3B0aW1pemUgQ1BVIG1hdG11bC5cbiAgICAgICAgICBzdW0gKz0gYUdldHRlcihhLCBpLCBrKSAqIGJHZXR0ZXIoYiwgaywgaik7XG4gICAgICAgIH1cbiAgICAgICAgdmFsdWVzW2luZGV4KytdID0gc3VtO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gQXJyYXkyRC5uZXcoW2xlZnREaW0sIHJpZ2h0RGltXSwgdmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAqIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgY29uc3QgbWF4Um93ID0gTWF0aC5tYXgoYS5zaGFwZVswXSwgYi5zaGFwZVswXSk7XG4gICAgY29uc3QgbWF4Q29sID0gTWF0aC5tYXgoYS5zaGFwZVsxXSwgYi5zaGFwZVsxXSk7XG5cbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG1heFJvdyAqIG1heENvbCk7XG4gICAgbGV0IGluZGV4ID0gMDtcbiAgICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCBtYXhSb3c7IHJvdysrKSB7XG4gICAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBtYXhDb2w7IGNvbCsrKSB7XG4gICAgICAgIHZhbHVlc1tpbmRleCsrXSA9IGEuZ2V0KHJvdyAlIGEuc2hhcGVbMF0sIGNvbCAlIGEuc2hhcGVbMV0pICpcbiAgICAgICAgICAgIGIuZ2V0KHJvdyAlIGIuc2hhcGVbMF0sIGNvbCAlIGIuc2hhcGVbMV0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gQXJyYXkyRC5uZXcoW21heFJvdywgbWF4Q29sXSwgdmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBkaXZpZGVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBiVmFsdWVzID0gYi5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IGFWYWx1ZXNbaV0gLyBiVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6XG4gICAgICBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbHVlID0gYy5nZXQoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IGNWYWx1ZSAvIGFWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBjVmFsdWUgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAvIGNWYWx1ZTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzdW1JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBsZXQgc3VtID0gMDtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBzdW0gKz0gdmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhzdW0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBtaW4gPSBOdW1iZXIuTUFYX1ZBTFVFO1xuICAgIGxldCBtaW5JbmRleCA9IC0xO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIDwgbWluKSB7XG4gICAgICAgIG1pbiA9IHZhbHVlO1xuICAgICAgICBtaW5JbmRleCA9IGk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1pbkluZGV4KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBsZXQgbWF4ID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIGxldCBtYXhJbmRleCA9IC0xO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlID4gbWF4KSB7XG4gICAgICAgIG1heCA9IHZhbHVlO1xuICAgICAgICBtYXhJbmRleCA9IGk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1heEluZGV4KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhFcXVhbHNJbnRlcm5hbCh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IGFyZ01heDEgPSB0aGlzLmFyZ01heEludGVybmFsKHgxKS5nZXQoKTtcbiAgICBjb25zdCBhcmdNYXgyID0gdGhpcy5hcmdNYXhJbnRlcm5hbCh4MikuZ2V0KCk7XG4gICAgaWYgKGlzTmFOKGFyZ01heDEpIHx8IGlzTmFOKGFyZ01heDIpKSB7XG4gICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldygrKGFyZ01heDEgPT09IGFyZ01heDIpKTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0b3BLSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSwgazogbnVtYmVyKTpcbiAgICAgIHt2YWx1ZXM6IEFycmF5MUQsIGluZGljZXM6IEFycmF5MUR9IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHZhbHVlc0FuZEluZGljZXM6IEFycmF5PHt2YWx1ZTogbnVtYmVyLCBpbmRleDogbnVtYmVyfT4gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFsdWVzQW5kSW5kaWNlcy5wdXNoKHt2YWx1ZTogdmFsdWVzW2ldLCBpbmRleDogaX0pO1xuICAgIH1cbiAgICB2YWx1ZXNBbmRJbmRpY2VzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIHJldHVybiBiLnZhbHVlIC0gYS52YWx1ZTtcbiAgICB9KTtcbiAgICBjb25zdCB0b3BrVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShrKTtcbiAgICBjb25zdCB0b3BrSW5kaWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoayk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBrOyBpKyspIHtcbiAgICAgIHRvcGtWYWx1ZXNbaV0gPSB2YWx1ZXNBbmRJbmRpY2VzW2ldLnZhbHVlO1xuICAgICAgdG9wa0luZGljZXNbaV0gPSB2YWx1ZXNBbmRJbmRpY2VzW2ldLmluZGV4O1xuICAgIH1cbiAgICByZXR1cm4ge3ZhbHVlczogQXJyYXkxRC5uZXcodG9wa1ZhbHVlcyksIGluZGljZXM6IEFycmF5MUQubmV3KHRvcGtJbmRpY2VzKX07XG4gIH1cblxuICBwcm90ZWN0ZWQgbWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBsZXQgbWluID0gdmFsdWVzWzBdO1xuICAgIGZvciAobGV0IGkgPSAxOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIFNjYWxhci5uZXcoTmFOKTtcbiAgICAgIH1cbiAgICAgIGlmICh2YWx1ZSA8IG1pbikge1xuICAgICAgICBtaW4gPSB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWluKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGxldCBtYXggPSB2YWx1ZXNbMF07XG4gICAgZm9yIChsZXQgaSA9IDE7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlID4gbWF4KSB7XG4gICAgICAgIG1heCA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhtYXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGV4cEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBNYXRoLmV4cCh2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IE1hdGgubG9nKHZhbHVlKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBsb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB4TWF4ID0gdGhpcy5tYXgobmRhcnJheSk7XG4gICAgY29uc3QgYSA9IHRoaXMuYXJyYXlNaW51c1NjYWxhcihuZGFycmF5LCB4TWF4KTtcbiAgICBjb25zdCBiID0gdGhpcy5leHAoYSk7XG4gICAgY29uc3QgYyA9IHRoaXMuc3VtKGIpO1xuICAgIGNvbnN0IGQgPSB0aGlzLmxvZyhjKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLmFkZCh4TWF4LCBkKTtcblxuICAgIHhNYXguZGlzcG9zZSgpO1xuICAgIGEuZGlzcG9zZSgpO1xuICAgIGIuZGlzcG9zZSgpO1xuICAgIGMuZGlzcG9zZSgpO1xuICAgIGQuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gTWF0aC5tYXgoMCwgdmFsdWVzW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaWdtb2lkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gMSAvICgxICsgTWF0aC5leHAoLXZhbHVlc1tpXSkpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRhbmhJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSB1dGlsLnRhbmgodmFsdWVzW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaW5JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBNYXRoLnNpbih2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN0ZXBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IHZhbHVlID4gMCA/IDEgOiAodmFsdWUgPCAwID8gMCA6IHZhbHVlKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBpbnB1dERlcHRoXSA9IHguc2hhcGU7XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCB4UkNvcm5lcik7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIGZpZWxkU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IHkuc2hhcGVbMV07ICsreUMpIHtcbiAgICAgICAgICBjb25zdCB4Q0Nvcm5lciA9IHlDICogc3RyaWRlIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDTWluID0gTWF0aC5tYXgoMCwgeENDb3JuZXIpO1xuICAgICAgICAgIGNvbnN0IHhDTWF4ID0gTWF0aC5taW4oeENvbHMsIGZpZWxkU2l6ZSArIHhDQ29ybmVyKTtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBpbnB1dERlcHRoOyArK2QxKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGl4ZWwgPSB4LmdldCh4UiwgeEMsIGQxKTtcbiAgICAgICAgICAgICAgICBjb25zdCB3ZWlnaHQgPSB3ZWlnaHRzLmdldCh3Uiwgd0MsIGQxLCBkMik7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIHdlaWdodDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBiaWFzID0gKGJpYXNlcyAhPSBudWxsKSA/IGJpYXNlcy5nZXQoZDIpIDogMDtcbiAgICAgICAgICB5LnNldChkb3RQcm9kICsgYmlhcywgeVIsIHlDLCBkMik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgZHcgPSB0aGlzLmNvbnYyZERlcldlaWdodHMoeCwgZHksIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgZGIgPSB0aGlzLmNvbnYyZERlckJpYXMoZHkpO1xuICAgIGNvbnN0IGR4ID0gdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbChkeSwgd2VpZ2h0cywgbnVsbCwgc3RyaWRlLCBwYWQpO1xuICAgIHJldHVybiB7ZHgsIGRiLCBkd307XG4gIH1cblxuICAvKipcbiAgICogaW1hZ2UgaXMgb2Ygc2hhcGUgW3IsIGMsIGQxXS5cbiAgICogd2VpZ2h0cyBpcyBvZiBzaGFwZSBbRiwgRiwgZDEsIGQyXS5cbiAgICovXG4gIHByb3RlY3RlZCBjb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgICBvcmlnUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCB4RGVwdGhdID0geC5zaGFwZTtcblxuICAgIC8vIERpbGF0ZSB0aGUgaW5wdXQuXG4gICAgY29uc3QgeFJvd3NEaWxhdGVkID0gKHhSb3dzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgICBjb25zdCB4Q29sc0RpbGF0ZWQgPSAoeENvbHMgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuXG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93c0RpbGF0ZWQsIHhDb2xzRGlsYXRlZCwgb3JpZ091dHB1dERlcHRoXSwgZlNpemUsIG9yaWdJbnB1dERlcHRoLCAxLFxuICAgICAgICBwYWQpO1xuICAgIGNvbnN0IHkgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3JpZ0lucHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKHhSQ29ybmVyIC8gb3JpZ1N0cmlkZSkpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCAoZlNpemUgKyB4UkNvcm5lcikgLyBvcmlnU3RyaWRlKTtcblxuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCBNYXRoLmNlaWwoeENDb3JuZXIgLyBvcmlnU3RyaWRlKSk7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgKGZTaXplICsgeENDb3JuZXIpIC8gb3JpZ1N0cmlkZSk7XG5cbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAqIG9yaWdTdHJpZGUgLSB4UkNvcm5lcjtcblxuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDICogb3JpZ1N0cmlkZSAtIHhDQ29ybmVyO1xuXG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBvcmlnT3V0cHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9XG4gICAgICAgICAgICAgICAgICAgIHdlaWdodHMuZ2V0KGZTaXplIC0gMSAtIHdSLCBmU2l6ZSAtIDEgLSB3QywgZDIsIGQxKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJpYXMgPSBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXQoZDIpIDogMDtcbiAgICAgICAgICB5LnNldChkb3RQcm9kICsgYmlhcywgeVIsIHlDLCBkMik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICAvKipcbiAgICogaW1hZ2UgaXMgb2Ygc2hhcGUgW3IsIGMsIGQxXS5cbiAgICogd2VpZ2h0cyBpcyBvZiBzaGFwZSBbRiwgRiwgZDEsIGQyXS5cbiAgICovXG4gIHByb3RlY3RlZCBjb252MmRUcmFuc3Bvc2VTaGFkZXJMaWtlKFxuICAgICAgeDogQXJyYXkzRCwgb3JpZ1dlaWdodHM6IEFycmF5NEQsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGZTaXplID0gb3JpZ1dlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IG9yaWdXZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG9yaWdPdXRwdXREZXB0aCA9IG9yaWdXZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIHhEZXB0aF0gPSB4LnNoYXBlO1xuXG4gICAgLy8gRGlsYXRlIHRoZSBpbnB1dC5cbiAgICBjb25zdCB4Um93c0RpbGF0ZWQgPSAoeFJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IHhDb2xzRGlsYXRlZCA9ICh4Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzRGlsYXRlZCwgeENvbHNEaWxhdGVkLCBvcmlnT3V0cHV0RGVwdGhdLCBmU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIDEsXG4gICAgICAgIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuXG4gICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG9yaWdJbnB1dERlcHRoOyArK2QyKSB7XG4gICAgICBmb3IgKGxldCB5UiA9IDA7IHlSIDwgeS5zaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIC8vIFNoYWRlciBjb2RlIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgLSBwYWQ7XG4gICAgICAgICAgbGV0IGRvdFByb2QgPSAwO1xuICAgICAgICAgIGZvciAobGV0IHdSID0gMDsgd1IgPCBmU2l6ZTsgKyt3Uikge1xuICAgICAgICAgICAgY29uc3QgeFIgPSAoeFJDb3JuZXIgKyB3UikgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgaWYgKHhSIDwgMCB8fCB4UiA+PSB4Um93cyB8fCBNYXRoLmZsb29yKHhSKSAhPT0geFIpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGxldCB3QyA9IDA7IHdDIDwgZlNpemU7ICsrd0MpIHtcbiAgICAgICAgICAgICAgY29uc3QgeEMgPSAoeENDb3JuZXIgKyB3QykgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgICBpZiAoeEMgPCAwIHx8IHhDID49IHhDb2xzIHx8IE1hdGguZmxvb3IoeEMpICE9PSB4Qykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBvcmlnT3V0cHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9XG4gICAgICAgICAgICAgICAgICAgIG9yaWdXZWlnaHRzLmdldChmU2l6ZSAtIDEgLSB3UiwgZlNpemUgLSAxIC0gd0MsIGQyLCBkMSk7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIHdlaWdodDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICB5LnNldChkb3RQcm9kLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIGNvbnYyZERlcldlaWdodHMoXG4gICAgICB4OiBBcnJheTNELCBkWTogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTREIHtcbiAgICBjb25zdCBpbnB1dERlcHRoID0geC5zaGFwZVsyXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IGRZLnNoYXBlWzJdO1xuICAgIGNvbnN0IHdlaWdodHNTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1NoYXBlNEQoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICBjb25zdCBkVyA9IEFycmF5NEQuemVyb3Mod2VpZ2h0c1NoYXBlKTtcblxuICAgIGNvbnN0IHlOdW1Sb3dzID0gZFkuc2hhcGVbMF07XG4gICAgY29uc3QgeU51bUNvbHMgPSBkWS5zaGFwZVsxXTtcbiAgICBjb25zdCB4TnVtUm93cyA9IHguc2hhcGVbMF07XG4gICAgY29uc3QgeE51bUNvbHMgPSB4LnNoYXBlWzFdO1xuXG4gICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICBjb25zdCB5Uk1pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgoemVyb1BhZCAtIHdSKSAvIHN0cmlkZSkpO1xuICAgICAgY29uc3QgeVJNYXggPSBNYXRoLm1pbih5TnVtUm93cywgKHhOdW1Sb3dzICsgemVyb1BhZCAtIHdSKSAvIHN0cmlkZSk7XG5cbiAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICBjb25zdCB5Q01pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgoemVyb1BhZCAtIHdDKSAvIHN0cmlkZSkpO1xuICAgICAgICBjb25zdCB5Q01heCA9IE1hdGgubWluKHlOdW1Db2xzLCAoeE51bUNvbHMgKyB6ZXJvUGFkIC0gd0MpIC8gc3RyaWRlKTtcblxuICAgICAgICBmb3IgKGxldCBkMSA9IDA7IGQxIDwgaW5wdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgICAgICAgLy8gTmVlZCB0byBjb252b2x2ZS5cbiAgICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICAgIGZvciAobGV0IHlSID0geVJNaW47IHlSIDwgeVJNYXg7ICsreVIpIHtcbiAgICAgICAgICAgICAgY29uc3QgeFIgPSB3UiArIHlSICogc3RyaWRlIC0gemVyb1BhZDtcbiAgICAgICAgICAgICAgZm9yIChsZXQgeUMgPSB5Q01pbjsgeUMgPCB5Q01heDsgKyt5Qykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHhDID0gd0MgKyB5QyAqIHN0cmlkZSAtIHplcm9QYWQ7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSB4LmdldCh4UiwgeEMsIGQxKSAqIGRZLmdldCh5UiwgeUMsIGQyKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZFcuc2V0KGRvdFByb2QsIHdSLCB3QywgZDEsIGQyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGRXO1xuICB9XG5cbiAgY29udjJkRGVyQmlhcyhkWTogQXJyYXkzRCk6IEFycmF5MUQge1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgbnVtUm93cyA9IGRZLnNoYXBlWzBdO1xuICAgIGNvbnN0IG51bUNvbHMgPSBkWS5zaGFwZVsxXTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG91dHB1dERlcHRoKTtcbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3V0cHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGxldCBzdW0gPSAwO1xuICAgICAgZm9yIChsZXQgciA9IDA7IHIgPCBudW1Sb3dzOyArK3IpIHtcbiAgICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBudW1Db2xzOyArK2MpIHtcbiAgICAgICAgICBzdW0gKz0gZFkuZ2V0KHIsIGMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdmFsdWVzW2QyXSA9IHN1bTtcbiAgICB9XG4gICAgcmV0dXJuIEFycmF5MUQubmV3KHZhbHVlcyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KHQ6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUIHtcbiAgICBjb25zdCBuZXdTaGFwZTogbnVtYmVyW10gPSBuZXcgQXJyYXkodC5yYW5rKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld1NoYXBlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBuZXdTaGFwZVtpXSA9IHQuc2hhcGVbbmV3RGltW2ldXTtcbiAgICB9XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh0LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IHQuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgcmVzdWx0ID0gTkRBcnJheS5tYWtlPFQ+KG5ld1NoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHQuc2l6ZTsgKytpKSB7XG4gICAgICBjb25zdCBsb2MgPSB0LmluZGV4VG9Mb2MoaSk7XG5cbiAgICAgIC8vIFBlcm11dGUgbG9jYXRpb24uXG4gICAgICBjb25zdCBuZXdMb2M6IG51bWJlcltdID0gbmV3IEFycmF5KGxvYy5sZW5ndGgpO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZXdMb2MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbmV3TG9jW2ldID0gbG9jW25ld0RpbVtpXV07XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG5ld0luZGV4ID0gcmVzdWx0LmxvY1RvSW5kZXgobmV3TG9jKTtcbiAgICAgIHJlc3VsdFZhbHVlc1tuZXdJbmRleF0gPSB2YWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIHBvb2woXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIsXG4gICAgICBwb29sVHlwZTogJ21heCd8J21pbid8J2F2ZycpIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBkZXB0aF0gPSB4LnNoYXBlO1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbeFJvd3MsIHhDb2xzLCBkZXB0aF0sIGZTaXplLCBkZXB0aCwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IHkgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgeFJDb3JuZXIpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCBmU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IHkuc2hhcGVbMV07ICsreUMpIHtcbiAgICAgICAgICBjb25zdCB4Q0Nvcm5lciA9IHlDICogc3RyaWRlIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDTWluID0gTWF0aC5tYXgoMCwgeENDb3JuZXIpO1xuICAgICAgICAgIGNvbnN0IHhDTWF4ID0gTWF0aC5taW4oeENvbHMsIGZTaXplICsgeENDb3JuZXIpO1xuXG5cbiAgICAgICAgICBsZXQgbWluTWF4VmFsdWUgPVxuICAgICAgICAgICAgICAocG9vbFR5cGUgPT09ICdtYXgnID8gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZIDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5QT1NJVElWRV9JTkZJTklUWSk7XG4gICAgICAgICAgbGV0IGF2Z1ZhbHVlID0gMDtcblxuICAgICAgICAgIGZvciAobGV0IHhSID0geFJNaW47IHhSIDwgeFJNYXg7ICsreFIpIHtcbiAgICAgICAgICAgIGNvbnN0IHdSID0geFIgLSB4UkNvcm5lcjtcbiAgICAgICAgICAgIGZvciAobGV0IHhDID0geENNaW47IHhDIDwgeENNYXg7ICsreEMpIHtcbiAgICAgICAgICAgICAgY29uc3Qgd0MgPSB4QyAtIHhDQ29ybmVyO1xuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZCk7XG4gICAgICAgICAgICAgIGlmIChpc05hTihwaXhlbCkpIHtcbiAgICAgICAgICAgICAgICBtaW5NYXhWYWx1ZSA9IE5hTjtcbiAgICAgICAgICAgICAgICBhdmdWYWx1ZSA9IE5hTjtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoKHBvb2xUeXBlID09PSAnbWF4JyAmJiBwaXhlbCA+IG1pbk1heFZhbHVlKSB8fFxuICAgICAgICAgICAgICAgICAgKHBvb2xUeXBlID09PSAnbWluJyAmJiBwaXhlbCA8IG1pbk1heFZhbHVlKSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gcGl4ZWw7XG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAocG9vbFR5cGUgPT09ICdhdmcnKSB7XG4gICAgICAgICAgICAgICAgYXZnVmFsdWUgKz0gcGl4ZWwgLyAoZlNpemUgKiBmU2l6ZSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc05hTihtaW5NYXhWYWx1ZSkpIHtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHkuc2V0KHBvb2xUeXBlID09PSAnYXZnJyA/IGF2Z1ZhbHVlIDogbWluTWF4VmFsdWUsIHlSLCB5QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIHRoaXMucG9vbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdtYXgnKTtcbiAgfVxuXG4gIG1heFBvb2xQb3NpdGlvbnMoeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKSB7XG4gICAgY29uc3QgW3hSb3dzLCB4Q29scywgZGVwdGhdID0geC5zaGFwZTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRCh4LnNoYXBlLCBmU2l6ZSwgZGVwdGgsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCBtYXhQb3NpdGlvbnMgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCBvdXRwdXRTaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSICogc3RyaWRlIC0gcGFkO1xuICAgICAgICBjb25zdCB4Uk1pbiA9IE1hdGgubWF4KDAsIHhSQ29ybmVyKTtcbiAgICAgICAgY29uc3QgeFJNYXggPSBNYXRoLm1pbih4Um93cywgZlNpemUgKyB4UkNvcm5lcik7XG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCBvdXRwdXRTaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZlNpemUgKyB4Q0Nvcm5lcik7XG4gICAgICAgICAgbGV0IG1heFZhbHVlID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgICAgICAgIGxldCBtYXhQb3NpdGlvbiA9IC0xO1xuICAgICAgICAgIGZvciAobGV0IHhSID0geFJNaW47IHhSIDwgeFJNYXg7ICsreFIpIHtcbiAgICAgICAgICAgIGNvbnN0IHdSID0geFIgLSB4UkNvcm5lcjtcbiAgICAgICAgICAgIGZvciAobGV0IHhDID0geENNaW47IHhDIDwgeENNYXg7ICsreEMpIHtcbiAgICAgICAgICAgICAgY29uc3Qgd0MgPSB4QyAtIHhDQ29ybmVyO1xuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZCk7XG4gICAgICAgICAgICAgIGlmIChwaXhlbCA+IG1heFZhbHVlKSB7XG4gICAgICAgICAgICAgICAgbWF4VmFsdWUgPSBwaXhlbDtcbiAgICAgICAgICAgICAgICBtYXhQb3NpdGlvbiA9IHdSICogZlNpemUgKyB3QztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBtYXhQb3NpdGlvbnMuc2V0KG1heFBvc2l0aW9uLCB5UiwgeUMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBtYXhQb3NpdGlvbnM7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoXG4gICAgICBkeTogQXJyYXkzRCwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9zaXRpb25zID0gdGhpcy5tYXhQb29sUG9zaXRpb25zKHgsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IFtkeVJvd3MsIGR5Q29scywgZGVwdGhdID0gZHkuc2hhcGU7XG5cbiAgICAvLyBEaWxhdGUgdGhlIGlucHV0LlxuICAgIGNvbnN0IGR5Um93c0RpbGF0ZWQgPSAoZHlSb3dzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgICBjb25zdCBkeENvbHNEaWxhdGVkID0gKGR5Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW2R5Um93c0RpbGF0ZWQsIGR4Q29sc0RpbGF0ZWQsIGRlcHRoXSwgZlNpemUsIGRlcHRoLCAxLCBwYWQpO1xuICAgIGNvbnN0IGR4ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IGR4UiA9IDA7IGR4UiA8IGR4LnNoYXBlWzBdOyArK2R4Uikge1xuICAgICAgICBmb3IgKGxldCBkeEMgPSAwOyBkeEMgPCBkeC5zaGFwZVsxXTsgKytkeEMpIHtcbiAgICAgICAgICAvLyBTaGFkZXIgY29kZSBiZWdpbnMuXG4gICAgICAgICAgY29uc3QgZHlSQ29ybmVyID0gZHhSIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IGR5Q0Nvcm5lciA9IGR4QyAtIHBhZDtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICAgICAgICBjb25zdCBkeVIgPSAoZHlSQ29ybmVyICsgd1IpIC8gb3JpZ1N0cmlkZTtcbiAgICAgICAgICAgIGlmIChkeVIgPCAwIHx8IGR5UiA+PSBkeVJvd3MgfHwgTWF0aC5mbG9vcihkeVIpICE9PSBkeVIpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGxldCB3QyA9IDA7IHdDIDwgZlNpemU7ICsrd0MpIHtcbiAgICAgICAgICAgICAgY29uc3QgZHlDID0gKGR5Q0Nvcm5lciArIHdDKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICAgIGlmIChkeUMgPCAwIHx8IGR5QyA+PSBkeUNvbHMgfHwgTWF0aC5mbG9vcihkeUMpICE9PSBkeUMpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBtYXhQb3MgPSBmU2l6ZSAqIGZTaXplIC0gMSAtIG1heFBvc2l0aW9ucy5nZXQoZHlSLCBkeUMsIGQpO1xuICAgICAgICAgICAgICBjb25zdCBjdXJQb3MgPSB3UiAqIGZTaXplICsgd0M7XG5cbiAgICAgICAgICAgICAgY29uc3QgbWFzayA9IG1heFBvcyA9PT0gY3VyUG9zID8gMSA6IDA7XG4gICAgICAgICAgICAgIGlmIChtYXNrID09PSAwKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IGR5LmdldChkeVIsIGR5QywgZCk7XG4gICAgICAgICAgICAgIGRvdFByb2QgKz0gcGl4ZWwgKiBtYXNrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBkeC5zZXQoZG90UHJvZCwgZHhSLCBkeEMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkeDtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5Qb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5wb29sKHgsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21pbicpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGF2Z1Bvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnBvb2woeCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnYXZnJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGFsaWduQ29ybmVyczogYm9vbGVhbik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG91dHB1dCA9IEFycmF5M0QuemVyb3MoW25ld1NoYXBlMkRbMF0sIG5ld1NoYXBlMkRbMV0sIHguc2hhcGVbMl1dKTtcblxuICAgIGNvbnN0IGVmZmVjdGl2ZUlucHV0U2l6ZSA9XG4gICAgICAgIGFsaWduQ29ybmVycyA/IFt4LnNoYXBlWzBdIC0gMSwgeC5zaGFwZVsxXSAtIDEsIHguc2hhcGVbMl1dIDogeC5zaGFwZTtcbiAgICBjb25zdCBlZmZlY3RpdmVPdXRwdXRTaXplID0gYWxpZ25Db3JuZXJzID9cbiAgICAgICAgW291dHB1dC5zaGFwZVswXSAtIDEsIG91dHB1dC5zaGFwZVsxXSAtIDEsIG91dHB1dC5zaGFwZVsyXV0gOlxuICAgICAgICBvdXRwdXQuc2hhcGU7XG4gICAgZm9yIChsZXQgciA9IDA7IHIgPCBvdXRwdXQuc2hhcGVbMF07IHIrKykge1xuICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBvdXRwdXQuc2hhcGVbMV07IGMrKykge1xuICAgICAgICBmb3IgKGxldCBkID0gMDsgZCA8IG91dHB1dC5zaGFwZVsyXTsgZCsrKSB7XG4gICAgICAgICAgLy8gQmVnaW4gc2hhZGVyLlxuXG4gICAgICAgICAgLy8gQ29tcHV0ZSB0aGUgZnJhY3Rpb25hbCBpbmRleCBvZiB0aGUgc291cmNlLlxuICAgICAgICAgIGNvbnN0IHNvdXJjZUZyYWNSb3cgPVxuICAgICAgICAgICAgICAoZWZmZWN0aXZlSW5wdXRTaXplWzBdKSAqIHIgLyAoZWZmZWN0aXZlT3V0cHV0U2l6ZVswXSk7XG4gICAgICAgICAgY29uc3Qgc291cmNlRnJhY0NvbCA9XG4gICAgICAgICAgICAgIChlZmZlY3RpdmVJbnB1dFNpemVbMV0pICogYyAvIChlZmZlY3RpdmVPdXRwdXRTaXplWzFdKTtcblxuICAgICAgICAgIGNvbnN0IHNvdXJjZVJvd0Zsb29yID0gTWF0aC5mbG9vcihzb3VyY2VGcmFjUm93KTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VSb3dDZWlsID1cbiAgICAgICAgICAgICAgTWF0aC5taW4oeC5zaGFwZVswXSAtIDEsIE1hdGguY2VpbChzb3VyY2VGcmFjUm93KSk7XG4gICAgICAgICAgY29uc3Qgc291cmNlQ29sRmxvb3IgPSBNYXRoLmZsb29yKHNvdXJjZUZyYWNDb2wpO1xuICAgICAgICAgIGNvbnN0IHNvdXJjZUNvbENlaWwgPVxuICAgICAgICAgICAgICBNYXRoLm1pbih4LnNoYXBlWzFdIC0gMSwgTWF0aC5jZWlsKHNvdXJjZUZyYWNDb2wpKTtcblxuICAgICAgICAgIGNvbnN0IHRvcExlZnQgPSB4LmdldChzb3VyY2VSb3dGbG9vciwgc291cmNlQ29sRmxvb3IsIGQpO1xuICAgICAgICAgIGNvbnN0IGJvdHRvbUxlZnQgPSB4LmdldChzb3VyY2VSb3dDZWlsLCBzb3VyY2VDb2xGbG9vciwgZCk7XG4gICAgICAgICAgY29uc3QgdG9wUmlnaHQgPSB4LmdldChzb3VyY2VSb3dGbG9vciwgc291cmNlQ29sQ2VpbCwgZCk7XG4gICAgICAgICAgY29uc3QgYm90dG9tUmlnaHQgPSB4LmdldChzb3VyY2VSb3dDZWlsLCBzb3VyY2VDb2xDZWlsLCBkKTtcblxuICAgICAgICAgIGNvbnN0IHJvd0ZyYWMgPSBzb3VyY2VGcmFjUm93IC0gc291cmNlUm93Rmxvb3I7XG4gICAgICAgICAgY29uc3QgY29sRnJhYyA9IHNvdXJjZUZyYWNDb2wgLSBzb3VyY2VDb2xGbG9vcjtcblxuICAgICAgICAgIGNvbnN0IHRvcCA9IHRvcExlZnQgKyAodG9wUmlnaHQgLSB0b3BMZWZ0KSAqIGNvbEZyYWM7XG4gICAgICAgICAgY29uc3QgYm90dG9tID0gYm90dG9tTGVmdCArIChib3R0b21SaWdodCAtIGJvdHRvbUxlZnQpICogY29sRnJhYztcbiAgICAgICAgICBjb25zdCBuZXdWYWx1ZSA9IHRvcCArIChib3R0b20gLSB0b3ApICogcm93RnJhYztcblxuICAgICAgICAgIG91dHB1dC5zZXQobmV3VmFsdWUsIHIsIGMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dHB1dDtcbiAgfVxuXG4gIHByb3RlY3RlZCBiYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbWVhbjogQXJyYXkzRHxBcnJheTFELCB2YXJpYW5jZTogQXJyYXkzRHxBcnJheTFELFxuICAgICAgdmFyaWFuY2VFcHNpbG9uID0gLjAwMSwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEIHtcbiAgICBjb25zdCB4VmFsdWVzID0geC5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBtZWFuVmFsdWVzID0gbWVhbi5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCB2YXJpYW5jZVZhbHVlcyA9IHZhcmlhbmNlLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHNjYWxlVmFsdWVzID0gc2NhbGUgPyBzY2FsZS5nZXRWYWx1ZXMoKSA6IG5ldyBGbG9hdDMyQXJyYXkoWzFdKTtcbiAgICBjb25zdCBvZmZzZXRWYWx1ZXMgPSBvZmZzZXQgPyBvZmZzZXQuZ2V0VmFsdWVzKCkgOiBuZXcgRmxvYXQzMkFycmF5KFswXSk7XG4gICAgY29uc3Qgb3V0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh4VmFsdWVzLmxlbmd0aCk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHhWYWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIG91dFZhbHVlc1tpXSA9IG9mZnNldFZhbHVlc1tpICUgb2Zmc2V0VmFsdWVzLmxlbmd0aF0gK1xuICAgICAgICAgICh4VmFsdWVzW2ldIC0gbWVhblZhbHVlc1tpICUgbWVhblZhbHVlcy5sZW5ndGhdKSAqXG4gICAgICAgICAgICAgIHNjYWxlVmFsdWVzW2kgJSBzY2FsZVZhbHVlcy5sZW5ndGhdIC9cbiAgICAgICAgICAgICAgTWF0aC5zcXJ0KFxuICAgICAgICAgICAgICAgICAgdmFyaWFuY2VWYWx1ZXNbaSAlIHZhcmlhbmNlVmFsdWVzLmxlbmd0aF0gKyB2YXJpYW5jZUVwc2lsb24pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KHguc2hhcGUsIHt2YWx1ZXM6IG91dFZhbHVlc30pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL2NvbnZfdXRpbCc7XG5pbXBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoJztcbmltcG9ydCAqIGFzIG5kYXJyYXkgZnJvbSAnLi9uZGFycmF5JztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgQXJyYXkzRCwgQXJyYXk0RCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL25kYXJyYXknO1xuaW1wb3J0ICogYXMgYWRkc2NhbGVkbWF0X2dwdSBmcm9tICcuL3dlYmdsL2FkZHNjYWxlZG1hdF9ncHUnO1xuaW1wb3J0ICogYXMgYWRkc3VibXVsZGl2X2dwdSBmcm9tICcuL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUnO1xuaW1wb3J0IHtPcGVyYW5kVHlwZX0gZnJvbSAnLi93ZWJnbC9hZGRzdWJtdWxkaXZfZ3B1JztcbmltcG9ydCAqIGFzIGFyZ21heGVxdWFsc19ncHUgZnJvbSAnLi93ZWJnbC9hcmdtYXhlcXVhbHNfZ3B1JztcbmltcG9ydCAqIGFzIGFyZ21pbm1heF9ncHUgZnJvbSAnLi93ZWJnbC9hcmdtaW5tYXhfZ3B1JztcbmltcG9ydCAqIGFzIGF2Z19wb29sX2dwdSBmcm9tICcuL3dlYmdsL2F2Z19wb29sX2dwdSc7XG5pbXBvcnQgKiBhcyBiYXRjaG5vcm1fZ3B1IGZyb20gJy4vd2ViZ2wvYmF0Y2hub3JtX2dwdSc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF9ncHUgZnJvbSAnLi93ZWJnbC9jb25jYXQzZF9ncHUnO1xuaW1wb3J0ICogYXMgY29udl9iYWNrcHJvcF9ncHUgZnJvbSAnLi93ZWJnbC9jb252X2JhY2twcm9wX2dwdSc7XG5pbXBvcnQgKiBhcyBjb252X2dwdSBmcm9tICcuL3dlYmdsL2NvbnZfZ3B1JztcbmltcG9ydCAqIGFzIGNvcHlfZ3B1IGZyb20gJy4vd2ViZ2wvY29weV9ncHUnO1xuaW1wb3J0ICogYXMgZXhwX2dwdSBmcm9tICcuL3dlYmdsL2V4cF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBncGdwdV91dGlsIGZyb20gJy4vd2ViZ2wvZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyBsb2dfZ3B1IGZyb20gJy4vd2ViZ2wvbG9nX2dwdSc7XG5pbXBvcnQgKiBhcyBsb2dzdW1leHBfZ3B1IGZyb20gJy4vd2ViZ2wvbG9nc3VtZXhwX2dwdSc7XG5pbXBvcnQgKiBhcyBtYXhfcG9vbF9iYWNrcHJvcF9ncHUgZnJvbSAnLi93ZWJnbC9tYXhfcG9vbF9iYWNrcHJvcF9ncHUnO1xuaW1wb3J0ICogYXMgbWF4X3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvbWF4X3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIG1pbl9wb29sX2dwdSBmcm9tICcuL3dlYmdsL21pbl9wb29sX2dwdSc7XG5pbXBvcnQgKiBhcyBtaW5tYXhfZ3B1IGZyb20gJy4vd2ViZ2wvbWlubWF4X2dwdSc7XG5pbXBvcnQgKiBhcyBtdWxtYXRfZ3B1IGZyb20gJy4vd2ViZ2wvbXVsbWF0X2dwdSc7XG5pbXBvcnQgKiBhcyBuZWdfZ3B1IGZyb20gJy4vd2ViZ2wvbmVnX2dwdSc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3dlYmdsL3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIHJlZHVjZXN1bV9ncHUgZnJvbSAnLi93ZWJnbC9yZWR1Y2VzdW1fZ3B1JztcbmltcG9ydCAqIGFzIHJlbHVfZ3B1IGZyb20gJy4vd2ViZ2wvcmVsdV9ncHUnO1xuaW1wb3J0ICogYXMgcmVzaGFwZV9ncHUgZnJvbSAnLi93ZWJnbC9yZXNoYXBlX2dwdSc7XG5pbXBvcnQgKiBhcyByZXNpemVfYmlsaW5lYXJfZ3B1IGZyb20gJy4vd2ViZ2wvcmVzaXplX2JpbGluZWFyX2dwdSc7XG5pbXBvcnQgKiBhcyBzaGFkZXJfY29tcGlsZXIgZnJvbSAnLi93ZWJnbC9zaGFkZXJfY29tcGlsZXInO1xuaW1wb3J0ICogYXMgc2lnbW9pZF9ncHUgZnJvbSAnLi93ZWJnbC9zaWdtb2lkX2dwdSc7XG5pbXBvcnQgKiBhcyBzdGVwX2dwdSBmcm9tICcuL3dlYmdsL3N0ZXBfZ3B1JztcbmltcG9ydCB7VGV4dHVyZU1hbmFnZXJ9IGZyb20gJy4vd2ViZ2wvdGV4dHVyZV9tYW5hZ2VyJztcbmltcG9ydCAqIGFzIHRyaWdfZ3B1IGZyb20gJy4vd2ViZ2wvdHJpZ19ncHUnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsL3dlYmdsX3V0aWwnO1xuXG5jb25zdCBBUkdNQVhfUFJPRyA9ICdhcmdtYXgnO1xuY29uc3QgQVJHTUFYX0VRVUFMU19QUk9HID0gJ2FyZ21heGVxdWFscyc7XG5jb25zdCBBUkdNSU5fUFJPRyA9ICdhcmdtaW4nO1xuXG5jb25zdCBCQVRDSE5PUk1fUFJPRyA9ICdiYXRjaG5vcm0nO1xuXG5jb25zdCBDT1BZX1BST0cgPSAnY29weSc7XG5jb25zdCBDT05DQVRfUFJPRyA9ICdjb25jYXQnO1xuXG4vLyBNYXRyaXggYWxnZWJyYS5cbmNvbnN0IEFERF9TQ0FMRURfTUFUX1BST0cgPSAnYWRkc2NhbGVkbWF0JztcbmNvbnN0IE1BVE1VTF9QUk9HID0gJ21hdG11bCc7XG5cbi8vIEVsZW1lbnQtd2lzZSBvcHMuXG5jb25zdCBSRUxVX1BST0cgPSAncmVsdSc7XG5jb25zdCBUQU5IX1BST0cgPSAndGFuaCc7XG5jb25zdCBTSU5fUFJPRyA9ICdzaW4nO1xuY29uc3QgU0lHTU9JRF9QUk9HID0gJ3NpZ21vaWQnO1xuY29uc3QgTUFYX1BST0cgPSAnbWF4JztcbmNvbnN0IE1JTl9QUk9HID0gJ21pbic7XG5jb25zdCBORUdfUFJPRyA9ICduZWcnO1xuY29uc3QgRVhQX1BST0cgPSAnZXhwJztcbmNvbnN0IExPR19QUk9HID0gJ2xvZyc7XG5jb25zdCBTVU1fUFJPRyA9ICdzdW0nO1xuY29uc3QgU1RFUF9QUk9HID0gJ3N0ZXAnO1xuY29uc3QgTE9HU1VNRVhQX1BST0cgPSAnbG9nc3VtZXhwJztcbmNvbnN0IFJFU0hBUEVfUFJPRyA9ICdyZXNoYXBlJztcbmNvbnN0IEFERF9TVU1fTVVMX0RJVl9QUk9HID0gJ2FkZHN1bW11bGRpdic7XG5cbi8vIENvbnZvbHV0aW9uLlxuY29uc3QgQ09OVjJEX1BST0cgPSAnY29udic7XG5jb25zdCBDT05WMkRfVFJBTlNQT1NFX1BST0cgPSAnY29udl90cmFuc3Bvc2UnO1xuY29uc3QgQ09OVjJEX0RFUldfUFJPRyA9ICdjb252X2RlcncnO1xuY29uc3QgQ09OVjJEX0RFUkJfUFJPRyA9ICdjb252X2RlcmInO1xuY29uc3QgTUFYX1BPT0xfUFJPRyA9ICdtYXhwb29sJztcbmNvbnN0IE1BWF9QT09MX1BPU0lUSU9OU19QUk9HID0gJ21heHBvb2xfcG9zbic7XG5jb25zdCBNQVhfUE9PTF9CQUNLUFJPUF9QUk9HID0gJ21heHBvb2xfYmFja3Byb3AnO1xuY29uc3QgTUlOX1BPT0xfUFJPRyA9ICdtaW5wb29sJztcbmNvbnN0IEFWR19QT09MX1BST0cgPSAnYXZncG9vbCc7XG5cbmNvbnN0IFJFU0laRV9CSUxJTkVBUl9QUk9HID0gJ3Jlc2l6ZWJpbGluJztcblxuZnVuY3Rpb24gbWFrZUNvcHlQcm9ncmFtTmFtZShcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBkZXN0U2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXBlTmFtZSA9IGAke3NvdXJjZVNoYXBlUm93Q29sWzBdfV8ke3NvdXJjZVNoYXBlUm93Q29sWzFdfWA7XG4gIGNvbnN0IHNyY1NpemVOYW1lID0gYCR7c291cmNlU2l6ZVJvd0NvbFswXX1fJHtzb3VyY2VTaXplUm93Q29sWzFdfWA7XG4gIGNvbnN0IGRzdFNpemVOYW1lID0gYCR7ZGVzdFNpemVSb3dDb2xbMF19XyR7ZGVzdFNpemVSb3dDb2xbMV19YDtcbiAgcmV0dXJuIGAke0NPUFlfUFJPR31fJHtzaGFwZU5hbWV9XyR7c3JjU2l6ZU5hbWV9XyR7ZHN0U2l6ZU5hbWV9YDtcbn1cblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlNYXRoR1BVIGV4dGVuZHMgTkRBcnJheU1hdGgge1xuICBwcml2YXRlIGdwZ3B1OiBHUEdQVUNvbnRleHQ7XG4gIHByaXZhdGUgdGV4dHVyZU1hbmFnZXI6IFRleHR1cmVNYW5hZ2VyO1xuICBwcml2YXRlIHByb2dyYW1DYWNoZToge1trZXk6IHN0cmluZ106IFdlYkdMUHJvZ3JhbX0gPSB7fTtcbiAgcHJpdmF0ZSBncGdwdUNyZWF0ZWRMb2NhbGx5OiBib29sZWFuO1xuXG4gIGNvbnN0cnVjdG9yKGdwZ3B1PzogR1BHUFVDb250ZXh0LCBzYWZlTW9kZSA9IHRydWUpIHtcbiAgICBzdXBlcihzYWZlTW9kZSk7XG4gICAgaWYgKGdwZ3B1ID09IG51bGwpIHtcbiAgICAgIGNvbnN0IGdsID0gZ3BncHVfdXRpbC5jcmVhdGVXZWJHTENvbnRleHQoKTtcbiAgICAgIHRoaXMuZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KGdsKTtcbiAgICAgIHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSA9IHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZ3BncHUgPSBncGdwdTtcbiAgICAgIHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSA9IGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIgPSBuZXcgVGV4dHVyZU1hbmFnZXIodGhpcy5ncGdwdSk7XG5cbiAgICBuZGFycmF5LmluaXRpYWxpemVHUFUodGhpcy5ncGdwdSwgdGhpcy50ZXh0dXJlTWFuYWdlcik7XG4gIH1cblxuICBnZXRHUEdQVUNvbnRleHQoKTogR1BHUFVDb250ZXh0IHtcbiAgICByZXR1cm4gdGhpcy5ncGdwdTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIG1ha2VDb3B5UHJvZ3JhbU5hbWUodGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDLCB0ZXh0dXJlU2hhcGVSQyksXG4gICAgICAgICgpID0+IGNvcHlfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDLCB0ZXh0dXJlU2hhcGVSQykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgY29weV9ncHUuY29weShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDLCBbMCwgMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDLCByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQywgWzAsIDBdLCB0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNoYXBlSW50ZXJuYWw8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgbmRhcnJheTogVDEsIG5ld1NoYXBlOiBudW1iZXJbXSk6IFQyIHtcbiAgICBsZXQgbmV3VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgICBzd2l0Y2ggKG5ld1NoYXBlLmxlbmd0aCkge1xuICAgICAgY2FzZSAwOlxuICAgICAgICBuZXdUZXhTaGFwZSA9IFsxLCAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCBuZXdTaGFwZVsxXV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICBuZXdUZXhTaGFwZSA9IFtuZXdTaGFwZVswXSwgbmV3U2hhcGVbMV0gKiBuZXdTaGFwZVsyXV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhyb3cgRXJyb3IoXG4gICAgICAgICAgICBgUmVzaGFwZXMgaW50byAke25ld1NoYXBlLmxlbmd0aH0tZGltIG5kYXJyYXkgaXMgbm90IHlldCBgICtcbiAgICAgICAgICAgIGBzdXBwb3J0ZWQgb24gR1BVYCk7XG4gICAgfVxuXG4gICAgY29uc3QgYWN0dWFsVGV4U2hhcGUgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKG5ld1RleFNoYXBlKTtcbiAgICBsZXQgY2xvbmVkQXJyYXk6IFQxO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxUZXhTaGFwZSwgbmV3VGV4U2hhcGUpKSB7XG4gICAgICBjbG9uZWRBcnJheSA9IHRoaXMucmVzaGFwZVRleHR1cmUobmRhcnJheSwgbmV3VGV4U2hhcGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjbG9uZWRBcnJheSA9IHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KTtcbiAgICB9XG4gICAgcmV0dXJuIGNsb25lZEFycmF5LnJlc2hhcGU8VDI+KG5ld1NoYXBlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzbGljZTJESW50ZXJuYWwoXG4gICAgICBpbnB1dDogQXJyYXkyRCwgYmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRCB7XG4gICAgY29uc3QgcmVzdWx0ID0gTkRBcnJheS5tYWtlPEFycmF5MkQ+KHNpemVSb3dDb2wsIHtcbiAgICAgIHRleHR1cmU6IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoc2l6ZVJvd0NvbCksXG4gICAgICB0ZXh0dXJlU2hhcGVSQzogc2l6ZVJvd0NvbFxuICAgIH0pO1xuICAgIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIGlucHV0LCBiZWdpblJvd0NvbCwgc2l6ZVJvd0NvbCwgcmVzdWx0LCBbMCwgMF0sIHNpemVSb3dDb2wpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29weTJESW50ZXJuYWwoXG4gICAgICBzb3VyY2U6IEFycmF5MkQsIHNvdXJjZUJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogQXJyYXkyRCxcbiAgICAgIGRlc3RCZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZCB7XG4gICAgY29uc3Qgc291cmNlU2hhcGVSQyA9IHNvdXJjZS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGRlc3RTaGFwZVJDID0gZGVzdC5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBtYWtlQ29weVByb2dyYW1OYW1lKHNvdXJjZVNoYXBlUkMsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3RTaXplUm93Q29sKSxcbiAgICAgICAgKCkgPT4gY29weV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICBzb3VyY2VTaGFwZVJDLCBzb3VyY2VTaXplUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCkpO1xuXG4gICAgY29weV9ncHUuY29weShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgc291cmNlLmdldFRleHR1cmUoKSwgc291cmNlU2hhcGVSQyxcbiAgICAgICAgc291cmNlQmVnaW5Sb3dDb2wsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3QuZ2V0VGV4dHVyZSgpLCBkZXN0U2hhcGVSQyxcbiAgICAgICAgZGVzdEJlZ2luUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29uY2F0M0RJbnRlcm5hbCh4MTogQXJyYXkzRCwgeDI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHgxVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeDEuc2hhcGUpO1xuICAgIGNvbnN0IHgyVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeDIuc2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYMVRleFNoYXBlID0geDEuZ2V0VGV4dHVyZVNoYXBlUkMoeDFUZXhTaGFwZVJDKTtcbiAgICBsZXQgY2xlYW51cFgxID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFgxVGV4U2hhcGUsIHgxVGV4U2hhcGVSQykpIHtcbiAgICAgIHgxID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4MSwgeDFUZXhTaGFwZVJDKTtcbiAgICAgIGNsZWFudXBYMSA9IHRydWU7XG4gICAgfVxuICAgIGNvbnN0IGFjdHVhbFgyVGV4U2hhcGUgPSB4Mi5nZXRUZXh0dXJlU2hhcGVSQyh4MlRleFNoYXBlUkMpO1xuICAgIGxldCBjbGVhbnVwWDIgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDJUZXhTaGFwZSwgeDJUZXhTaGFwZVJDKSkge1xuICAgICAgeDIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgyLCB4MlRleFNoYXBlUkMpO1xuICAgICAgY2xlYW51cFgyID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRTaGFwZVJDRCA9XG4gICAgICAgIGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlLCBheGlzKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtDT05DQVRfUFJPR31fJHt4MS5zaGFwZX1fJHt4Mi5zaGFwZX1fJHtheGlzfWAsXG4gICAgICAgICgpID0+IGNvbmNhdDNkX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHgxLnNoYXBlLCB4Mi5zaGFwZSwgcmVzdWx0U2hhcGVSQ0QsIGF4aXMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZVJDRCk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb25jYXQzZF9ncHUuY29uY2F0M0QoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHgxLmdldFRleHR1cmUoKSwgeDIuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWDEpIHtcbiAgICAgIHgxLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBpZiAoY2xlYW51cFgyKSB7XG4gICAgICB4Mi5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGVSQ0QsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGMsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy0nLCBPcGVyYW5kVHlwZS5TQ0FMQVIpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJy0nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjMTogU2NhbGFyLCBhOiBULCBjMjogU2NhbGFyLCBiOiBUKSB7XG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKCF0aGlzLmRvR1BVU2hhcGVzTWF0Y2goYSwgYikpIHtcbiAgICAgIGIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGIsIGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgICBjbGVhbnVwQiA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIEFERF9TQ0FMRURfTUFUX1BST0csICgpID0+IGFkZHNjYWxlZG1hdF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBhZGRzY2FsZWRtYXRfZ3B1LmFkZFNjYWxlZE1hdHJpY2VzKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYi5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgYzEuZ2V0VGV4dHVyZSgpLCBjMi5nZXRUZXh0dXJlKCksIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgLy8gQnJpbmcgdGhlIHJlc3VsdCBiYWNrIHRvIHRoZSBvcmlnaW5hbCBzaGFwZS5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBjLCBhLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5TQ0FMQVIsICcqJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIE5FR19QUk9HLCAoKSA9PiBuZWdfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgbmVnX2dwdS5uZWcoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByaXZhdGUgcmVzaGFwZVRleHR1cmU8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIG5ld1RleHR1cmVTaGFwZTogW1xuICAgIG51bWJlciwgbnVtYmVyXG4gIF0pOiBUIHtcbiAgICBjb25zdCBhVGV4U2hhcGUgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgUkVTSEFQRV9QUk9HLCAoKSA9PiByZXNoYXBlX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKG5ld1RleHR1cmVTaGFwZSk7XG4gICAgcmVzaGFwZV9ncHUucmVzaGFwZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIGFUZXhTaGFwZVswXSwgYVRleFNoYXBlWzFdLFxuICAgICAgICByZXN1bHRUZXh0dXJlLCBuZXdUZXh0dXJlU2hhcGVbMF0sIG5ld1RleHR1cmVTaGFwZVsxXSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBhLnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IG5ld1RleHR1cmVTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1hdE11bEludGVybmFsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbixcbiAgICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBBcnJheTJEIHtcbiAgICBjb25zdCBzaGFyZWREaW0gPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMV0gOiBhLnNoYXBlWzBdO1xuICAgIGNvbnN0IG91dGVyU2hhcGVBID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzBdIDogYS5zaGFwZVsxXTtcbiAgICBjb25zdCBvdXRlclNoYXBlQiA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVsxXSA6IGIuc2hhcGVbMF07XG4gICAgY29uc3Qgb3V0U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbb3V0ZXJTaGFwZUEsIG91dGVyU2hhcGVCXTtcbiAgICBjb25zdCBvdXRUZXhTaGFwZSA9XG4gICAgICAgIHdlYmdsX3V0aWwuZ2V0VGV4dHVyZVNoYXBlRnJvbUxvZ2ljYWxTaGFwZSh0aGlzLmdwZ3B1LmdsLCBvdXRTaGFwZSk7XG4gICAgY29uc3Qgb3V0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUob3V0VGV4U2hhcGUpO1xuICAgIGNvbnN0IG91dCA9IG5ldyBBcnJheTJEKFxuICAgICAgICBvdXRTaGFwZSwge3RleHR1cmU6IG91dFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiBvdXRUZXhTaGFwZX0pO1xuXG4gICAgY29uc3Qga2V5ID0gc2hhZGVyX2NvbXBpbGVyLm1ha2VTaGFkZXJLZXkoW2EsIGJdLCBvdXQpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNQVRNVUxfUFJPR31fJHtrZXl9XyR7YU9yaWVudGF0aW9ufV8ke2JPcmllbnRhdGlvbn1gLFxuICAgICAgICAoKSA9PiBtdWxtYXRfZ3B1LmdldEZyYWdtZW50U2hhZGVyKFxuICAgICAgICAgICAgYSwgYiwgb3V0LCBhT3JpZW50YXRpb24sIGJPcmllbnRhdGlvbikpO1xuXG4gICAgbXVsbWF0X2dwdS5tdWx0aXBseU1hdHJpeChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIGIuZ2V0VGV4dHVyZSgpLCBvdXRUZXh0dXJlLFxuICAgICAgICBvdXRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gb3V0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJyonLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdOb3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb246IG51bWJlciwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEIHtcbiAgICBjb25zdCB4VGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBsZXQgY2xlYW51cE1lYW4gPSBmYWxzZTtcbiAgICBjb25zdCBwcmVmZXJyZWRNZWFuVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICBtZWFuLnJhbmsgPT09IDEgPyBbMSwgbWVhbi5zaXplXSA6IHhUZXhTaGFwZTtcbiAgICBsZXQgbWVhblRleFNoYXBlID0gbWVhbi5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRNZWFuVGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChtZWFuVGV4U2hhcGUsIHByZWZlcnJlZE1lYW5UZXhTaGFwZSkpIHtcbiAgICAgIG1lYW4gPSB0aGlzLnJlc2hhcGVUZXh0dXJlKG1lYW4sIHByZWZlcnJlZE1lYW5UZXhTaGFwZSk7XG4gICAgICBtZWFuVGV4U2hhcGUgPSBwcmVmZXJyZWRNZWFuVGV4U2hhcGU7XG4gICAgICBjbGVhbnVwTWVhbiA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBWYXJpYW5jZSA9IGZhbHNlO1xuICAgIGNvbnN0IHByZWZlcnJlZFZhcmlhbmNlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICB2YXJpYW5jZS5yYW5rID09PSAxID8gWzEsIHZhcmlhbmNlLnNpemVdIDogeFRleFNoYXBlO1xuICAgIGxldCB2YXJpYW5jZVRleFNoYXBlID0gdmFyaWFuY2UuZ2V0VGV4dHVyZVNoYXBlUkMocHJlZmVycmVkTWVhblRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwodmFyaWFuY2VUZXhTaGFwZSwgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZSkpIHtcbiAgICAgIHZhcmlhbmNlID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh2YXJpYW5jZSwgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZSk7XG4gICAgICB2YXJpYW5jZVRleFNoYXBlID0gcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZTtcbiAgICAgIGNsZWFudXBWYXJpYW5jZSA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IHNjYWxlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl18bnVsbCA9IG51bGw7XG4gICAgbGV0IGNsZWFudXBTY2FsZSA9IGZhbHNlO1xuICAgIGlmIChzY2FsZSAhPSBudWxsKSB7XG4gICAgICBjb25zdCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgICBzY2FsZS5yYW5rID09PSAxID8gWzEsIHNjYWxlLnNpemVdIDogeFRleFNoYXBlO1xuXG4gICAgICBzY2FsZVRleFNoYXBlID0gc2NhbGUuZ2V0VGV4dHVyZVNoYXBlUkMocHJlZmVycmVkU2NhbGVUZXhTaGFwZSk7XG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoc2NhbGVUZXhTaGFwZSwgcHJlZmVycmVkU2NhbGVUZXhTaGFwZSkpIHtcbiAgICAgICAgc2NhbGUgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHNjYWxlLCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlKTtcbiAgICAgICAgc2NhbGVUZXhTaGFwZSA9IHByZWZlcnJlZFNjYWxlVGV4U2hhcGU7XG4gICAgICAgIGNsZWFudXBTY2FsZSA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbGV0IG9mZnNldFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdfG51bGwgPSBudWxsO1xuICAgIGxldCBjbGVhbnVwT2Zmc2V0ID0gZmFsc2U7XG4gICAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgICBjb25zdCBwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgICAgb2Zmc2V0LnJhbmsgPT09IDEgPyBbMSwgb2Zmc2V0LnNpemVdIDogeFRleFNoYXBlO1xuXG4gICAgICBvZmZzZXRUZXhTaGFwZSA9IG9mZnNldC5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZSk7XG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwob2Zmc2V0VGV4U2hhcGUsIHByZWZlcnJlZE9mZnNldFRleFNoYXBlKSkge1xuICAgICAgICBvZmZzZXQgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKG9mZnNldCwgcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGUpO1xuICAgICAgICBvZmZzZXRUZXhTaGFwZSA9IHByZWZlcnJlZE9mZnNldFRleFNoYXBlO1xuICAgICAgICBjbGVhbnVwT2Zmc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtCQVRDSE5PUk1fUFJPR31fJHt4VGV4U2hhcGV9XyR7bWVhblRleFNoYXBlfV8ke3ZhcmlhbmNlVGV4U2hhcGV9X2AgK1xuICAgICAgICAgICAgYCR7c2NhbGVUZXhTaGFwZSF9XyR7b2Zmc2V0VGV4U2hhcGUhfV8ke3ZhcmlhbmNlRXBzaWxvbn1gLFxuICAgICAgICAoKSA9PiBiYXRjaG5vcm1fZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgeFRleFNoYXBlLCBtZWFuVGV4U2hhcGUsIHZhcmlhbmNlVGV4U2hhcGUsIG9mZnNldFRleFNoYXBlLFxuICAgICAgICAgICAgc2NhbGVUZXhTaGFwZSwgdmFyaWFuY2VFcHNpbG9uKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBiYXRjaG5vcm1fZ3B1LmJhdGNoTm9ybWFsaXphdGlvbihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHhUZXhTaGFwZSwgbWVhbi5nZXRUZXh0dXJlKCksXG4gICAgICAgIG1lYW5UZXhTaGFwZSwgdmFyaWFuY2UuZ2V0VGV4dHVyZSgpLCB2YXJpYW5jZVRleFNoYXBlLFxuICAgICAgICBvZmZzZXQgIT0gbnVsbCA/IG9mZnNldC5nZXRUZXh0dXJlKCkgOiBudWxsLFxuICAgICAgICBvZmZzZXQgIT0gbnVsbCA/IG9mZnNldFRleFNoYXBlIDogbnVsbCxcbiAgICAgICAgc2NhbGUgIT0gbnVsbCA/IHNjYWxlLmdldFRleHR1cmUoKSA6IG51bGwsXG4gICAgICAgIHNjYWxlICE9IG51bGwgPyBzY2FsZVRleFNoYXBlIDogbnVsbCwgcmVzdWx0VGV4dHVyZSwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBNZWFuKSB7XG4gICAgICBtZWFuLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBWYXJpYW5jZSkge1xuICAgICAgdmFyaWFuY2UuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFNjYWxlKSB7XG4gICAgICBzY2FsZSEuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cE9mZnNldCkge1xuICAgICAgb2Zmc2V0IS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgeC5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN3aXRjaERpbUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBuZXdEaW06IG51bWJlcltdKTogVCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdOb3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke1NVTV9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiByZWR1Y2VzdW1fZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICByZWR1Y2VzdW1fZ3B1LnJlZHVjZVN1bShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNSU5fUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWlubWF4X2dwdS5nZXRBcmdNaW5GcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWlubWF4X2dwdS5hcmdNaW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7QVJHTUFYX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IGFyZ21pbm1heF9ncHUuZ2V0QXJnTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIGFyZ21pbm1heF9ncHUuYXJnTWluTWF4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2gsIGRvIGEgcGh5c2ljYWwgcmVzaGFwZSBzbyB0aGV5IGRvLlxuICAgIGNvbnN0IGFjdHVhbFgxVGV4U2hhcGUgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGFjdHVhbFgyVGV4U2hhcGUgPSB4Mi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGxldCBjbGVhbnVwWDIgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDFUZXhTaGFwZSwgYWN0dWFsWDJUZXhTaGFwZSkpIHtcbiAgICAgIHgyID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4MiwgYWN0dWFsWDFUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWDIgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0geDEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNQVhfRVFVQUxTX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IGFyZ21heGVxdWFsc19ncHUuZ2V0QXJnTWF4RXF1YWxzRnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICBudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWF4ZXF1YWxzX2dwdS5hcmdNYXhFcXVhbHMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHgxLmdldFRleHR1cmUoKSwgeDIuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLFxuICAgICAgICBudW1Db2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIGlmIChjbGVhbnVwWDIpIHtcbiAgICAgIHgyLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH0ge1xuICAgIHRocm93IG5ldyBFcnJvcigndG9wSyBHUFUgbm90IHlldCBpbXBsZW1lbnRlZCEnKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNSU5fUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbWlubWF4X2dwdS5nZXRNaW5GcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgbWlubWF4X2dwdS5taW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7TUFYX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IG1pbm1heF9ncHUuZ2V0TWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIG1pbm1heF9ncHUubWluTWF4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRpdmlkZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcvJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6XG4gICAgICBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgICAgICAgICBjLCBhLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5TQ0FMQVIsICcvJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFycmF5RGl2aWRlZEJ5U2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6XG4gICAgICBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgICAgICAgICBhLCBjLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcvJywgT3BlcmFuZFR5cGUuU0NBTEFSKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcrJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1YkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICctJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtMT0dTVU1FWFBfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbG9nc3VtZXhwX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHQgPVxuICAgICAgICBuZXcgU2NhbGFyKHt0ZXh0dXJlOiB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSl9KTtcblxuICAgIHJlZHVjZXN1bV9ncHUucmVkdWNlU3VtKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0LmdldFRleHR1cmUoKSk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGV4cEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIEVYUF9QUk9HLCAoKSA9PiBleHBfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgZXhwX2dwdS5leHAoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgTE9HX1BST0csICgpID0+IGxvZ19ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG4gICAgbG9nX2dwdS5sb2coXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVsdUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFJFTFVfUFJPRywgKCkgPT4gcmVsdV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICByZWx1X2dwdS5yZWx1KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBTSUdNT0lEX1BST0csICgpID0+IHNpZ21vaWRfZ3B1LmdldFNpZ21vaWRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHNpZ21vaWRfZ3B1LnNpZ21vaWQoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFRBTkhfUFJPRywgKCkgPT4gdHJpZ19ncHUuZ2V0VGFuaEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgdHJpZ19ncHUudGFuaChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaW5JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBTSU5fUFJPRywgKCkgPT4gdHJpZ19ncHUuZ2V0U2luRnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICB0cmlnX2dwdS5zaW4oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RlcEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNURVBfUFJPRywgKCkgPT4gc3RlcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBzdGVwX2dwdS5zdGVwKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBpbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgcHJvZ0tleSA9IFtcbiAgICAgIENPTlYyRF9QUk9HLCB4LnNoYXBlLCBvdXRwdXREZXB0aCwgZmllbGRTaXplLCBzdHJpZGUsIGJpYXNlcyAhPSBudWxsXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0ocHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIG91dHB1dERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSwgemVyb1BhZCwgYmlhc2VzICE9IG51bGwpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCB3VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgICBjb25zdCBiaWFzVGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICBjb25zdCBhY3R1YWxCVGV4U2hhcGUgPSBiaWFzZXMuZ2V0VGV4dHVyZVNoYXBlUkMoYmlhc1RleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxCVGV4U2hhcGUsIGJpYXNUZXhTaGFwZSkpIHtcbiAgICAgICAgYmlhc2VzID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiaWFzZXMsIGJpYXNUZXhTaGFwZSk7XG4gICAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9ncHUuY29udm9sdmUoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCB3ZWlnaHRzLmdldFRleHR1cmUoKSxcbiAgICAgICAgYmlhc2VzICE9IG51bGwgPyBiaWFzZXMuZ2V0VGV4dHVyZSgpIDogbnVsbCwgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFcpIHtcbiAgICAgIHdlaWdodHMuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cEIgJiYgYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGJpYXNlcy5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZEJhY2tQcm9wSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBkeTogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IHtkeDogQXJyYXkzRCwgZHc6IEFycmF5NEQsIGRiOiBBcnJheTFEfSB7XG4gICAgY29uc3QgZlNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgY29uc3QgeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChkeS5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoeFRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWFRleFNoYXBlLCB4VGV4U2hhcGUpKSB7XG4gICAgICB4ID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4LCB4VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFggPSB0cnVlO1xuICAgIH1cblxuICAgIGxldCBjbGVhbnVwVyA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFdUZXhTaGFwZSA9IHdlaWdodHMuZ2V0VGV4dHVyZVNoYXBlUkMod1RleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsV1RleFNoYXBlLCB3VGV4U2hhcGUpKSB7XG4gICAgICB3ZWlnaHRzID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh3ZWlnaHRzLCB3VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFcgPSB0cnVlO1xuICAgIH1cblxuICAgIGxldCBjbGVhbnVwWSA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFlUZXhTaGFwZSA9IGR5LmdldFRleHR1cmVTaGFwZVJDKHlUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFlUZXhTaGFwZSwgeVRleFNoYXBlKSkge1xuICAgICAgZHkgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGR5LCB5VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFkgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGR3ID0gdGhpcy5jb252MmREZXJXZWlnaHRzKHgsIGR5LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IGRiID0gdGhpcy5jb252MmREZXJCaWFzKGR5KTtcbiAgICBjb25zdCBkeCA9IHRoaXMuY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoXG4gICAgICAgIGR5LCB3ZWlnaHRzLCBudWxsIC8qKiBiaWFzZXMgKi8sIHN0cmlkZSwgcGFkKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZHkuZGlzcG9zZSgpO1xuICAgIH1cbiAgICByZXR1cm4ge2R4LCBkYiwgZHd9O1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvcmlnT3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IGZpZWxkU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG5cbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX1RSQU5TUE9TRV9QUk9HLCB4LnNoYXBlLCBmaWVsZFNpemUsIG9yaWdJbnB1dERlcHRoLCBvcmlnU3RyaWRlLFxuICAgICAgb3JpZ1BhZCwgYmlhc2VzICE9IG51bGxcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZpZWxkU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIG9yaWdTdHJpZGUsIG9yaWdQYWQsXG4gICAgICAgICAgYmlhc2VzICE9IG51bGwpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCB3VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShcbiAgICAgICAgb3JpZ0lucHV0RGVwdGgsIG9yaWdPdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgICBjb25zdCBiaWFzVGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG9yaWdJbnB1dERlcHRoKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICBjb25zdCBhY3R1YWxCaWFzVGV4U2hhcGUgPSBiaWFzZXMuZ2V0VGV4dHVyZVNoYXBlUkMoYmlhc1RleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxCaWFzVGV4U2hhcGUsIGJpYXNUZXhTaGFwZSkpIHtcbiAgICAgICAgYmlhc2VzID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiaWFzZXMsIGJpYXNUZXhTaGFwZSk7XG4gICAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBGaWd1cmUgb3V0IHRoZSBvdXRwdXQgc2hhcGUgYnkgZGlsYXRpbmcgdGhlIGlucHV0LlxuICAgIGNvbnN0IGRpbGF0ZWRSQyA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlRGlsYXRlZFJDKFt4LnNoYXBlWzBdLCB4LnNoYXBlWzFdXSwgb3JpZ1N0cmlkZSk7XG4gICAgY29uc3QgcGFkID0gZmllbGRTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgcmVzdWx0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFtkaWxhdGVkUkNbMF0sIGRpbGF0ZWRSQ1sxXSwgb3JpZ091dHB1dERlcHRoXSwgZmllbGRTaXplLFxuICAgICAgICBvcmlnSW5wdXREZXB0aCwgMSwgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuY29udlRyYW5zcG9zZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHdlaWdodHMuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXRUZXh0dXJlKCkgOiBudWxsLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwQikge1xuICAgICAgYmlhc2VzIS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgY29udjJkRGVyV2VpZ2h0cyhcbiAgICAgIHg6IEFycmF5M0QsIGRZOiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5NEQge1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB4LnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgcHJvZ0tleSA9IFtcbiAgICAgIENPTlYyRF9ERVJXX1BST0csIHguc2hhcGUsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0ocHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGNvbnZfYmFja3Byb3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyRGVyV2VpZ2h0c1NvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHlTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZlNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpO1xuICAgIGNvbnN0IHlUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeVNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZFkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkWSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZFksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuZGVyV2VpZ2h0cyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIGRZLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4LFxuICAgICAgICByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFkpIHtcbiAgICAgIGRZLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBjb25zdCB3ZWlnaHRzU2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNTaGFwZTREKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTREPihcbiAgICAgICAgd2VpZ2h0c1NoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIGNvbnYyZERlckJpYXMoZFk6IEFycmF5M0QpOiBBcnJheTFEIHtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IGRZLnNoYXBlWzJdO1xuICAgIGNvbnN0IHByb2dLZXkgPSBbQ09OVjJEX0RFUkJfUFJPRywgZFkuc2hhcGVdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJEZXJCaWFzU291cmNlKGRZLnNoYXBlKTtcbiAgICB9KTtcbiAgICBjb25zdCB5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGRZLnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZFkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkWSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZFksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoKTtcbiAgICBjb25zdCByZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbnZfYmFja3Byb3BfZ3B1LmRlckJpYXMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGRZLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFkpIHtcbiAgICAgIGRZLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5MUQ+KFxuICAgICAgICBbb3V0cHV0RGVwdGhdLCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIHByaXZhdGUgcG9vbChcbiAgICAgIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbFhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoeFRleFNoYXBlKTtcbiAgICBsZXQgY2xlYW51cFggPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWFRleFNoYXBlLCB4VGV4U2hhcGUpKSB7XG4gICAgICB4ID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4LCB4VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFggPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKHguc2hhcGUsIGZTaXplLCB4LnNoYXBlWzJdLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHJlc3VsdFNoYXBlKTtcbiAgICBjb25zdCBwb29sUmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBwb29sX2dwdS5wb29sQ29tbW9uKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgcG9vbFJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHBvb2xSZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heFBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG1heFBvb2xQcm9nS2V5ID1cbiAgICAgICAgW01BWF9QT09MX1BST0csIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZF0uam9pbignXycpO1xuICAgIGNvbnN0IG1heFBvb2xQcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShtYXhQb29sUHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIG1heF9wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlck1heFBvb2xTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLnBvb2wobWF4UG9vbFByb2dyYW0sIHgsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWluUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWluUG9vbFByb2dLZXkgPVxuICAgICAgICBbTUlOX1BPT0xfUFJPRywgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWluUG9vbFByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1pblBvb2xQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWluX3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWluUG9vbFNvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMucG9vbChtaW5Qb29sUHJvZ3JhbSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhdmdQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBhdmdQb29sUHJvZ0tleSA9XG4gICAgICAgIFtBVkdfUE9PTF9QUk9HLCB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWRdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBhdmdQb29sUHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oYXZnUG9vbFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBhdmdfcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJBdmdQb29sU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5wb29sKGF2Z1Bvb2xQcm9ncmFtLCB4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heFBvb2xCYWNrcHJvcEludGVybmFsKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG1heFBvb2xQb3NpdGlvbnNQcm9nS2V5ID0gW1xuICAgICAgTUFYX1BPT0xfUE9TSVRJT05TX1BST0csIHguc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Byb2dyYW0gPVxuICAgICAgICB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xQb3NpdGlvbnNQcm9nS2V5LCAoKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIG1heF9wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlck1heFBvb2xQb3NpdGlvbnNTb3VyY2UoXG4gICAgICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICAgICAgfSk7XG5cbiAgICBjb25zdCBtYXhQb29sUmVzdWx0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIHguc2hhcGUsIGZTaXplLCB4LnNoYXBlWzJdLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICBjb25zdCBtYXhQb29sUmVzdWx0VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKG1heFBvb2xSZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCA9XG4gICAgICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUobWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbWF4X3Bvb2xfZ3B1Lm1heFBvb2xDb21tb24oXG4gICAgICAgIHRoaXMuZ3BncHUsIG1heFBvb2xQb3NpdGlvbnNQcm9ncmFtLCB4LmdldFRleHR1cmUoKSxcbiAgICAgICAgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCwgbWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbnN0IG1heFBvb2xCYWNrcHJvcFByb2dLZXkgPSBbXG4gICAgICBNQVhfUE9PTF9CQUNLUFJPUF9QUk9HLCBkeS5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShtYXhQb29sQmFja3Byb3BQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWF4X3Bvb2xfYmFja3Byb3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbEJhY2twcm9wKFxuICAgICAgICAgIGR5LnNoYXBlLCBmU2l6ZSwgb3JpZ1N0cmlkZSwgb3JpZ1BhZCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBkeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChkeS5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbER5VGV4U2hhcGUgPSBkeS5nZXRUZXh0dXJlU2hhcGVSQyhkeVRleFNoYXBlKTtcbiAgICBsZXQgY2xlYW51cER5ID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbER5VGV4U2hhcGUsIGR5VGV4U2hhcGUpKSB7XG4gICAgICBkeSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZHksIGR5VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cER5ID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCBkaWxhdGVkRHlSQyA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlRGlsYXRlZFJDKFtkeS5zaGFwZVswXSwgZHkuc2hhcGVbMV1dLCBvcmlnU3RyaWRlKTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IHJlc3VsdFNoYXBlUkNEID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbZGlsYXRlZER5UkNbMF0sIGRpbGF0ZWREeVJDWzFdLCBkeS5zaGFwZVsyXV0sIGZTaXplLCBkeS5zaGFwZVsyXSwgMSxcbiAgICAgICAgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGVSQ0QpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgbWF4X3Bvb2xfYmFja3Byb3BfZ3B1Lm1heFBvb2xCYWNrcHJvcChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgZHkuZ2V0VGV4dHVyZSgpLCBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4LFxuICAgICAgICByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwRHkpIHtcbiAgICAgIGR5LmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIucmVsZWFzZVRleHR1cmUoXG4gICAgICAgIG1heFBvb2xQb3NpdGlvbnNSZXN1bHRUZXgsIG1heFBvb2xSZXN1bHRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGFsaWduQ29ybmVyczogYm9vbGVhbik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHByb2dyYW1LZXkgPVxuICAgICAgICBbUkVTSVpFX0JJTElORUFSX1BST0csIHguc2hhcGUsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVyc10uam9pbignXycpO1xuXG4gICAgY29uc3QgbmV3U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIFtuZXdTaGFwZTJEWzBdLCBuZXdTaGFwZTJEWzFdLCB4LnNoYXBlWzJdXTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QobmV3U2hhcGVSQ0QpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIHByb2dyYW1LZXksXG4gICAgICAgICgpID0+IHJlc2l6ZV9iaWxpbmVhcl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB4LnNoYXBlLCBuZXdTaGFwZTJELCBhbGlnbkNvcm5lcnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJlc2l6ZV9iaWxpbmVhcl9ncHUucmVzaXplQmlsaW5lYXIoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICBuZXdTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRBbmRTYXZlUHJvZ3JhbShwcm9ncmFtS2V5OiBzdHJpbmcsIGdldFNoYWRlclNvdXJjZTogKCkgPT4gc3RyaW5nKTpcbiAgICAgIFdlYkdMUHJvZ3JhbSB7XG4gICAgaWYgKCEocHJvZ3JhbUtleSBpbiB0aGlzLnByb2dyYW1DYWNoZSkpIHtcbiAgICAgIHRoaXMucHJvZ3JhbUNhY2hlW3Byb2dyYW1LZXldID1cbiAgICAgICAgICB0aGlzLmdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0U2hhZGVyU291cmNlKCkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5wcm9ncmFtQ2FjaGVbcHJvZ3JhbUtleV07XG4gIH1cblxuICBwcml2YXRlIGFkZFN1Yk11bERpdihcbiAgICAgIGE6IE5EQXJyYXksIGI6IE5EQXJyYXksIHJlc3VsdFNoYXBlOiBudW1iZXJbXSxcbiAgICAgIG9wZXJhbmRBOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhbmRUeXBlLFxuICAgICAgb3BUeXBlOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhdGlvbixcbiAgICAgIG9wZXJhbmRCOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhbmRUeXBlKTogTkRBcnJheSB7XG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG5cbiAgICBjb25zdCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuICAgIGxldCBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuXG4gICAgbGV0IGxvZ2ljYWxCVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgICBpZiAob3BlcmFuZEEgPT09IE9wZXJhbmRUeXBlLk1BVFJJWCAmJiBvcGVyYW5kQiA9PT0gT3BlcmFuZFR5cGUuTUFUUklYKSB7XG4gICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUpO1xuXG4gICAgICBpZiAoYS5pbkdQVSgpKSB7XG4gICAgICAgIC8vIFByZWZlciBCIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEEuXG4gICAgICAgIGIuZ2V0VGV4dHVyZVNoYXBlUkMoYS5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIH0gZWxzZSBpZiAoYi5pbkdQVSgpKSB7XG4gICAgICAgIC8vIFByZWZlciBBIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEIuXG4gICAgICAgIGEuZ2V0VGV4dHVyZVNoYXBlUkMoYi5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYVRleFNoYXBlID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgY29uc3QgYlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IGJUZXhTaGFwZTtcblxuICAgICAgaWYgKGEucmFuayA9PT0gMSkge1xuICAgICAgICAvLyBXaGVuIGRlYWxpbmcgd2l0aCB2ZWN0b3JzLCB3ZSBjYW4gc2FtcGxlIGluIHRyYW5zcG9zZWQgd2F5IHdpdGhvdXRcbiAgICAgICAgLy8gdGhlIG5lZWQgdG8gZG8gcGh5c2ljYWwgcmVzaGFwZS5cbiAgICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGJUZXhTaGFwZSwgYVRleFNoYXBlKSkge1xuICAgICAgICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlRSQU5TUE9TRUQ7XG4gICAgICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IFtiVGV4U2hhcGVbMV0sIGJUZXhTaGFwZVswXV07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFUZXhTaGFwZSwgbG9naWNhbEJUZXhTaGFwZSkpIHtcbiAgICAgICAgYiA9IHRoaXMucmVzaGFwZVRleHR1cmUoYiwgYVRleFNoYXBlKTtcbiAgICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUjtcbiAgICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IGIuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBsb2dpY2FsQlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIH1cblxuICAgIGNvbnN0IGFUZXhTaGFwZSA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBiVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBjb25zdCBwcm9ncmFtS2V5ID0gW1xuICAgICAgQUREX1NVTV9NVUxfRElWX1BST0csIG9wZXJhbmRBLCBhT3JpZW50YXRpb24sIG9wVHlwZSwgb3BlcmFuZEIsXG4gICAgICBiT3JpZW50YXRpb25cbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgcHJvZ3JhbUtleSxcbiAgICAgICAgKCkgPT4gYWRkc3VibXVsZGl2X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIG9wZXJhbmRBLCBhT3JpZW50YXRpb24sIG9wVHlwZSwgb3BlcmFuZEIsIGJPcmllbnRhdGlvbikpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID0gW1xuICAgICAgTWF0aC5tYXgoYVRleFNoYXBlWzBdLCBsb2dpY2FsQlRleFNoYXBlWzBdKSxcbiAgICAgIE1hdGgubWF4KGFUZXhTaGFwZVsxXSwgbG9naWNhbEJUZXhTaGFwZVsxXSlcbiAgICBdO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9XG4gICAgICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4dHVyZVNoYXBlKTtcblxuICAgIGFkZHN1Ym11bGRpdl9ncHUuYWRkU3ViTXVsRGl2KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYVRleFNoYXBlLCBiLmdldFRleHR1cmUoKSxcbiAgICAgICAgYlRleFNoYXBlLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXh0dXJlU2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlKFxuICAgICAgICByZXN1bHRTaGFwZSxcbiAgICAgICAge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXh0dXJlU2hhcGV9KTtcbiAgfVxuXG4gIHByaXZhdGUgZG9HUFVTaGFwZXNNYXRjaChhOiBOREFycmF5LCBiOiBOREFycmF5KTogYm9vbGVhbiB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlKTtcbiAgICBpZiAoYS5pbkdQVSgpKSB7XG4gICAgICAvLyBQcmVmZXIgQiB0byBoYXZlIHRoZSBzaGFwZSBvZiBBLlxuICAgICAgYi5nZXRUZXh0dXJlU2hhcGVSQyhhLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgIH0gZWxzZSBpZiAoYi5pbkdQVSgpKSB7XG4gICAgICAvLyBQcmVmZXIgQSB0byBoYXZlIHRoZSBzaGFwZSBvZiBCLlxuICAgICAgYS5nZXRUZXh0dXJlU2hhcGVSQyhiLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgIH1cbiAgICByZXR1cm4gdXRpbC5hcnJheXNFcXVhbChhLmdldFRleHR1cmVTaGFwZVJDKCksIGIuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gIH1cblxuICBnZXRUZXh0dXJlTWFuYWdlcigpOiBUZXh0dXJlTWFuYWdlciB7XG4gICAgcmV0dXJuIHRoaXMudGV4dHVyZU1hbmFnZXI7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGZvciAoY29uc3QgcHJvZ3JhbUtleSBpbiB0aGlzLnByb2dyYW1DYWNoZSkge1xuICAgICAgaWYgKHRoaXMucHJvZ3JhbUNhY2hlLmhhc093blByb3BlcnR5KHByb2dyYW1LZXkpKSB7XG4gICAgICAgIHRoaXMuZ3BncHUuZGVsZXRlUHJvZ3JhbSh0aGlzLnByb2dyYW1DYWNoZVtwcm9ncmFtS2V5XSk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuZGlzcG9zZSgpO1xuXG4gICAgaWYgKHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSkge1xuICAgICAgdGhpcy5ncGdwdS5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtUZXh0dXJlTWFuYWdlcn0gZnJvbSAnLi93ZWJnbC90ZXh0dXJlX21hbmFnZXInO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsL3dlYmdsX3V0aWwnO1xuXG4vLyBUaGVzZSBnbG9iYWwgdmFyaWFibGVzIG5lZWQgdG8gYmUgaW5pdGlhbGl6ZWQgdG8gbnVsbCBzbyB0aGF0IGNsb3N1cmUga25vd3Ncbi8vIG5vdCB0byBzZWFsIHRoZW0uXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGxldCBHUEdQVTogR1BHUFVDb250ZXh0ID0gbnVsbCE7XG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGxldCBURVhUVVJFX01BTkFHRVI6IFRleHR1cmVNYW5hZ2VyID0gbnVsbCE7XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgaW50ZXJmYWNlIE5EQXJyYXlEYXRhIHtcbiAgdmFsdWVzPzogRmxvYXQzMkFycmF5O1xuICB0ZXh0dXJlPzogV2ViR0xUZXh0dXJlO1xuICAvKiogW3Jvd3MsIGNvbHVtbnNdIHNoYXBlIG9mIHRoZSB0ZXh0dXJlLiAqL1xuICB0ZXh0dXJlU2hhcGVSQz86IFtudW1iZXIsIG51bWJlcl07XG59XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgZnVuY3Rpb24gaW5pdGlhbGl6ZUdQVShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCB0ZXh0dXJlTWFuYWdlcjogVGV4dHVyZU1hbmFnZXIpIHtcbiAgR1BHUFUgPSBncGdwdTtcbiAgVEVYVFVSRV9NQU5BR0VSID0gdGV4dHVyZU1hbmFnZXI7XG59XG5cbmZ1bmN0aW9uIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpIHtcbiAgaWYgKEdQR1BVID09IG51bGwgfHwgVEVYVFVSRV9NQU5BR0VSID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0dQVSBub3QgaW50aWFsaXplZC4nKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheSB7XG4gIC8qKiBUaGUgc2hhcGUgb2YgdGhlIG5kYXJyYXkuICovXG4gIHNoYXBlOiBudW1iZXJbXTtcbiAgLyoqIE51bWJlciBvZiBlbGVtZW50cyBpbiB0aGUgbmRhcnJheS4gKi9cbiAgc2l6ZTogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBOdW1iZXIgb2YgZWxlbWVudHMgdG8gc2tpcCBpbiBlYWNoIGRpbWVuc2lvbiB3aGVuIGluZGV4aW5nLiBTZWVcbiAgICogaHR0cHM6Ly9kb2NzLnNjaXB5Lm9yZy9kb2MvbnVtcHkvcmVmZXJlbmNlL2dlbmVyYXRlZC9udW1weS5uZGFycmF5LnN0cmlkZXMuaHRtbFxuICAgKi9cbiAgcHJvdGVjdGVkIHN0cmlkZXM6IG51bWJlcltdO1xuXG4gIHByaXZhdGUgZGF0YTogTkRBcnJheURhdGE7XG5cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKHNoYXBlOiBudW1iZXJbXSwgZGF0YTogTkRBcnJheURhdGEpIHtcbiAgICAvLyBTYW5pdHkgY2hlY2tzLlxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkYXRhLnZhbHVlcyAhPSBudWxsIHx8IGRhdGEudGV4dHVyZSAhPSBudWxsLFxuICAgICAgICAnRWl0aGVyIGB2YWx1ZXNgIG9yIGB0ZXh0dXJlYCBtdXN0IGJlIGRlZmluZWQnKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkYXRhLnRleHR1cmUgPT0gbnVsbCB8fCAoZGF0YS50ZXh0dXJlU2hhcGVSQyAhPSBudWxsKSxcbiAgICAgICAgJ2B0ZXh0dXJlU2hhcGVgIG11c3QgYmUgZGVmaW5lZCB3aGVuIGB0ZXh0dXJlYCBpcyBkZWZpbmVkJyk7XG5cbiAgICB0aGlzLnNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpO1xuXG4gICAgaWYgKGRhdGEudmFsdWVzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMuc2l6ZSA9PT0gZGF0YS52YWx1ZXMubGVuZ3RoLFxuICAgICAgICAgICdDb25zdHJ1Y3RpbmcgbmRhcnJheSBvZiBzaGFwZSAoJyArIHRoaXMuc2l6ZSArICcpIHNob3VsZCBtYXRjaCB0aGUnICtcbiAgICAgICAgICAgICAgJyBsZW5ndGggb2YgdmFsdWVzICgnICsgZGF0YS52YWx1ZXMubGVuZ3RoICsgJyknKTtcbiAgICB9XG5cbiAgICB0aGlzLnNoYXBlID0gc2hhcGU7XG4gICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICBjb25zdCBkaW0gPSB0aGlzLnNoYXBlLmxlbmd0aDtcblxuICAgIGlmIChkaW0gPCAyKSB7XG4gICAgICB0aGlzLnN0cmlkZXMgPSBbXTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTGFzdCBkaW1lbnNpb24gaGFzIGltcGxpY2l0IHN0cmlkZSBvZiAxLCB0aHVzIGhhdmluZyBELTEgKGluc3RlYWQgb2YgRClcbiAgICAgIC8vIHN0cmlkZXMuXG4gICAgICB0aGlzLnN0cmlkZXMgPSBuZXcgQXJyYXkoZGltIC0gMSk7XG4gICAgICB0aGlzLnN0cmlkZXNbZGltIC0gMl0gPSB0aGlzLnNoYXBlW2RpbSAtIDFdO1xuICAgICAgZm9yIChsZXQgaSA9IGRpbSAtIDM7IGkgPj0gMDsgLS1pKSB7XG4gICAgICAgIHRoaXMuc3RyaWRlc1tpXSA9IHRoaXMuc3RyaWRlc1tpICsgMV0gKiB0aGlzLnNoYXBlW2kgKyAxXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgb2YgemVyb3Mgd2l0aCB0aGUgc3BlY2lmaWVkIHNoYXBlLiAqL1xuICBzdGF0aWMgemVyb3M8VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSk6IFQge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodXRpbC5zaXplRnJvbVNoYXBlKHNoYXBlKSk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihzaGFwZSwge3ZhbHVlc30pO1xuICB9XG5cbiAgLyoqIENyZWF0ZXMgYSBuZGFycmF5IG9mIHplcm9zIHdpdGggdGhlIHNhbWUgc2hhcGUgYXMgdGhlIHNwZWNpZmllZCBuZGFycmF5LlxuICAgKi9cbiAgc3RhdGljIHplcm9zTGlrZTxUIGV4dGVuZHMgTkRBcnJheT4oYW5vdGhlcjogVCk6IFQge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zKGFub3RoZXIuc2hhcGUpIGFzIFQ7XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgd2l0aCB0aGUgc2FtZSB2YWx1ZXMvc2hhcGUgYXMgdGhlIHNwZWNpZmllZCBuZGFycmF5LiAqL1xuICBzdGF0aWMgbGlrZTxUIGV4dGVuZHMgTkRBcnJheT4oYW5vdGhlcjogVCk6IFQge1xuICAgIGNvbnN0IHZhbHVlcyA9IGFub3RoZXIuZ2V0VmFsdWVzKCk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhbm90aGVyLnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlcyBhIG5ldyBuZGFycmF5IHdpdGggdGhlIHByb3ZpZGVkIHNoYXBlIGFuZCB2YWx1ZXMuIFZhbHVlcyBzaG91bGQgYmUgaW5cbiAgICogYSBmbGF0IGFycmF5LlxuICAgKi9cbiAgc3RhdGljIG1ha2U8VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSwgZGF0YTogTkRBcnJheURhdGEpOiBUIHtcbiAgICBzd2l0Y2ggKHNoYXBlLmxlbmd0aCkge1xuICAgICAgY2FzZSAwOlxuICAgICAgICByZXR1cm4gbmV3IFNjYWxhcihkYXRhKSBhcyBUO1xuICAgICAgY2FzZSAxOlxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXkxRChkYXRhKSBhcyBhbnk7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTJEKHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IEFycmF5M0Qoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhKSBhcyBhbnk7XG4gICAgICBjYXNlIDQ6XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXk0RChcbiAgICAgICAgICAgICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgICAgICAgICAgICAgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBOREFycmF5KHNoYXBlLCBkYXRhKSBhcyBhbnk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlc2hhcGVzIHRoZSBjdXJyZW50IG5kYXJyYXkgaW50byB0aGUgcHJvdmlkZWQgc2hhcGUuICovXG4gIHJlc2hhcGU8VCBleHRlbmRzIE5EQXJyYXk+KG5ld1NoYXBlOiBudW1iZXJbXSk6IFQge1xuICAgIGlmICh1dGlsLmFycmF5c0VxdWFsKHRoaXMuc2hhcGUsIG5ld1NoYXBlKSkge1xuICAgICAgLy8gTm8tb3AuXG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICByZXR1cm4gdGhpcyBhcyBhbnk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuc2l6ZSA9PT0gdXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKSxcbiAgICAgICAgJ25ldyBzaGFwZSBhbmQgb2xkIHNoYXBlIG11c3QgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2YgZWxlbWVudHMuJyk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5ld1NoYXBlLCB0aGlzLmRhdGEpO1xuICB9XG5cbiAgYXNTY2FsYXIoKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydCh0aGlzLnNpemUgPT09IDEsICdUaGUgYXJyYXkgbXVzdCBoYXZlIG9ubHkgMSBlbGVtZW50LicpO1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8U2NhbGFyPihbXSk7XG4gIH1cblxuICBhczFEKCk6IEFycmF5MUQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkxRD4oW3RoaXMuc2l6ZV0pO1xuICB9XG5cbiAgYXMyRChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEFycmF5MkQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkyRD4oW3Jvd3MsIGNvbHVtbnNdKTtcbiAgfVxuXG4gIGFzM0Qocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGRlcHRoOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5yZXNoYXBlPEFycmF5M0Q+KFtyb3dzLCBjb2x1bW5zLCBkZXB0aF0pO1xuICB9XG5cbiAgYXM0RChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgZGVwdGg6IG51bWJlciwgZGVwdGgyOiBudW1iZXIpOiBBcnJheTREIHtcbiAgICByZXR1cm4gdGhpcy5yZXNoYXBlPEFycmF5NEQ+KFtyb3dzLCBjb2x1bW5zLCBkZXB0aCwgZGVwdGgyXSk7XG4gIH1cblxuICBnZXQgcmFuaygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnNoYXBlLmxlbmd0aDtcbiAgfVxuXG4gIGdldCguLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIGxldCBpbmRleCA9IGxvY3NbbG9jcy5sZW5ndGggLSAxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBpbmRleCArPSB0aGlzLnN0cmlkZXNbaV0gKiBsb2NzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVtpbmRleF07XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgLi4ubG9jczogbnVtYmVyW10pIHtcbiAgICB0aGlzLnNldCh0aGlzLmdldCguLi5sb2NzKSArIHZhbHVlLCAuLi5sb2NzKTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCAuLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIGxldCBpbmRleCA9IGxvY3NbbG9jcy5sZW5ndGggLSAxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBpbmRleCArPSB0aGlzLnN0cmlkZXNbaV0gKiBsb2NzW2ldO1xuICAgIH1cbiAgICB0aGlzLmdldFZhbHVlcygpW2luZGV4XSA9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBudW1iZXJbXSk6IG51bWJlciB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHJldHVybiBpbmRleDtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IG51bWJlcltdIHtcbiAgICBjb25zdCBsb2NzOiBudW1iZXJbXSA9IG5ldyBBcnJheSh0aGlzLnNoYXBlLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsb2NzLmxlbmd0aCAtIDE7ICsraSkge1xuICAgICAgbG9jc1tpXSA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZXNbaV0pO1xuICAgICAgaW5kZXggLT0gbG9jc1tpXSAqIHRoaXMuc3RyaWRlc1tpXTtcbiAgICB9XG4gICAgbG9jc1tsb2NzLmxlbmd0aCAtIDFdID0gaW5kZXg7XG4gICAgcmV0dXJuIGxvY3M7XG4gIH1cblxuICBmaWxsKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpLmZpbGwodmFsdWUpO1xuICB9XG5cbiAgZ2V0RGF0YSgpOiBOREFycmF5RGF0YSB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YTtcbiAgfVxuXG4gIGdldFZhbHVlcygpOiBGbG9hdDMyQXJyYXkge1xuICAgIGlmICh0aGlzLmRhdGEudmFsdWVzID09IG51bGwpIHtcbiAgICAgIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpO1xuICAgICAgdGhpcy5kYXRhLnZhbHVlcyA9IEdQR1BVLmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUhLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMhWzBdLFxuICAgICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyFbMV0pO1xuICAgICAgdGhpcy5kaXNwb3NlVGV4dHVyZSgpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kYXRhLnZhbHVlcztcbiAgfVxuXG4gIHByaXZhdGUgdXBsb2FkVG9HUFUocHJlZmVycmVkVGV4U2hhcGU/OiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDID0gd2ViZ2xfdXRpbC5nZXRUZXh0dXJlU2hhcGVGcm9tTG9naWNhbFNoYXBlKFxuICAgICAgICBHUEdQVS5nbCwgdGhpcy5zaGFwZSwgcHJlZmVycmVkVGV4U2hhcGUpO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlID1cbiAgICAgICAgVEVYVFVSRV9NQU5BR0VSLmFjcXVpcmVUZXh0dXJlKHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBHUEdQVS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQ1sxXSwgdGhpcy5kYXRhLnZhbHVlcyEpO1xuXG4gICAgdGhpcy5kYXRhLnZhbHVlcyA9IG51bGwhO1xuICB9XG5cbiAgZ2V0VGV4dHVyZShwcmVmZXJyZWRTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXSk6IFdlYkdMVGV4dHVyZSB7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlID09IG51bGwpIHtcbiAgICAgIHRoaXMudXBsb2FkVG9HUFUocHJlZmVycmVkU2hhcGVSQyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRhdGEudGV4dHVyZSE7XG4gIH1cblxuICBnZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICAgIGlmICh0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMgPT0gbnVsbCkge1xuICAgICAgdGhpcy51cGxvYWRUb0dQVShwcmVmZXJyZWRTaGFwZVJDKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyE7XG4gIH1cblxuICBkaXNwb3NlKCk6IHZvaWQge1xuICAgIHRoaXMuZGF0YS52YWx1ZXMgPSBudWxsITtcbiAgICB0aGlzLnNoYXBlID0gbnVsbCE7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZGlzcG9zZVRleHR1cmUoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGRpc3Bvc2VUZXh0dXJlKCkge1xuICAgIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpO1xuICAgIFRFWFRVUkVfTUFOQUdFUi5yZWxlYXNlVGV4dHVyZShcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUhLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMhKTtcbiAgICB0aGlzLmRhdGEudGV4dHVyZSA9IG51bGwhO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyA9IG51bGwhO1xuICB9XG5cbiAgaW5HUFUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlICE9IG51bGw7XG4gIH1cblxuICBlcXVhbHModDogTkRBcnJheSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB1dGlsLmFycmF5c0VxdWFsKHRoaXMuc2hhcGUsIHQuc2hhcGUpICYmXG4gICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5nZXRWYWx1ZXMoKSwgdC5nZXRWYWx1ZXMoKSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZDxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCByYW5kRnVuY3Rpb246ICgpID0+IG51bWJlcik6XG4gICAgICBUIHtcbiAgICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHNoYXBlKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHNpemUpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2l6ZTsgaSsrKSB7XG4gICAgICB2YWx1ZXNbaV0gPSByYW5kRnVuY3Rpb24oKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KHNoYXBlLCB7dmFsdWVzfSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZE5vcm1hbDxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCBtZWFuID0gMCwgc3RkRGV2ID0gMSkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmQ8VD4oc2hhcGUsICgpID0+IHV0aWwucmFuZEdhdXNzKG1lYW4sIHN0ZERldikpO1xuICB9XG5cbiAgc3RhdGljIHJhbmRUcnVuY2F0ZWROb3JtYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgc2hhcGU6IG51bWJlcltdLCBtZWFuID0gMCwgc3RkRGV2ID0gMSkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmQ8VD4oc2hhcGUsICgpID0+IHV0aWwucmFuZEdhdXNzKG1lYW4sIHN0ZERldiwgdHJ1ZSkpO1xuICB9XG5cbiAgc3RhdGljIHJhbmRVbmlmb3JtPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIGE6IG51bWJlciwgYjogbnVtYmVyKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kVW5pZm9ybShhLCBiKSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNjYWxhciBleHRlbmRzIE5EQXJyYXkge1xuICBjb25zdHJ1Y3RvcihkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIGlmIChkYXRhLnRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgZGF0YS50ZXh0dXJlU2hhcGVSQyA9IFsxLCAxXTtcbiAgICB9XG4gICAgc3VwZXIoW10sIGRhdGEpO1xuICB9XG5cbiAgc3RhdGljIG5ldyh2YWx1ZTogbnVtYmVyKSB7XG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3ZhbHVlczogbmV3IEZsb2F0MzJBcnJheShbdmFsdWVdKX0pO1xuICB9XG5cbiAgc3RhdGljIFpFUk8gPSBTY2FsYXIubmV3KDApO1xuICBzdGF0aWMgT05FID0gU2NhbGFyLm5ldygxKTtcbiAgc3RhdGljIFRXTyA9IFNjYWxhci5uZXcoMik7XG4gIHN0YXRpYyBORUdfT05FID0gU2NhbGFyLm5ldygtMSk7XG5cbiAgZ2V0KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbMF07XG4gIH1cblxuICBzZXQodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbMF0gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVswXSArPSB2YWx1ZTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXJyYXkxRCBleHRlbmRzIE5EQXJyYXkge1xuICBzaGFwZTogW251bWJlcl07XG5cbiAgY29uc3RydWN0b3IoZGF0YTogTkRBcnJheURhdGEpIHtcbiAgICBjb25zdCBzaGFwZSA9IChkYXRhLnZhbHVlcyAhPSBudWxsKSA/XG4gICAgICAgIFtkYXRhLnZhbHVlcy5sZW5ndGhdIDpcbiAgICAgICAgW3V0aWwuc2l6ZUZyb21TaGFwZShkYXRhLnRleHR1cmVTaGFwZVJDISldO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcodmFsdWVzOiBGbG9hdDMyQXJyYXl8bnVtYmVyW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBpbmZlcnJlZFNoYXBlLmxlbmd0aCA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgY29uc3RydWN0aW5nIEFycmF5MUQuIFNoYXBlIG9mIHZhbHVlcyAke2luZmVycmVkU2hhcGV9IGlzIGAgK1xuICAgICAgICAgICAgICBgbm90IDEgZGltZW5zaW9uYWwuYCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkxRCh7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbaV07XG4gIH1cblxuICBzZXQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVtpXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIsIGk6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbaV0gKz0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvYzogW251bWJlcl0pOiBudW1iZXIge1xuICAgIHJldHVybiBsb2NbMF07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyXSB7XG4gICAgcmV0dXJuIFtpbmRleF07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXJdKTogQXJyYXkxRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkxRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTJEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdO1xuXG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gMiwgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggMicpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gIH1cblxuICBzdGF0aWMgbmV3KFxuICAgICAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICBpZiAoaW5mZXJyZWRTaGFwZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgICAgICAgICBzaGFwZSwgaW5mZXJyZWRTaGFwZSxcbiAgICAgICAgICAgIGBFcnJvciB3aGVuIGNvbnN0cnVjdGluZyBBcnJheTJELiBTaGFwZSBvZiB2YWx1ZXMgYCArXG4gICAgICAgICAgICAgICAgYCR7aW5mZXJyZWRTaGFwZX0gZG9lcyBub3QgbWF0Y2ggdGhlIHByb3ZpZGVkIHNoYXBlIGAgK1xuICAgICAgICAgICAgICAgIGAke3NoYXBlfS4gYCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkyRChzaGFwZSwge3ZhbHVlczogdG9UeXBlZEFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIGdldChpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyBqXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyBqXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jczogW251bWJlciwgbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc3RyaWRlMCAqIGxvY3NbMF0gKyBsb2NzWzFdO1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgcmV0dXJuIFtNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUwKSwgaW5kZXggJSB0aGlzLnN0cmlkZTBdO1xuICB9XG5cbiAgc3RhdGljIHplcm9zKHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkyRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTNEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTE6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gMywgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggMycpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gICAgdGhpcy5zdHJpZGUxID0gdGhpcy5zdHJpZGVzWzFdO1xuICB9XG5cbiAgc3RhdGljIG5ldyhcbiAgICAgIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgICB2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICBpZiAoaW5mZXJyZWRTaGFwZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgICAgICAgICBzaGFwZSwgaW5mZXJyZWRTaGFwZSxcbiAgICAgICAgICAgIGBFcnJvciB3aGVuIGNvbnN0cnVjdGluZyBBcnJheTNELiBTaGFwZSBvZiB2YWx1ZXMgYCArXG4gICAgICAgICAgICAgICAgYCR7aW5mZXJyZWRTaGFwZX0gZG9lcyBub3QgbWF0Y2ggdGhlIHByb3ZpZGVkIHNoYXBlIGAgK1xuICAgICAgICAgICAgICAgIGAke3NoYXBlfS4gYCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkzRChzaGFwZSwge3ZhbHVlczogdG9UeXBlZEFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIGdldChpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyBrXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyBrXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jczogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIHRoaXMuc3RyaWRlMSAqIGxvY3NbMV0gKyBsb2NzWzJdO1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBpID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMCk7XG4gICAgaW5kZXggLT0gaSAqIHRoaXMuc3RyaWRlMDtcbiAgICByZXR1cm4gW2ksIE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTEpLCBpbmRleCAlIHRoaXMuc3RyaWRlMV07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IEFycmF5M0Qge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zPEFycmF5M0Q+KHNoYXBlKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXJyYXk0RCBleHRlbmRzIE5EQXJyYXkge1xuICBzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTE6IG51bWJlcjtcbiAgcHJpdmF0ZSBzdHJpZGUyOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3Ioc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gNCwgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggNCcpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gICAgdGhpcy5zdHJpZGUxID0gdGhpcy5zdHJpZGVzWzFdO1xuICAgIHRoaXMuc3RyaWRlMiA9IHRoaXMuc3RyaWRlc1syXTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcoXG4gICAgICBzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgICB2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5NEQuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTREKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpXG4gICAgICAgIFt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIHRoaXMuc3RyaWRlMiAqIGsgKyBsXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyLCBsOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpXG4gICAgICAgIFt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIHRoaXMuc3RyaWRlMiAqIGsgKyBsXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc3RyaWRlMCAqIGxvY3NbMF0gKyB0aGlzLnN0cmlkZTEgKiBsb2NzWzFdICtcbiAgICAgICAgdGhpcy5zdHJpZGUyICogbG9jc1syXSArIGxvY3NbM107XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gICAgY29uc3QgaSA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTApO1xuICAgIGluZGV4IC09IGkgKiB0aGlzLnN0cmlkZTA7XG4gICAgY29uc3QgaiA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTEpO1xuICAgIGluZGV4IC09IGogKiB0aGlzLnN0cmlkZTE7XG4gICAgcmV0dXJuIFtpLCBqLCBNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUyKSwgaW5kZXggJSB0aGlzLnN0cmlkZTJdO1xuICB9XG5cbiAgc3RhdGljIHplcm9zKHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IEFycmF5NEQge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zPEFycmF5NEQ+KHNoYXBlKTtcbiAgfVxufVxuXG50eXBlIEFycmF5RGF0YSA9IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdfG51bWJlcltdW11bXXxudW1iZXJbXVtdW11bXTtcblxuZnVuY3Rpb24gdG9UeXBlZEFycmF5KGE6IEFycmF5RGF0YSk6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiAoYSBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkgPyBhIDogbmV3IEZsb2F0MzJBcnJheSh1dGlsLmZsYXR0ZW4oYSkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QjtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBU2NhbGFyO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEJTY2FsYXI7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmVGV4ZWwgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGEgPSB0ZXh0dXJlMkQobWF0cml4QSwgcmVzdWx0VVYpLnI7XG4gICAgICBmbG9hdCBiID0gdGV4dHVyZTJEKG1hdHJpeEIsIHJlc3VsdFVWKS5yO1xuICAgICAgZmxvYXQgYVNjYWxhciA9IHRleHR1cmUyRChtYXRyaXhBU2NhbGFyLCBoYWxmVGV4ZWwpLnI7XG4gICAgICBmbG9hdCBiU2NhbGFyID0gdGV4dHVyZTJEKG1hdHJpeEJTY2FsYXIsIGhhbGZUZXhlbCkucjtcbiAgICAgIHZlYzIgYWJTY2FsZWQgPSB2ZWMyKGEsIGIpICogdmVjMihhU2NhbGFyLCBiU2NhbGFyKTtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoYWJTY2FsZWQueCArIGFiU2NhbGVkLnksIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgYWRkU2NhbGVkTWF0cmljZXNQcm9ncmFtOiBXZWJHTFByb2dyYW0sXG4gICAgYTogV2ViR0xUZXh0dXJlLCBiOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIGFTY2FsYXI6IFdlYkdMVGV4dHVyZSwgYlNjYWxhcjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnNldFByb2dyYW0oYWRkU2NhbGVkTWF0cmljZXNQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYVNjYWxhciwgJ21hdHJpeEFTY2FsYXInLCAyKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGJTY2FsYXIsICdtYXRyaXhCU2NhbGFyJywgMyk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRBZGRTY2FsZWRNYXRyaWNlc0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBhU2NhbGFyOiBudW1iZXIsIGJTY2FsYXI6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gIGNvbnN0IGFUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBiVGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgYVNjYWxhclRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGNvbnN0IGJTY2FsYXJUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBjb25zdCByZXN1bHRUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuXG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4LCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJUZXgsIHJvd3MsIGNvbHVtbnMsIGIpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVNjYWxhclRleCwgMSwgMSwgbmV3IEZsb2F0MzJBcnJheShbYVNjYWxhcl0pKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJTY2FsYXJUZXgsIDEsIDEsIG5ldyBGbG9hdDMyQXJyYXkoW2JTY2FsYXJdKSk7XG5cbiAgYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgICBncGdwdSwgcHJvZ3JhbSwgYVRleCwgYlRleCwgcm93cywgY29sdW1ucywgYVNjYWxhclRleCwgYlNjYWxhclRleCxcbiAgICAgIHJlc3VsdFRleCk7XG5cbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXgsIHJvd3MsIGNvbHVtbnMpO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleCk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYlRleCk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhU2NhbGFyVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiU2NhbGFyVGV4KTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb259IGZyb20gJy4uL21hdGgnO1xuXG5pbXBvcnQgKiBhcyBiaW5hcnlvcF9ncHUgZnJvbSAnLi9iaW5hcnlvcF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCB0eXBlIE9wZXJhdGlvbiA9ICcrJyB8ICctJyB8ICcqJyB8ICcvJztcblxuZXhwb3J0IGVudW0gT3BlcmFuZFR5cGUge1xuICBNQVRSSVgsXG4gIFNDQUxBUlxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgYVR5cGU6IE9wZXJhbmRUeXBlLCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLCBvcDogT3BlcmF0aW9uLFxuICAgIGJUeXBlOiBPcGVyYW5kVHlwZSwgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIGNvbnN0IGFVViA9IG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoYVR5cGUsIGFPcmllbnRhdGlvbik7XG4gIGNvbnN0IGJVViA9IG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoYlR5cGUsIGJPcmllbnRhdGlvbik7XG4gIGNvbnN0IHJlc3VsdE9wID0gYGdsX0ZyYWdDb2xvciA9IHZlYzQoYSAke29wfSBiLCAwLCAwLCAwKTtgO1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGFVViwgYlVWLCByZXN1bHRPcCk7XG59XG5cbmZ1bmN0aW9uIG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoXG4gICAgb3BlcmFuZDogT3BlcmFuZFR5cGUsIG9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIHN3aXRjaCAob3BlcmFuZCkge1xuICAgIGNhc2UgT3BlcmFuZFR5cGUuTUFUUklYOlxuICAgICAgcmV0dXJuICdyZXN1bHRVVicgK1xuICAgICAgICAgIChvcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUiA/ICcuc3QnIDogJy50cycpO1xuICAgIGNhc2UgT3BlcmFuZFR5cGUuU0NBTEFSOlxuICAgICAgcmV0dXJuICd2ZWMyKDAuNSwgMC41KSc7XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBvcGVyYW5kIHR5cGUnKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYWRkU3ViTXVsRGl2KFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYjogV2ViR0xUZXh0dXJlLFxuICAgIGJTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS5iaW5hcnlPcChcbiAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhLCBhU2hhcGVSb3dDb2wsIGIsIGJTaGFwZVJvd0NvbCwgcmVzdWx0LFxuICAgICAgcmVzdWx0U2hhcGVSb3dDb2wpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkU2NhbGFyUGx1c01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhNaW51c1NjYWxhckRvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBudW1iZXIsXG4gICAgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuTUFUUklYLCBhT3JpZW50YXRpb24sICctJywgT3BlcmFuZFR5cGUuU0NBTEFSLFxuICAgICAgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIGEsIGFTaGFwZSwgbmV3IEZsb2F0MzJBcnJheShbYl0pLCBbMSwgMV0sIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJNaW51c01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnLScsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJUaW1lc01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUaW1lc01hdHJpeERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCBzaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBzcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgIE9wZXJhbmRUeXBlLk1BVFJJWCwgYU9yaWVudGF0aW9uLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCwgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKGEsIHNoYXBlLCBiLCBzaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFBsdXNNYXRyaXhEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIGI6IEZsb2F0MzJBcnJheSwgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5NQVRSSVgsIGFPcmllbnRhdGlvbiwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgsIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChhLCBzaGFwZSwgYiwgc2hhcGUsIHNyYyk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGFyZ21pbm1heF9ncHUgZnJvbSAnLi9hcmdtaW5tYXhfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtgO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhcmdNYXhBID0gZ2V0QXJnTWluTWF4KG1hdHJpeEEpO1xuICAgICAgZmxvYXQgYXJnTWF4QiA9IGdldEFyZ01pbk1heChtYXRyaXhCKTtcbiAgICAgIGZsb2F0IHZhbHVlO1xuICAgICAgaWYgKGlzTmFOKGFyZ01heEEpKSB7XG4gICAgICAgIHZhbHVlID0gYXJnTWF4QTtcbiAgICAgIH0gZWxzZSBpZiAoaXNOYU4oYXJnTWF4QikpIHtcbiAgICAgICAgdmFsdWUgPSBhcmdNYXhCO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFsdWUgPSBmbG9hdChhcmdNYXhBID09IGFyZ01heEIpO1xuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcmdNYXhFcXVhbHNGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpLFxuICAgIGFyZ21pbm1heF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoJz4nLCByb3dzLCBjb2x1bW5zKSxcbiAgICBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKVxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXJnTWF4RXF1YWxzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIG1heEVxdWFsc1Byb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGI6IFdlYkdMVGV4dHVyZSwgbnVtUm93czogbnVtYmVyLCBudW1Db2xzOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCAxLCAxKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtYXhFcXVhbHNQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCB7SVNfTkFOX1NIQURFUl9GVU5DfSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtgO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGdldEFyZ01pbk1heChtYXRyaXhBKSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmZ1bmN0aW9uIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBjb21wT3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpLFxuICAgIGdldEZyYWdtZW50U2hhZGVyR2V0QXJnTWluTWF4U291cmNlKGNvbXBPcCwgcm93cywgY29sdW1ucyksXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJNYWluU291cmNlKClcbiAgXS5qb2luKCdcXG4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01pbkZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICc8Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcmdNYXhGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRBcmdNaW5NYXhGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnPicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoXG4gICAgY29tcE9wOiBzdHJpbmcsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gIHJldHVybiBgXG4gICAgY29uc3QgdmVjMiBkaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICAke0lTX05BTl9TSEFERVJfRlVOQ31cblxuICAgIGZsb2F0IGdldEFyZ01pbk1heChpbiBzYW1wbGVyMkQgbWF0cml4KSB7XG4gICAgICB2ZWMyIGJlc3RDUiA9IHZlYzIoMCwgMCk7XG4gICAgICBmbG9hdCBiZXN0VmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4LCBiZXN0Q1IpLnI7XG5cbiAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGRpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGRpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiBjciA9IHZlYzIoYywgcik7XG4gICAgICAgICAgdmVjMiB1diA9IChjciArIGhhbGZDUikgLyBkaW1DUjtcbiAgICAgICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXgsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHZhbHVlICR7Y29tcE9wfSBiZXN0VmFsdWUpIHtcbiAgICAgICAgICAgIGJlc3RWYWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgYmVzdENSID0gY3I7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gYmVzdENSLnggKyAoYmVzdENSLnkgKiBkaW1DUi54KTtcbiAgICB9XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcmdNaW5NYXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWluTWF4UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYU51bVJvd3M6IG51bWJlciwgYU51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1pbk1heFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3Bvb2xfZ3B1JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyQXZnUG9vbFNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIHBvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnYXZnJywgZmFsc2UpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXZnUG9vbChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcG9vbF9ncHUucG9vbENvbW1vbihncGdwdSwgcHJvZ3JhbSwgeCwgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHhUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdLCBtZWFuVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSxcbiAgICB2YXJpYW5jZVRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgb2Zmc2V0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXXxudWxsLFxuICAgIHNjYWxlVGV4U2hhcGVSQz86IFtudW1iZXIsIG51bWJlcl18bnVsbCwgdmFyaWFuY2VFcHNpbG9uID0gMC4wMDEpOiBzdHJpbmcge1xuICBsZXQgb2Zmc2V0U2FtcGxlclNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRDb29yZHNTbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRVVlNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFZhbHVlU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0T3BlcmF0aW9uU25pcHBldCA9ICcwLjAnO1xuXG4gIGxldCBzY2FsZVNhbXBsZXJTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZUNvb3Jkc1NuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlVVZTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZVZhbHVlU25pcHBldCA9ICcnO1xuICBsZXQgc2NhbGVPcGVyYXRpb25TbmlwcGV0ID0gJyc7XG5cbiAgaWYgKG9mZnNldFRleFNoYXBlUkMgIT0gbnVsbCkge1xuICAgIG9mZnNldFNhbXBsZXJTbmlwcGV0ID0gJ3VuaWZvcm0gc2FtcGxlcjJEIG9mZnNldDsnO1xuICAgIG9mZnNldFNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gYGNvbnN0IHZlYzIgb2Zmc2V0U2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICAgICAke29mZnNldFRleFNoYXBlUkNbMV19LCAke29mZnNldFRleFNoYXBlUkNbMF19KTtgO1xuICAgIG9mZnNldENvb3Jkc1NuaXBwZXQgPSAndmVjMiBvZmZzZXRDb29yZHNDUiA9IG1vZCh5VGV4Q1IsIG9mZnNldFNoYXBlQ1IpOyc7XG4gICAgb2Zmc2V0VVZTbmlwcGV0ID1cbiAgICAgICAgJ3ZlYzIgb2Zmc2V0VVYgPSAob2Zmc2V0Q29vcmRzQ1IgKyBoYWxmQ1IpIC8gb2Zmc2V0U2hhcGVDUjsnO1xuICAgIG9mZnNldFZhbHVlU25pcHBldCA9ICdmbG9hdCBvZmZzZXRWYWx1ZSA9IHRleHR1cmUyRChvZmZzZXQsIG9mZnNldFVWKS5yOyc7XG4gICAgb2Zmc2V0T3BlcmF0aW9uU25pcHBldCA9ICdvZmZzZXRWYWx1ZSc7XG4gIH1cblxuICBpZiAoc2NhbGVUZXhTaGFwZVJDICE9IG51bGwpIHtcbiAgICBzY2FsZVNhbXBsZXJTbmlwcGV0ID0gJ3VuaWZvcm0gc2FtcGxlcjJEIHNjYWxlOyc7XG4gICAgc2NhbGVTaGFwZUluaXRpYWxpemF0aW9uU25pcHBldCA9IGBjb25zdCB2ZWMyIHNjYWxlU2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICAgICAke3NjYWxlVGV4U2hhcGVSQ1sxXX0sICR7c2NhbGVUZXhTaGFwZVJDWzBdfSk7YDtcbiAgICBzY2FsZUNvb3Jkc1NuaXBwZXQgPSAndmVjMiBzY2FsZUNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgc2NhbGVTaGFwZUNSKTsnO1xuICAgIHNjYWxlVVZTbmlwcGV0ID0gJ3ZlYzIgc2NhbGVVViA9IChzY2FsZUNvb3Jkc0NSICsgaGFsZkNSKSAvIHNjYWxlU2hhcGVDUjsnO1xuICAgIHNjYWxlVmFsdWVTbmlwcGV0ID0gJ2Zsb2F0IHNjYWxlVmFsdWUgPSB0ZXh0dXJlMkQoc2NhbGUsIHNjYWxlVVYpLnI7JztcbiAgICBzY2FsZU9wZXJhdGlvblNuaXBwZXQgPSAnaW52ICo9IHNjYWxlVmFsdWU7JztcbiAgfVxuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWVhbjtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB2YXJpYW5jZTtcbiAgICAke29mZnNldFNhbXBsZXJTbmlwcGV0fVxuICAgICR7c2NhbGVTYW1wbGVyU25pcHBldH1cblxuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG4gICAgY29uc3QgdmVjMiBtZWFuU2hhcGVDUiA9IHZlYzIoJHttZWFuVGV4U2hhcGVSQ1sxXX0sICR7bWVhblRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHZhcmlhbmNlU2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICR7dmFyaWFuY2VUZXhTaGFwZVJDWzFdfSwgJHt2YXJpYW5jZVRleFNoYXBlUkNbMF19KTtcblxuICAgICR7b2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXR9XG4gICAgJHtzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0fVxuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCBmbG9hdCB2YXJpYW5jZUVwc2lsb24gPSAke3ZhcmlhbmNlRXBzaWxvbn07XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIHZlYzIgbWVhbkNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgbWVhblNoYXBlQ1IpO1xuICAgICAgdmVjMiB2YXJpYW5jZUNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgdmFyaWFuY2VTaGFwZUNSKTtcbiAgICAgICR7b2Zmc2V0Q29vcmRzU25pcHBldH1cbiAgICAgICR7c2NhbGVDb29yZHNTbmlwcGV0fVxuXG4gICAgICB2ZWMyIG1lYW5VViA9IChtZWFuQ29vcmRzQ1IgKyBoYWxmQ1IpIC8gbWVhblNoYXBlQ1I7XG4gICAgICB2ZWMyIHZhcmlhbmNlVVYgPSAodmFyaWFuY2VDb29yZHNDUiArIGhhbGZDUikgLyB2YXJpYW5jZVNoYXBlQ1I7XG4gICAgICAke29mZnNldFVWU25pcHBldH1cbiAgICAgICR7c2NhbGVVVlNuaXBwZXR9XG5cbiAgICAgIGZsb2F0IHhWYWx1ZSA9IHRleHR1cmUyRCh4LCByZXN1bHRVVikucjtcbiAgICAgIGZsb2F0IG1lYW5WYWx1ZSA9IHRleHR1cmUyRChtZWFuLCBtZWFuVVYpLnI7XG4gICAgICBmbG9hdCB2YXJpYW5jZVZhbHVlID0gdGV4dHVyZTJEKHZhcmlhbmNlLCB2YXJpYW5jZVVWKS5yO1xuICAgICAgJHtvZmZzZXRWYWx1ZVNuaXBwZXR9XG4gICAgICAke3NjYWxlVmFsdWVTbmlwcGV0fVxuXG4gICAgICBmbG9hdCBpbnYgPSAxLjAgLyBzcXJ0KHZhcmlhbmNlVmFsdWUgKyB2YXJpYW5jZUVwc2lsb24pO1xuICAgICAgJHtzY2FsZU9wZXJhdGlvblNuaXBwZXR9XG4gICAgICBmbG9hdCB4VGltZXNJbnYgPSB4VmFsdWUgKiBpbnY7XG4gICAgICBmbG9hdCBtZWFuVGltZXNJbnZXaXRoT2Zmc2V0ID0gJHtvZmZzZXRPcGVyYXRpb25TbmlwcGV0fVxuICAgICAgICAgIC0gbWVhblZhbHVlICogaW52O1xuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHhUaW1lc0ludiArIG1lYW5UaW1lc0ludldpdGhPZmZzZXQsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmF0Y2hOb3JtYWxpemF0aW9uKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHhTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgbWVhbjogV2ViR0xUZXh0dXJlLFxuICAgIG1lYW5TaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgdmFyaWFuY2U6IFdlYkdMVGV4dHVyZSxcbiAgICB2YXJpYW5jZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBvZmZzZXQ6IFdlYkdMVGV4dHVyZXxudWxsLFxuICAgIG9mZnNldFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsIHNjYWxlOiBXZWJHTFRleHR1cmV8bnVsbCxcbiAgICBzY2FsZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUobWVhbiwgJ21lYW4nLCAxKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHZhcmlhbmNlLCAndmFyaWFuY2UnLCAyKTtcbiAgbGV0IG5leHRJbmRleCA9IDM7XG4gIGlmIChvZmZzZXQgIT0gbnVsbCkge1xuICAgIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShvZmZzZXQsICdvZmZzZXQnLCBuZXh0SW5kZXgpO1xuICAgIG5leHRJbmRleCsrO1xuICB9XG4gIGlmIChzY2FsZSAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNjYWxlLCAnc2NhbGUnLCBuZXh0SW5kZXgpO1xuICB9XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIGFSZXN1bHRVVjogc3RyaW5nLCBiUmVzdWx0VVY6IHN0cmluZywgb3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhID0gdGV4dHVyZTJEKG1hdHJpeEEsICR7YVJlc3VsdFVWfSkucjtcbiAgICAgIGZsb2F0IGIgPSB0ZXh0dXJlMkQobWF0cml4QiwgJHtiUmVzdWx0VVZ9KS5yO1xuICAgICAgJHtvcH1cbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmFyeU9wKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYjogV2ViR0xUZXh0dXJlLFxuICAgIGJTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIGFTaGFwZTogW251bWJlciwgbnVtYmVyXSwgYjogRmxvYXQzMkFycmF5LFxuICAgIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSwgZnJhZ21lbnRTaGFkZXJTb3VyY2U6IHN0cmluZyk6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtID0gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG5cbiAgY29uc3QgYVRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGFTaGFwZVswXSwgYVNoYXBlWzFdKTtcbiAgY29uc3QgYlRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGJTaGFwZVswXSwgYlNoYXBlWzFdKTtcblxuICBjb25zdCByZXN1bHRTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICBbTWF0aC5tYXgoYVNoYXBlWzBdLCBiU2hhcGVbMF0pLCBNYXRoLm1heChhU2hhcGVbMV0sIGJTaGFwZVsxXSldO1xuXG4gIGNvbnN0IHJlc3VsdFRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJlc3VsdFNoYXBlWzBdLCByZXN1bHRTaGFwZVsxXSk7XG5cbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCBhU2hhcGVbMF0sIGFTaGFwZVsxXSwgYSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShiVGV4dHVyZSwgYlNoYXBlWzBdLCBiU2hhcGVbMV0sIGIpO1xuXG4gIGJpbmFyeU9wKFxuICAgICAgZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCBhU2hhcGUsIGJUZXh0dXJlLCBiU2hhcGUsIHJlc3VsdFRleHR1cmUsXG4gICAgICByZXN1bHRTaGFwZSk7XG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICByZXN1bHRUZXh0dXJlLCByZXN1bHRTaGFwZVswXSwgcmVzdWx0U2hhcGVbMV0pO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGJUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgeDFTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB4MlNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgcmVzdWx0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgYXhpczogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgeDFUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MVNoYXBlUkNEKTtcbiAgY29uc3QgeDJUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MlNoYXBlUkNEKTtcblxuICBjb25zdCB5QXhlcyA9IFsneVInLCAneUMnLCAneUQnXTtcbiAgY29uc3QgY29uY2F0QXhpcyA9IHlBeGVzW2F4aXNdO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHgxO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHgyO1xuXG4gICAgY29uc3QgdmVjMiB4MVNoYXBlQ1IgPSB2ZWMyKCR7eDFUZXhTaGFwZVJDWzFdfSwgJHt4MVRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHgyU2hhcGVDUiA9IHZlYzIoJHt4MlRleFNoYXBlUkNbMV19LjAsICR7eDJUZXhTaGFwZVJDWzBdfS4wKTtcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIHlEKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtyZXN1bHRTaGFwZVJDRFsyXX0uMCk7XG4gICAgICBmbG9hdCB5RCA9IG1vZCh5VGV4Q1IueCwgJHtyZXN1bHRTaGFwZVJDRFsyXX0uMCk7XG5cbiAgICAgIGZsb2F0IHZhbHVlID0gMC4wO1xuXG4gICAgICBpZiAoJHtjb25jYXRBeGlzfSA8ICR7eDFTaGFwZVJDRFtheGlzXX0uMCkge1xuICAgICAgICAvLyBNYXAgeVIsIHlDLCB5RCBiYWNrIHRvIHgxIGNvb3JkaW5hdGVzLlxuICAgICAgICB2ZWMyIHgxQ1IgPSB2ZWMyKHlDICogJHt4MVNoYXBlUkNEWzJdfS4wICsgeUQsIHlSKTtcbiAgICAgICAgdmVjMiB4MVVWID0gKHgxQ1IgKyBoYWxmQ1IpIC8geDFTaGFwZUNSO1xuICAgICAgICB2YWx1ZSA9IHRleHR1cmUyRCh4MSwgeDFVVikucjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgICR7Y29uY2F0QXhpc30gPSAke2NvbmNhdEF4aXN9IC0gJHt4MVNoYXBlUkNEW2F4aXNdfS4wO1xuXG4gICAgICAgIC8vIE1hcCB5UiwgeUMsIHlEIGJhY2sgdG8geDIgY29vcmRpbmF0ZXMuXG4gICAgICAgIHZlYzIgeDJDUiA9IHZlYzIoeUMgKiAke3gyU2hhcGVSQ0RbMl19LjAgKyB5RCwgeVIpO1xuICAgICAgICB2ZWMyIHgyVVYgPSAoeDJDUiArIGhhbGZDUikgLyB4MlNoYXBlQ1I7XG4gICAgICAgIHZhbHVlID0gdGV4dHVyZTJEKHgyLCB4MlVWKS5yO1xuICAgICAgfVxuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLjAsIDAuMCwgMC4wKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbmNhdDNEKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDE6IFdlYkdMVGV4dHVyZSxcbiAgICB4MjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcmVzdWx0U2hhcGVSQ1swXSwgcmVzdWx0U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4MSwgJ3gxJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4MiwgJ3gyJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuXG5pbXBvcnQgKiBhcyBjb252X2dwdSBmcm9tICcuL2NvbnZfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJXZWlnaHRzU291cmNlKFxuICAgIHhTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsXG4gICAgb3V0cHV0RGVwdGg6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHplcm9QYWQ6IG51bWJlcikge1xuICBjb25zdCBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCA9XG4gICAgICBjb252X2dwdS5nZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk7XG4gIGNvbnN0IGlucHV0RGVwdGggPSB4U2hhcGVSb3dDb2xEZXB0aFsyXTtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUm93Q29sRGVwdGgpO1xuXG4gIGNvbnN0IHlTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgIHhTaGFwZVJvd0NvbERlcHRoLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gIGNvbnN0IHlOdW1Sb3dzID0geVNoYXBlWzBdO1xuICBjb25zdCB5TnVtQ29scyA9IHlTaGFwZVsxXTtcbiAgY29uc3QgeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHlTaGFwZSk7XG5cbiAgY29uc3QgZlNpemVUaW1lc0lucHV0RGVwdGggPSBmU2l6ZSAqIGlucHV0RGVwdGg7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG4gIGA7XG5cbiAgcmV0dXJuIHByb2xvZ3VlICsgJ1xcbicgKyBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCArICdcXG4nICtcbiAgICAgIGBcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG4gICAgY29uc3QgdmVjMiBkeVNoYXBlQ1IgPSB2ZWMyKCR7eVRleFNoYXBlUkNbMV19LCAke3lUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHdUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh3VGV4Uiwgd1RleEMpIHRvIDREICh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICBmbG9hdCB3UiA9IGZsb29yKHdUZXhDUi55IC8gJHtmU2l6ZVRpbWVzSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCB3VGV4UkxlZnRvdmVyID0gd1RleENSLnkgLSB3UiAqICR7ZlNpemVUaW1lc0lucHV0RGVwdGh9LjA7XG4gICAgICBmbG9hdCB3QyA9IGZsb29yKHdUZXhSTGVmdG92ZXIgLyAke2lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDEgPSBtb2Qod1RleFJMZWZ0b3ZlciwgJHtpbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQyID0gd1RleENSLng7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggZHkoOiwgOiwgZDIpIHRvIGdldCBkdyh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHlSID0gMC4wOyB5UiA8ICR7eU51bVJvd3N9LjA7IHlSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHdSICsgeVIgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcbiAgICAgICAgZm9yIChmbG9hdCB5QyA9IDAuMDsgeUMgPCAke3lOdW1Db2xzfS4wOyB5QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHdDICsgeUMgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcblxuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh4UiwgeEMsIGQxKSB0byAyRCAoeFRleFIsIHhUZXhDKS5cbiAgICAgICAgICAvLyBNYXAgZnJvbSAzRCAoeVIsIHlDLCBkMikgdG8gMkQgKHlUZXhSLCB5VGV4QykuXG4gICAgICAgICAgdmVjMiB4eVRleEMgPSB2ZWMyKHhDLCB5QykgKiB2ZWMyKCR7aW5wdXREZXB0aH0uMCwgJHtvdXRwdXREZXB0aH0uMCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgdmVjMihkMSwgZDIpO1xuICAgICAgICAgIGZsb2F0IHhUZXhDID0geHlUZXhDLng7XG4gICAgICAgICAgZmxvYXQgeVRleEMgPSB4eVRleEMueTtcblxuICAgICAgICAgIC8vIFJlYWQgZHkoeVIsIHlDLCBkMikuXG4gICAgICAgICAgdmVjMiBkeVVWID0gKHZlYzIoeVRleEMsIHlUZXhSKSArIGhhbGZDUikgLyBkeVNoYXBlQ1I7XG4gICAgICAgICAgZmxvYXQgZHlWYWx1ZSA9IHRleHR1cmUyRChkeSwgZHlVVikucjtcblxuICAgICAgICAgIC8vIFJlYWQgeCh4UiwgeEMsIGQxKSAocG90ZW50aWFsbHkgemVyby1wYWRkZWQpLlxuICAgICAgICAgIGZsb2F0IHhWYWx1ZSA9XG4gICAgICAgICAgICBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCh4LCB4U2hhcGVDUiwgdmVjMih4VGV4QywgeFRleFIpKTtcblxuICAgICAgICAgIGRvdFByb2QgKz0gKHhWYWx1ZSAqIGR5VmFsdWUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvcmlnSW5wdXREZXB0aDogbnVtYmVyLFxuICAgIG9yaWdTdHJpZGU6IG51bWJlciwgb3JpZ1BhZDogbnVtYmVyLCBoYXNCaWFzOiBib29sZWFuKSB7XG4gIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gIGNvbnN0IFt4Um93cywgeENvbHMsIG9yaWdPdXRwdXREZXB0aF0gPSB4U2hhcGVSQ0Q7XG5cbiAgY29uc3QgeFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHhTaGFwZVJDRCk7XG4gIGNvbnN0IHdUZXhTaGFwZVJDID1cbiAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKG9yaWdJbnB1dERlcHRoLCBvcmlnT3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICBjb25zdCBnZXRCaWFzVmFsdWUgPSBoYXNCaWFzID9cbiAgICAgIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKG9yaWdJbnB1dERlcHRoKSA6XG4gICAgICAnJztcbiAgY29uc3QgYmlhc1Byb2xvZ3VlID0gaGFzQmlhcyA/ICd1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7JyA6ICcnO1xuICBjb25zdCBiaWFzT3BlcmF0aW9uID0gaGFzQmlhcyA/ICdkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTsnIDogJyc7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICAke2JpYXNQcm9sb2d1ZX1cbiAgICBgO1xuXG4gIHJldHVybiBwcm9sb2d1ZSArICdcXG4nICsgZ2V0Qmlhc1ZhbHVlICsgJ1xcbicgK1xuICAgICAgYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAtIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IHhSQ29ybmVyID0geFJDQ29ybmVyLng7XG4gICAgICBmbG9hdCB4Q0Nvcm5lciA9IHhSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBDb252b2x2ZSB4KD8sID8sIGQxKSB3aXRoIHcoOiwgOiwgZDIsIGQxKSB0byBnZXQgeSh5UiwgeUMsIGQyKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG5cbiAgICAgICAgZmxvYXQgeFIgPSAoeFJDb3JuZXIgKyB3UikgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgIC8vIFRPRE8oc21pbGtvdik6IFNwbGljZSB0aGlzIHdpdGggYW5vdGhlciB2ZXJzaW9uIHdoZXJlIHlvdSBjYWxsXG4gICAgICAgIC8vIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKCkuIEhlcmUgYW5kIGJlbG93LlxuICAgICAgICBpZiAoeFIgPCAwLjAgfHwgeFIgPj0gJHt4Um93c30uMCB8fCBmcmFjdCh4UikgPiAwLjApIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZsb2F0IHdSUGVybSA9ICR7ZlNpemV9LjAgLSAxLjAgLSB3UjtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHdDID0gMC4wOyB3QyA8ICR7ZlNpemV9LjA7IHdDICs9IDEuMCkge1xuXG4gICAgICAgICAgZmxvYXQgeEMgPSAoeENDb3JuZXIgKyB3QykgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgICAgaWYgKHhDIDwgMC4wIHx8IHhDID49ICR7eENvbHN9LjAgfHwgZnJhY3QoeEMpID4gMC4wKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBmbG9hdCB3Q1Blcm0gPSAke2ZTaXplfS4wIC0gMS4wIC0gd0M7XG4gICAgICAgICAgZmxvYXQgd1RleFIgPSB3UlBlcm0gKiAke2ZTaXplfS4wICogJHtvcmlnSW5wdXREZXB0aH0uMCArXG4gICAgICAgICAgICAgICAgICAgICAgICB3Q1Blcm0gKiAke29yaWdJbnB1dERlcHRofS4wICsgZDI7XG5cbiAgICAgICAgICBmb3IgKGZsb2F0IGQxID0gMC4wOyBkMSA8ICR7b3JpZ091dHB1dERlcHRofS4wOyBkMSArPSAxLjApIHtcbiAgICAgICAgICAgIGZsb2F0IHhUZXhDID0geEMgKiAke29yaWdPdXRwdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleEMgPSBkMTtcblxuICAgICAgICAgICAgLy8gUmVhZCB4KHhSLCB4QywgZDEpLlxuICAgICAgICAgICAgdmVjMiB4VVYgPSAodmVjMih4VGV4QywgeFRleFIpICsgaGFsZkNSKSAvIHhTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgeFZhbHVlID0gdGV4dHVyZTJEKHgsIHhVVikucjtcblxuICAgICAgICAgICAgLy8gUmVhZCB3KHdSUGVybSwgd0NQZXJtLCBkMiwgZDEpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAke2JpYXNPcGVyYXRpb259XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJCaWFzU291cmNlKFxuICAgIGR5U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuICBjb25zdCBbeU51bVJvd3MsIHlOdW1Db2xzLCBvdXRwdXREZXB0aF0gPSBkeVNoYXBlUkNEO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGR5O1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHtkeVRleFNoYXBlUkNbMV19LCAke2R5VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBiaWFzVGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBUaGUgYmlhcyB0ZXh0dXJlIFJDIHNoYXBlIGlzIFsxLCBkMl0uXG4gICAgICBmbG9hdCBkMiA9IGJpYXNUZXhDUi54O1xuXG4gICAgICBmbG9hdCBkZXJCaWFzID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB5UiA9IDAuMDsgeVIgPCAke3lOdW1Sb3dzfS4wOyB5UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHlDID0gMC4wOyB5QyA8ICR7eU51bUNvbHN9LjA7IHlDICs9IDEuMCkge1xuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh5UiwgeUMsIGQyKSB0byAyRCAoeVRleFIsIHlUZXhDKS5cbiAgICAgICAgICBmbG9hdCB5VGV4QyA9IHlDICogJHtvdXRwdXREZXB0aH0uMCArIGQyO1xuXG4gICAgICAgICAgLy8gUmVhZCBkeSh5UiwgeUMsIGQyKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMih5VGV4QywgeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgZGVyQmlhcyArPSBkeVZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRlckJpYXMsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVyQmlhcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGR5VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZXJXZWlnaHRzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeFRleDogV2ViR0xUZXh0dXJlLFxuICAgIGR5VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoZHlUZXgsICdkeScsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29udlRyYW5zcG9zZShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHhUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICB3ZWlnaHRzVGV4OiBXZWJHTFRleHR1cmUsIGJpYXNlc1RleDogV2ViR0xUZXh0dXJlfG51bGwsXG4gICAgcmVzdWx0VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUod2VpZ2h0c1RleCwgJ3dlaWdodHMnLCAxKTtcbiAgaWYgKGJpYXNlc1RleCAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGJpYXNlc1RleCwgJ2JpYXNlcycsIDIpO1xuICB9XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoaW4gc2FtcGxlcjJEIG1hdHJpeCwgdmVjMiBtYXRyaXhTaGFwZUNSLFxuICAgICAgICB2ZWMyIHJlcXVlc3RlZENSKSB7XG4gICAgICB2ZWMyIHV2ID0gKHJlcXVlc3RlZENSICsgdmVjMigwLjUsIDAuNSkpIC8gbWF0cml4U2hhcGVDUjtcbiAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeCwgdXYpLnI7XG4gICAgICBib29sIGxlc3NUaGFuWmVybyA9IGFueShsZXNzVGhhbih1diwgdmVjMigwLCAwKSkpO1xuICAgICAgYm9vbCBncmVhdGVyVGhhbk9uZSA9IGFueShncmVhdGVyVGhhbih1diwgdmVjMigxLCAxKSkpO1xuICAgICAgYm9vbCBvdXRzaWRlID0gbGVzc1RoYW5aZXJvIHx8IGdyZWF0ZXJUaGFuT25lO1xuICAgICAgcmV0dXJuIG1peCh2YWx1ZSwgMC4wLCBmbG9hdChvdXRzaWRlKSk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlciwgaGFzQmlhczogYm9vbGVhbikge1xuICBjb25zdCBbeFJvd3MsIHhDb2xzLCBpbnB1dERlcHRoXSA9IHhTaGFwZVJDRDtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcbiAgY29uc3Qgd1RleFNoYXBlUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICByZXR1cm4gYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgd1RleEMgPSBkMjtcblxuICAgICAgdmVjMiB4UkNDb3JuZXIgPSB2ZWMyKHlSLCB5QykgKiB2ZWMyKCR7c3RyaWRlfSwgJHtzdHJpZGV9KSAtXG4gICAgICAgICAgdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggdyg6LCA6LCBkMSwgZDIpIHRvIGdldCB5KHlSLCB5QywgZDIpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeFIgPSB4UkNvcm5lciArIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG4gICAgICAgICAgZmxvYXQgeEMgPSB4Q0Nvcm5lciArIHdDO1xuXG4gICAgICAgICAgZm9yIChmbG9hdCBkMSA9IDAuMDsgZDEgPCAke2lucHV0RGVwdGh9LjA7IGQxICs9IDEuMCkge1xuICAgICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleFIgPSB3UiAqICR7ZlNpemUgKiBpbnB1dERlcHRofS4wICtcbiAgICAgICAgICAgICAgICB3QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuXG4gICAgICAgICAgICBmbG9hdCB4VmFsdWUgPVxuICAgICAgICAgICAgICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKHgsIHhTaGFwZUNSLCB2ZWMyKHhUZXhDLCB4VGV4UikpO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHcod1IsIHdDLCBkMSwgZDIpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoJHtoYXNCaWFzfSkge1xuICAgICAgICBkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTtcbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldEJpYXNWYWx1ZVNvdXJjZShvdXRwdXREZXB0aDogbnVtYmVyKTpcbiAgICBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGZsb2F0IGdldEJpYXNWYWx1ZShpbiBzYW1wbGVyMkQgYmlhcywgZmxvYXQgYmlhc0MpIHtcbiAgICAgIGNvbnN0IHZlYzIgYmlhc1NoYXBlQ1IgPSB2ZWMyKCR7b3V0cHV0RGVwdGh9LCAxKTtcbiAgICAgIHZlYzIgYmlhc0NSID0gdmVjMihtb2QoYmlhc0MsICR7b3V0cHV0RGVwdGh9LjApLCAwKTtcbiAgICAgIHZlYzIgYmlhc1VWID0gKGJpYXNDUiArIHZlYzIoMC41LCAwLjUpKSAvIGJpYXNTaGFwZUNSO1xuICAgICAgcmV0dXJuIHRleHR1cmUyRChiaWFzLCBiaWFzVVYpLnI7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBhU2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCByZXN1bHREZXB0aDogbnVtYmVyLFxuICAgIGZpZWxkU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZDogbnVtYmVyLFxuICAgIGhhc0JpYXM6IGJvb2xlYW4pOiBzdHJpbmcge1xuICBjb25zdCBhU2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGFTaGFwZVJvd0NvbERlcHRoKTtcblxuICBjb25zdCB3ZWlnaHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID0gY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgICBhU2hhcGVSb3dDb2xEZXB0aFsyXSwgcmVzdWx0RGVwdGgsIGZpZWxkU2l6ZSk7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk7XG4gIGNvbnN0IGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkID1cbiAgICAgIGdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTtcbiAgY29uc3QgY29udm9sdmUgPSBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgICAgYVNoYXBlUm93Q29sRGVwdGgsIGZpZWxkU2l6ZSwgcmVzdWx0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCwgaGFzQmlhcyk7XG4gIGNvbnN0IGdldEJpYXNWYWx1ZSA9IGdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKHJlc3VsdERlcHRoKTtcblxuICByZXR1cm4gW1xuICAgIHByb2xvZ3VlLFxuICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkLFxuICAgIGdldEJpYXNWYWx1ZSxcbiAgICBjb252b2x2ZSxcbiAgXS5qb2luKCdcXG4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZvbHZlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHdlaWdodHM6IFdlYkdMVGV4dHVyZSwgYmlhc2VzOiBXZWJHTFRleHR1cmV8bnVsbCwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh3ZWlnaHRzLCAnd2VpZ2h0cycsIDEpO1xuICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYmlhc2VzLCAnYmlhc2VzJywgMik7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgc291cmNlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIHNvdXJjZVNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdW5pZm9ybSB2ZWMyIHNvdXJjZVN0YXJ0Q1I7XG4gICAgdW5pZm9ybSB2ZWMyIGRlc3RTdGFydENSO1xuXG4gICAgY29uc3QgdmVjMiBzb3VyY2VTaGFwZUNSID1cbiAgICAgIHZlYzIoJHtzb3VyY2VTaGFwZVJvd0NvbFsxXX0sICR7c291cmNlU2hhcGVSb3dDb2xbMF19KTtcbiAgICBjb25zdCB2ZWMyIHNvdXJjZVNpemVDUiA9XG4gICAgICB2ZWMyKCR7c291cmNlU2l6ZVJvd0NvbFsxXX0sICR7c291cmNlU2l6ZVJvd0NvbFswXX0pO1xuICAgIGNvbnN0IHZlYzIgZGVzdFNpemVDUiA9XG4gICAgICB2ZWMyKCR7ZGVzdFNpemVSb3dDb2xbMV19LCAke2Rlc3RTaXplUm93Q29sWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIGRlc3RPZmZzZXRDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSkgLSBkZXN0U3RhcnRDUjtcbiAgICAgIGZsb2F0IGRlc3RPZmZzZXRGbGF0ID0gKGRlc3RPZmZzZXRDUi55ICogZGVzdFNpemVDUi54KSArIGRlc3RPZmZzZXRDUi54O1xuICAgICAgdmVjMiBzb3VyY2VPZmZzZXRDUiA9IHZlYzIobW9kKGRlc3RPZmZzZXRGbGF0LCBzb3VyY2VTaXplQ1IueCksXG4gICAgICAgIGZsb29yKGRlc3RPZmZzZXRGbGF0IC8gc291cmNlU2l6ZUNSLngpKTtcbiAgICAgIHZlYzIgc291cmNlQ1IgPSBzb3VyY2VTdGFydENSICsgc291cmNlT2Zmc2V0Q1I7XG4gICAgICB2ZWMyIHNvdXJjZVVWID0gKHNvdXJjZUNSICsgdmVjMigwLjUsIDAuNSkpIC8gc291cmNlU2hhcGVDUjtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRChzb3VyY2UsIHNvdXJjZVVWKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvcHkoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBzb3VyY2U6IFdlYkdMVGV4dHVyZSxcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU3RhcnRSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogV2ViR0xUZXh0dXJlLFxuICAgIGRlc3RTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdFN0YXJ0Um93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoZGVzdCwgZGVzdFNoYXBlUm93Q29sWzBdLCBkZXN0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbihcbiAgICAgIGRlc3RTdGFydFJvd0NvbFswXSwgZGVzdFNpemVSb3dDb2xbMF0sIGRlc3RTdGFydFJvd0NvbFsxXSxcbiAgICAgIGRlc3RTaXplUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNvdXJjZSwgJ3NvdXJjZScsIDApO1xuICBjb25zdCBzb3VyY2VTdGFydENSTG9jID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdzb3VyY2VTdGFydENSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihcbiAgICAgIHNvdXJjZVN0YXJ0Q1JMb2MsIHNvdXJjZVN0YXJ0Um93Q29sWzFdLCBzb3VyY2VTdGFydFJvd0NvbFswXSk7XG4gIGNvbnN0IGRlc3RTdGFydENSTG9jID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdkZXN0U3RhcnRDUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYoZGVzdFN0YXJ0Q1JMb2MsIGRlc3RTdGFydFJvd0NvbFsxXSwgZGVzdFN0YXJ0Um93Q29sWzBdKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0RXhwVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gJ2dsX0ZyYWdDb2xvciA9IHZlYzQoZXhwKHZhbHVlKSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRFeHBVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZXhwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGV4cFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBleHBQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkRXhwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0RXhwVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgZ3BncHVfdXRpbCBmcm9tICcuL2dwZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgdGV4X3V0aWwgZnJvbSAnLi90ZXhfdXRpbCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmltcG9ydCB7V2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbn0gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGNsYXNzIEdQR1BVQ29udGV4dCB7XG4gIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIHRleHR1cmVGbG9hdEV4dGVuc2lvbjoge307XG4gIGNvbG9yQnVmZmVyRmxvYXRFeHRlbnNpb246IHt9O1xuICBsb3NlQ29udGV4dEV4dGVuc2lvbjogV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgdmVydGV4QnVmZmVyOiBXZWJHTEJ1ZmZlcjtcbiAgaW5kZXhCdWZmZXI6IFdlYkdMQnVmZmVyO1xuICBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcjtcbiAgb3V0cHV0VGV4dHVyZTogV2ViR0xUZXh0dXJlfG51bGwgPSBudWxsO1xuICBwcm9ncmFtOiBXZWJHTFByb2dyYW18bnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZGlzcG9zZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBhdXRvRGVidWdWYWxpZGF0ZSA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKGdsPzogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gICAgaWYgKGdsICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZ2wgPSBnbDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5nbCA9IGdwZ3B1X3V0aWwuY3JlYXRlV2ViR0xDb250ZXh0KCk7XG4gICAgfVxuXG4gICAgLy8gV2ViR0wgMi4wIGVuYWJsZXMgdGV4dHVyZSBmbG9hdHMgd2l0aG91dCBhbiBleHRlbnNpb24uXG4gICAgaWYgKCF3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgICB0aGlzLnRleHR1cmVGbG9hdEV4dGVuc2lvbiA9XG4gICAgICAgICAgd2ViZ2xfdXRpbC5nZXRFeHRlbnNpb25PclRocm93KHRoaXMuZ2wsICdPRVNfdGV4dHVyZV9mbG9hdCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNvbG9yQnVmZmVyRmxvYXRFeHRlbnNpb24gPVxuICAgICAgICAgIHdlYmdsX3V0aWwuZ2V0RXh0ZW5zaW9uT3JUaHJvdyh0aGlzLmdsLCAnRVhUX2NvbG9yX2J1ZmZlcl9mbG9hdCcpO1xuICAgIH1cblxuICAgIHRoaXMubG9zZUNvbnRleHRFeHRlbnNpb24gPVxuICAgICAgICB3ZWJnbF91dGlsLmdldEV4dGVuc2lvbk9yVGhyb3codGhpcy5nbCwgJ1dFQkdMX2xvc2VfY29udGV4dCcpIGFzXG4gICAgICAgIFdlYkdMTG9zZUNvbnRleHRFeHRlbnNpb247XG4gICAgdGhpcy52ZXJ0ZXhCdWZmZXIgPSBncGdwdV91dGlsLmNyZWF0ZVZlcnRleEJ1ZmZlcih0aGlzLmdsKTtcbiAgICB0aGlzLmluZGV4QnVmZmVyID0gZ3BncHVfdXRpbC5jcmVhdGVJbmRleEJ1ZmZlcih0aGlzLmdsKTtcbiAgICB0aGlzLmZyYW1lYnVmZmVyID0gd2ViZ2xfdXRpbC5jcmVhdGVGcmFtZWJ1ZmZlcih0aGlzLmdsKTtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwb3NlKCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSAhPSBudWxsKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ0Rpc3Bvc2luZyBhIEdQR1BVQ29udGV4dCB0aGF0IHN0aWxsIGhhcyBhIGJvdW5kIFdlYkdMUHJvZ3JhbS4nICtcbiAgICAgICAgICAnIFRoaXMgaXMgcHJvYmFibHkgYSByZXNvdXJjZSBsZWFrLCBkZWxldGUgdGhlIHByb2dyYW0gd2l0aCAnICtcbiAgICAgICAgICAnR1BHUFVDb250ZXh0LmRlbGV0ZVByb2dyYW0gYmVmb3JlIGRpc3Bvc2luZy4nKTtcbiAgICB9XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ0Rpc3Bvc2luZyBhIEdQR1BVQ29udGV4dCB0aGF0IHN0aWxsIGhhcyBhIGJvdW5kIG91dHB1dCBtYXRyaXggJyArXG4gICAgICAgICAgJ3RleHR1cmUuICBUaGlzIGlzIHByb2JhYmx5IGEgcmVzb3VyY2UgbGVhaywgZGVsZXRlIHRoZSBvdXRwdXQgJyArXG4gICAgICAgICAgJ21hdHJpeCB0ZXh0dXJlIHdpdGggR1BHUFVDb250ZXh0LmRlbGV0ZU1hdHJpeFRleHR1cmUgYmVmb3JlICcgK1xuICAgICAgICAgICdkaXNwb3NpbmcuJyk7XG4gICAgfVxuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZmluaXNoKCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIG51bGwpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlRnJhbWVidWZmZXIodGhpcy5mcmFtZWJ1ZmZlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVCdWZmZXIodGhpcy52ZXJ0ZXhCdWZmZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIG51bGwpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlQnVmZmVyKHRoaXMuaW5kZXhCdWZmZXIpKTtcbiAgICB0aGlzLmxvc2VDb250ZXh0RXh0ZW5zaW9uLmxvc2VDb250ZXh0KCk7XG4gICAgdGhpcy5kaXNwb3NlZCA9IHRydWU7XG4gIH1cblxuICBwdWJsaWMgZW5hYmxlQXV0b21hdGljRGVidWdWYWxpZGF0aW9uKGVuYWJsZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlID0gZW5hYmxlZDtcbiAgICB3ZWJnbF91dGlsLmVuYWJsZURlYnVnV2ViR0xFcnJvckNoZWNraW5nKGVuYWJsZWQpO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZU1hdHJpeFRleHR1cmUocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwuY3JlYXRlTWF0cml4VGV4dHVyZSh0aGlzLmdsLCByb3dzLCBjb2x1bW5zKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGxvYWRQaXhlbERhdGFUb1RleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgICBwaXhlbHM6IEltYWdlRGF0YXxIVE1MSW1hZ2VFbGVtZW50fEhUTUxDYW52YXNFbGVtZW50fEhUTUxWaWRlb0VsZW1lbnQpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGdwZ3B1X3V0aWwudXBsb2FkUGl4ZWxEYXRhVG9UZXh0dXJlKHRoaXMuZ2wsIHRleHR1cmUsIHBpeGVscyk7XG4gIH1cblxuICBwdWJsaWMgY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZShyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6XG4gICAgICBXZWJHTFRleHR1cmUge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwuY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZSh0aGlzLmdsLCByb3dzLCBjb2x1bW5zKTtcbiAgfVxuXG4gIHB1YmxpYyBkZWxldGVNYXRyaXhUZXh0dXJlKHRleHR1cmU6IFdlYkdMVGV4dHVyZSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSA9PT0gdGV4dHVyZSkge1xuICAgICAgd2ViZ2xfdXRpbC51bmJpbmRDb2xvclRleHR1cmVGcm9tRnJhbWVidWZmZXIodGhpcy5nbCwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgICB0aGlzLm91dHB1dFRleHR1cmUgPSBudWxsO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLmRlbGV0ZVRleHR1cmUodGV4dHVyZSkpO1xuICB9XG5cbiAgcHVibGljIHVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgbnVtQ2hhbm5lbHMgPSAxO1xuICAgIHJldHVybiBncGdwdV91dGlsLnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgICAgdGhpcy5nbCwgdGV4dHVyZSwgcm93cywgY29sdW1ucywgbWF0cml4LCBudW1DaGFubmVscyk7XG4gIH1cblxuICBwdWJsaWMgdXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICAgIG1hdHJpeDogRmxvYXQzMkFycmF5KSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC51cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgICAgIHRoaXMuZ2wsIHRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIG1hdHJpeCk7XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICAgIHJldHVybiB0aGlzLmRvd25sb2FkTWF0cml4RHJpdmVyKFxuICAgICAgICB0ZXh0dXJlLFxuICAgICAgICAoKSA9PlxuICAgICAgICAgICAgZ3BncHVfdXRpbC5kb3dubG9hZE1hdHJpeEZyb21PdXRwdXRUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpKTtcbiAgfVxuXG4gIHB1YmxpYyBkb3dubG9hZE1hdHJpeEZyb21QYWNrZWRUZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICAgIHRleHR1cmUsXG4gICAgICAgICgpID0+IGdwZ3B1X3V0aWwuZG93bmxvYWRNYXRyaXhGcm9tUGFja2VkT3V0cHV0VGV4dHVyZShcbiAgICAgICAgICAgIHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpKTtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFByb2dyYW0ge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIGNvbnN0IGZyYWdtZW50U2hhZGVyOiBXZWJHTFNoYWRlciA9XG4gICAgICAgIHdlYmdsX3V0aWwuY3JlYXRlRnJhZ21lbnRTaGFkZXIoZ2wsIGZyYWdtZW50U2hhZGVyU291cmNlKTtcbiAgICBjb25zdCB2ZXJ0ZXhTaGFkZXI6IFdlYkdMU2hhZGVyID0gZ3BncHVfdXRpbC5jcmVhdGVWZXJ0ZXhTaGFkZXIoZ2wpO1xuICAgIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IHdlYmdsX3V0aWwuY3JlYXRlUHJvZ3JhbShnbCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmF0dGFjaFNoYWRlcihwcm9ncmFtLCB2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIGZyYWdtZW50U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5saW5rUHJvZ3JhbShnbCwgcHJvZ3JhbSk7XG4gICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVQcm9ncmFtKGdsLCBwcm9ncmFtKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRldGFjaFNoYWRlcihwcm9ncmFtLCB2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlU2hhZGVyKHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZXRhY2hTaGFkZXIocHJvZ3JhbSwgZnJhZ21lbnRTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlU2hhZGVyKGZyYWdtZW50U2hhZGVyKSk7XG4gICAgcmV0dXJuIHByb2dyYW07XG4gIH1cblxuICBwdWJsaWMgZGVsZXRlUHJvZ3JhbShwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGlmIChwcm9ncmFtID09PSB0aGlzLnByb2dyYW0pIHtcbiAgICAgIHRoaXMucHJvZ3JhbSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChwcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKSk7XG4gICAgfVxuICB9XG5cbiAgcHVibGljIHNldFByb2dyYW0ocHJvZ3JhbTogV2ViR0xQcm9ncmFtfG51bGwpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMucHJvZ3JhbSA9IHByb2dyYW07XG4gICAgaWYgKCh0aGlzLnByb2dyYW0gIT0gbnVsbCkgJiYgdGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZVByb2dyYW0odGhpcy5nbCwgdGhpcy5wcm9ncmFtKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC51c2VQcm9ncmFtKHByb2dyYW0pKTtcbiAgfVxuXG4gIHB1YmxpYyBnZXRVbmlmb3JtTG9jYXRpb24odW5pZm9ybU5hbWU6IHN0cmluZyk6IFdlYkdMVW5pZm9ybUxvY2F0aW9uIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIHJldHVybiB3ZWJnbF91dGlsLmdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KFxuICAgICAgICB0aGlzLmdsLCB0aGlzLnByb2dyYW0hLCB1bmlmb3JtTmFtZSk7XG4gIH1cblxuICBwdWJsaWMgc2V0SW5wdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgaW5wdXRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHVuaWZvcm1OYW1lOiBzdHJpbmcsXG4gICAgICB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnRocm93SWZOb1Byb2dyYW0oKTtcbiAgICB3ZWJnbF91dGlsLmJpbmRUZXh0dXJlVG9Qcm9ncmFtVW5pZm9ybVNhbXBsZXIoXG4gICAgICAgIHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSEsIGlucHV0TWF0cml4VGV4dHVyZSwgdW5pZm9ybU5hbWUsIHRleHR1cmVVbml0KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgb3V0cHV0TWF0cml4VGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcikge1xuICAgIHRoaXMuc2V0T3V0cHV0TWF0cml4VGV4dHVyZURyaXZlcihvdXRwdXRNYXRyaXhUZXh0dXJlLCBjb2x1bW5zLCByb3dzKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRQYWNrZWRNYXRyaXhUZXh0dXJlKFxuICAgICAgb3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKG91dHB1dFBhY2tlZE1hdHJpeFRleHR1cmUsIHdpZHRoLCBoZWlnaHQpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgc3RhcnRSb3c6IG51bWJlciwgbnVtUm93czogbnVtYmVyLCBzdGFydENvbHVtbjogbnVtYmVyLFxuICAgICAgbnVtQ29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbkRyaXZlcihcbiAgICAgICAgc3RhcnRDb2x1bW4sIHN0YXJ0Um93LCBudW1Db2x1bW5zLCBudW1Sb3dzKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRQYWNrZWRNYXRyaXhXcml0ZVJlZ2lvbihcbiAgICAgIHN0YXJ0Um93OiBudW1iZXIsIG51bVJvd3M6IG51bWJlciwgc3RhcnRDb2x1bW46IG51bWJlcixcbiAgICAgIG51bUNvbHVtbnM6IG51bWJlcikge1xuICAgIHRocm93IG5ldyBFcnJvcignc2V0T3V0cHV0UGFja2VkTWF0cml4V3JpdGVSZWdpb24gbm90IGltcGxlbWVudGVkLicpO1xuICB9XG5cbiAgcHVibGljIGRlYnVnVmFsaWRhdGUoKSB7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSAhPSBudWxsKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlUHJvZ3JhbSh0aGlzLmdsLCB0aGlzLnByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIodGhpcy5nbCk7XG4gIH1cblxuICBwdWJsaWMgZXhlY3V0ZVByb2dyYW0oKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnRocm93SWZOb1Byb2dyYW0oKTtcbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgZ3BncHVfdXRpbC5iaW5kVmVydGV4UHJvZ3JhbUF0dHJpYnV0ZVN0cmVhbXMoXG4gICAgICAgIGdsLCB0aGlzLnByb2dyYW0hLCB0aGlzLnZlcnRleEJ1ZmZlcik7XG4gICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHRoaXMuZGVidWdWYWxpZGF0ZSgpO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgZ2wsICgpID0+IGdsLmRyYXdFbGVtZW50cyhnbC5UUklBTkdMRVMsIDYsIGdsLlVOU0lHTkVEX1NIT1JULCAwKSk7XG4gIH1cblxuICBwdWJsaWMgYmxvY2tVbnRpbEFsbFByb2dyYW1zQ29tcGxldGVkKCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5maW5pc2goKSk7XG4gIH1cblxuICBwcml2YXRlIGRvd25sb2FkTWF0cml4RHJpdmVyKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLFxuICAgICAgZG93bmxvYWRBbmREZWNvZGU6ICgpID0+IEZsb2F0MzJBcnJheSk6IEZsb2F0MzJBcnJheSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICB0aGlzLmdsLCB0ZXh0dXJlLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICBjb25zdCByZXN1bHQgPSBkb3dubG9hZEFuZERlY29kZSgpO1xuICAgIGlmICh0aGlzLm91dHB1dFRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgd2ViZ2xfdXRpbC5iaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICAgICAgICB0aGlzLmdsLCB0aGlzLm91dHB1dFRleHR1cmUsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZUZyYW1lYnVmZmVyKHRoaXMuZ2wpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB3ZWJnbF91dGlsLnVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcih0aGlzLmdsLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgc2V0T3V0cHV0TWF0cml4VGV4dHVyZURyaXZlcihcbiAgICAgIG91dHB1dE1hdHJpeFRleHR1cmVNYXliZVBhY2tlZDogV2ViR0xUZXh0dXJlLCB3aWR0aDogbnVtYmVyLFxuICAgICAgaGVpZ2h0OiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICBnbCwgb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZUZyYW1lYnVmZmVyKGdsKTtcbiAgICB9XG4gICAgdGhpcy5vdXRwdXRUZXh0dXJlID0gb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC52aWV3cG9ydCgwLCAwLCB3aWR0aCwgaGVpZ2h0KSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNjaXNzb3IoMCwgMCwgd2lkdGgsIGhlaWdodCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbkRyaXZlcihcbiAgICAgIHg6IG51bWJlciwgeTogbnVtYmVyLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICAgIHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuc2Npc3Nvcih4LCB5LCB3aWR0aCwgaGVpZ2h0KSk7XG4gIH1cblxuICBwcml2YXRlIHRocm93SWZEaXNwb3NlZCgpIHtcbiAgICBpZiAodGhpcy5kaXNwb3NlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgdG8gdXNlIGRpc3Bvc2VkIEdQR1BVQ29udGV4dC4nKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHRocm93SWZOb1Byb2dyYW0oKSB7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIEdQVSBwcm9ncmFtIGlzIGN1cnJlbnRseSBzZXQuJyk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHRleF91dGlsIGZyb20gJy4vdGV4X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0V2ViR0xDb250ZXh0QXR0cmlidXRlcygpOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzIHtcbiAgcmV0dXJuIHtcbiAgICBhbHBoYTogZmFsc2UsXG4gICAgYW50aWFsaWFzOiBmYWxzZSxcbiAgICBwcmVtdWx0aXBsaWVkQWxwaGE6IGZhbHNlLFxuICAgIHByZXNlcnZlRHJhd2luZ0J1ZmZlcjogZmFsc2UsXG4gICAgZGVwdGg6IGZhbHNlLFxuICAgIHN0ZW5jaWw6IGZhbHNlLFxuICAgIGZhaWxJZk1ham9yUGVyZm9ybWFuY2VDYXZlYXQ6IHRydWVcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMQ29udGV4dChjYW52YXM/OiBIVE1MQ2FudmFzRWxlbWVudCkge1xuICBjb25zdCBhdHRyaWJ1dGVzID0gZ2V0V2ViR0xDb250ZXh0QXR0cmlidXRlcygpO1xuICBsZXQgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgaWYgKGNhbnZhcyAhPSBudWxsKSB7XG4gICAgZ2wgPSB3ZWJnbF91dGlsLmNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoY2FudmFzLCBhdHRyaWJ1dGVzKTtcbiAgfSBlbHNlIHtcbiAgICBnbCA9IHdlYmdsX3V0aWwuY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0KGF0dHJpYnV0ZXMpO1xuICB9XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkRFUFRIX1RFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuU1RFTkNJTF9URVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkJMRU5EKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkRJVEhFUikpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5QT0xZR09OX09GRlNFVF9GSUxMKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLlNBTVBMRV9DT1ZFUkFHRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlKGdsLlNDSVNTT1JfVEVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlKGdsLkNVTExfRkFDRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuY3VsbEZhY2UoZ2wuQkFDSykpO1xuICByZXR1cm4gZ2w7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhTaGFkZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMU2hhZGVyIHtcbiAgY29uc3QgdmVydGV4U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICBhdHRyaWJ1dGUgdmVjMyBjbGlwU3BhY2VQb3M7XG4gICAgYXR0cmlidXRlIHZlYzIgdXY7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZ2xfUG9zaXRpb24gPSB2ZWM0KGNsaXBTcGFjZVBvcywgMSk7XG4gICAgICByZXN1bHRVViA9IHV2O1xuICAgIH1gO1xuICByZXR1cm4gd2ViZ2xfdXRpbC5jcmVhdGVWZXJ0ZXhTaGFkZXIoZ2wsIHZlcnRleFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhCdWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMQnVmZmVyIHtcbiAgLy8gW3ggeSB6IHUgdl0gKiBbdXBwZXItbGVmdCwgbG93ZXItbGVmdCwgdXBwZXItcmlnaHQsIGxvd2VyLXJpZ2h0XVxuICBjb25zdCB2ZXJ0ZXhBcnJheSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICBbLTEsIDEsIDAsIDAsIDEsIC0xLCAtMSwgMCwgMCwgMCwgMSwgMSwgMCwgMSwgMSwgMSwgLTEsIDAsIDEsIDBdKTtcbiAgcmV0dXJuIHdlYmdsX3V0aWwuY3JlYXRlU3RhdGljVmVydGV4QnVmZmVyKGdsLCB2ZXJ0ZXhBcnJheSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJbmRleEJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xCdWZmZXIge1xuICAvLyBPcGVuR0wgKGFuZCBXZWJHTCkgaGF2ZSBcIkNDVyA9PSBmcm9udFwiIHdpbmRpbmdcbiAgY29uc3QgdHJpYW5nbGVWZXJ0ZXhJbmRpY2VzID0gbmV3IFVpbnQxNkFycmF5KFswLCAxLCAyLCAyLCAxLCAzXSk7XG4gIHJldHVybiB3ZWJnbF91dGlsLmNyZWF0ZVN0YXRpY0luZGV4QnVmZmVyKGdsLCB0cmlhbmdsZVZlcnRleEluZGljZXMpO1xufVxuXG5mdW5jdGlvbiBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbnVtQ2hhbm5lbHM6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgaWYgKG51bUNoYW5uZWxzID09PSA0KSB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICByZXR1cm4gKGdsIGFzIGFueSkuUkdCQTMyRjtcbiAgICB9XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIHJldHVybiAoZ2wgYXMgYW55KS5SMzJGO1xuICB9XG4gIHJldHVybiBnbC5SR0JBO1xufVxuXG5mdW5jdGlvbiBnZXRUZXh0dXJlRm9ybWF0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIG51bUNoYW5uZWxzOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAod2ViZ2xfdXRpbC5pc1dlYkdMMkVuYWJsZWQoKSAmJiBudW1DaGFubmVscyA9PT0gMSkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKGdsIGFzIGFueSkuUkVEO1xuICB9XG4gIHJldHVybiBnbC5SR0JBO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyLFxuICAgIG51bUNoYW5uZWxzOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICB3ZWJnbF91dGlsLnZhbGlkYXRlVGV4dHVyZVNpemUoZ2wsIHdpZHRoLCBoZWlnaHQpO1xuICBjb25zdCB0ZXh0dXJlID0gd2ViZ2xfdXRpbC5jcmVhdGVUZXh0dXJlKGdsKTtcblxuICBjb25zdCB0ZXgyZCA9IGdsLlRFWFRVUkVfMkQ7XG4gIGNvbnN0IGludGVybmFsRm9ybWF0ID0gZ2V0VGV4dHVyZUludGVybmFsRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG4gIGNvbnN0IGZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKHRleDJkLCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfV1JBUF9TLCBnbC5DTEFNUF9UT19FREdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfV1JBUF9ULCBnbC5DTEFNUF9UT19FREdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfTUlOX0ZJTFRFUiwgZ2wuTkVBUkVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC50ZXhQYXJhbWV0ZXJpKHRleDJkLCBnbC5URVhUVVJFX01BR19GSUxURVIsIGdsLk5FQVJFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnRleEltYWdlMkQoXG4gICAgICAgICAgdGV4MmQsIDAsIGludGVybmFsRm9ybWF0LCB3aWR0aCwgaGVpZ2h0LCAwLCBmb3JtYXQsIGdsLkZMT0FULCBudWxsKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG4gIHJldHVybiB0ZXh0dXJlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDE7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVDb2xvck1hdHJpeFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0Q29sb3JNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICByZXR1cm4gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShnbCwgd2lkdGgsIGhlaWdodCwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICByZXR1cm4gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShnbCwgd2lkdGgsIGhlaWdodCwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFZlcnRleFByb2dyYW1BdHRyaWJ1dGVTdHJlYW1zKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICB2ZXJ0ZXhCdWZmZXI6IFdlYkdMQnVmZmVyKSB7XG4gIGNvbnN0IHBvc09mZnNldCA9IDA7ICAgICAgICAgICAgICAgLy8geCBpcyB0aGUgZmlyc3QgYnVmZmVyIGVsZW1lbnRcbiAgY29uc3QgdXZPZmZzZXQgPSAzICogNDsgICAgICAgICAgICAvLyB1diBjb21lcyBhZnRlciBbeCB5IHpdXG4gIGNvbnN0IHN0cmlkZSA9ICgzICogNCkgKyAoMiAqIDQpOyAgLy8geHl6ICsgdXYsIGVhY2ggZW50cnkgaXMgNC1ieXRlIGZsb2F0LlxuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgdmVydGV4QnVmZmVyKSk7XG4gIHdlYmdsX3V0aWwuYmluZFZlcnRleEJ1ZmZlclRvUHJvZ3JhbUF0dHJpYnV0ZShcbiAgICAgIGdsLCBwcm9ncmFtLCAnY2xpcFNwYWNlUG9zJywgdmVydGV4QnVmZmVyLCAzLCBzdHJpZGUsIHBvc09mZnNldCk7XG4gIHRyeSB7XG4gICAgd2ViZ2xfdXRpbC5iaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgICAgICBnbCwgcHJvZ3JhbSwgJ3V2JywgdmVydGV4QnVmZmVyLCAyLCBzdHJpZGUsIHV2T2Zmc2V0KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIFByb2dyYW1zIHdpdGggMXgxIG91dHB1dCB0ZXh0dXJlcyBkb24ndCB1c2UgdGhlIHV2IGF0dHJpYnV0ZS5cbiAgICAvLyBUaGlzIGNhbiBjYXVzZSB0aGUgc2hhZGVyIGxpbmtlciB0byBkZWFkLXN0cmlwIGl0LCBzbyB3ZSBzaG91bGRuJ3RcbiAgICAvLyBjb21wbGFpbiBvciBmYWlsIGlmIGl0J3Mgbm90IHByZXNlbnQuXG4gICAgaWYgKCFlLmhhc093blByb3BlcnR5KCduYW1lZFZlcnRleEF0dHJpYnV0ZU5vdEZvdW5kJykpIHtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRQaXhlbERhdGFUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLFxuICAgIHBpeGVsczogSW1hZ2VEYXRhfEhUTUxJbWFnZUVsZW1lbnR8SFRNTENhbnZhc0VsZW1lbnR8SFRNTFZpZGVvRWxlbWVudCkge1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIGNvbnN0IGludGVybmFsRm9ybWF0ID0gZ2V0VGV4dHVyZUludGVybmFsRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC50ZXhJbWFnZTJEKFxuICAgICAgICAgIGdsLlRFWFRVUkVfMkQsIDAsIGludGVybmFsRm9ybWF0LCBnbC5SR0JBLCBnbC5GTE9BVCwgcGl4ZWxzKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmZ1bmN0aW9uIHVwbG9hZERhdGFUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCB3aWR0aDogbnVtYmVyLFxuICAgIGhlaWdodDogbnVtYmVyLCBkYXRhOiBGbG9hdDMyQXJyYXksIG51bUNoYW5uZWxzOiBudW1iZXIpIHtcbiAgY29uc3QgdGV4dHVyZUZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcblxuICB3ZWJnbF91dGlsLnZhbGlkYXRlVGV4dHVyZVNpemUoZ2wsIHdpZHRoLCBoZWlnaHQpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgdGV4dHVyZSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudGV4U3ViSW1hZ2UyRChcbiAgICAgICAgICBnbC5URVhUVVJFXzJELCAwLCAwLCAwLCB3aWR0aCwgaGVpZ2h0LCB0ZXh0dXJlRm9ybWF0LCBnbC5GTE9BVCxcbiAgICAgICAgICBkYXRhKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCBtYXRyaXg6IEZsb2F0MzJBcnJheSwgbnVtQ2hhbm5lbHM6IG51bWJlcikge1xuICBjb25zdCBbdywgaF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPVxuICAgICAgbnVtQ2hhbm5lbHMgPT09IDEgPyB3ZWJnbF91dGlsLmdldENoYW5uZWxzUGVyVGV4dHVyZSgpIDogbnVtQ2hhbm5lbHM7XG4gIGNvbnN0IHVucGFja2VkQXJyYXkgPVxuICAgICAgbmV3IEZsb2F0MzJBcnJheSh0ZXhfdXRpbC5nZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgICAgICAgIG1hdHJpeC5sZW5ndGgsIGNoYW5uZWxzUGVyVGV4dHVyZSkpO1xuICB0ZXhfdXRpbC5lbmNvZGVNYXRyaXhUb1VucGFja2VkQXJyYXkoXG4gICAgICBtYXRyaXgsIHVucGFja2VkQXJyYXksIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG5cbiAgdXBsb2FkRGF0YVRvVGV4dHVyZShnbCwgdGV4dHVyZSwgdywgaCwgdW5wYWNrZWRBcnJheSwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLFxuICAgIGNvbHVtbnM6IG51bWJlciwgbWF0cml4OiBGbG9hdDMyQXJyYXkpIHtcbiAgY29uc3QgW3csIGhdID0gdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHBhY2tlZFJHQkEgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKSk7XG4gIHRleF91dGlsLmVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShtYXRyaXgsIHJvd3MsIGNvbHVtbnMsIHBhY2tlZFJHQkEpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHVwbG9hZERhdGFUb1RleHR1cmUoZ2wsIHRleHR1cmUsIHcsIGgsIHBhY2tlZFJHQkEsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRvd25sb2FkTWF0cml4RnJvbU91dHB1dFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBbdywgaF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPSA0O1xuICBjb25zdCB1bnBhY2tlZEFycmF5ID1cbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkodGV4X3V0aWwuZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICAgICAgICByb3dzICogY29sdW1ucywgY2hhbm5lbHNQZXJUZXh0dXJlKSk7XG4gIGNvbnN0IHRleHR1cmVGb3JtYXQgPSBnZXRUZXh0dXJlRm9ybWF0KGdsLCBjaGFubmVsc1BlclRleHR1cmUpO1xuXG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnJlYWRQaXhlbHMoMCwgMCwgdywgaCwgZ2wuUkdCQSwgZ2wuRkxPQVQsIHVucGFja2VkQXJyYXkpKTtcblxuICBjb25zdCBtYXRyaXggPSBuZXcgRmxvYXQzMkFycmF5KHJvd3MgKiBjb2x1bW5zKTtcbiAgdGV4X3V0aWwuZGVjb2RlTWF0cml4RnJvbVVucGFja2VkQXJyYXkoXG4gICAgICB1bnBhY2tlZEFycmF5LCBtYXRyaXgsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIHJldHVybiBtYXRyaXg7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkb3dubG9hZE1hdHJpeEZyb21QYWNrZWRPdXRwdXRUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgW3csIGhdID0gdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHBhY2tlZFJHQkEgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnJlYWRQaXhlbHMoMCwgMCwgdywgaCwgZ2wuUkdCQSwgZ2wuRkxPQVQsIHBhY2tlZFJHQkEpKTtcbiAgY29uc3QgbWF0cml4ID0gbmV3IEZsb2F0MzJBcnJheShyb3dzICogY29sdW1ucyk7XG4gIHJldHVybiB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tUGFja2VkUkdCQShwYWNrZWRSR0JBLCByb3dzLCBjb2x1bW5zLCBtYXRyaXgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRMb2dVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNChsb2codmFsdWUpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldExvZ1VuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2coXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbG9nUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIGxvZ1Byb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRMb2dEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRMb2dVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGFEaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhTWF4ID0gdGV4dHVyZTJEKG1hdHJpeEEsIGhhbGZDUiAvIGFEaW1DUikucjtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGFDdXIgPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgICAgYU1heCA9IG1heChhTWF4LCBhQ3VyKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmbG9hdCBleHBTdW0gPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGMsIHIpICsgaGFsZkNSKSAvIGFEaW1DUjtcbiAgICAgICAgICBmbG9hdCBhQ3VyID0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICAgIGV4cFN1bSArPSBleHAoYUN1ciAtIGFNYXgpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoYU1heCArIGxvZyhleHBTdW0pLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1N1bUV4cChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBsb2dTdW1FeHBQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKGxvZ1N1bUV4cFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZExvZ1N1bUV4cERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgcHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucykpO1xuICBjb25zdCBhVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHJlc3VsdFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICBsb2dTdW1FeHAoZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCAxLCAxKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdFswXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbEJhY2twcm9wKFxuICAgIGR5U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgIG9yaWdQYWQ6IG51bWJlcikge1xuICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IGR5U2hhcGVSQ0RbMl07XG4gIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gIGNvbnN0IFtkeVJvd3MsIGR5Q29scywgZGVwdGhdID0gZHlTaGFwZVJDRDtcblxuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGR5O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1heFBvcztcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiBkeVNoYXBlQ1IgPSB2ZWMyKCR7ZHlUZXhTaGFwZVJDWzFdfSwgJHtkeVRleFNoYXBlUkNbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgZHhUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEIChkeFRleFIsIGR4VGV4QykgdG8gM0QgKGR4UiwgZHhDLCBkKS5cbiAgICAgIGZsb2F0IGR4UiA9IGR4VGV4Q1IueTtcbiAgICAgIGZsb2F0IGR4QyA9IGZsb29yKGR4VGV4Q1IueCAvICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZCA9IG1vZChkeFRleENSLngsICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIGR5UkNDb3JuZXIgPSB2ZWMyKGR4UiwgZHhDKSAtIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IGR5UkNvcm5lciA9IGR5UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IGR5Q0Nvcm5lciA9IGR5UkNDb3JuZXIueTtcblxuICAgICAgLy8gQ29udm9sdmUgZHkoPywgPywgZCkgd2l0aCBwb3MgbWFzayg6LCA6LCBkKSB0byBnZXQgZHgoeVIsIGR4QywgZCkuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuXG4gICAgICAgIGZsb2F0IGR5UiA9IChkeVJDb3JuZXIgKyB3UikgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgIC8vIFRPRE8obnN0aG9yYXQpOiBTcGxpY2UgdGhpcyB3aXRoIGFub3RoZXIgdmVyc2lvbiB3aGVyZSB5b3UgY2FsbFxuICAgICAgICAvLyBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCgpLiBIZXJlIGFuZCBiZWxvdy5cbiAgICAgICAgaWYgKGR5UiA8IDAuMCB8fCBkeVIgPj0gJHtkeVJvd3N9LjAgfHwgZnJhY3QoZHlSKSA+IDAuMCkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgZmxvYXQgZHlUZXhSID0gZHlSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG5cbiAgICAgICAgICBmbG9hdCBkeUMgPSAoZHlDQ29ybmVyICsgd0MpIC8gJHtvcmlnU3RyaWRlfS4wO1xuICAgICAgICAgIGlmIChkeUMgPCAwLjAgfHwgZHlDID49ICR7ZHlDb2xzfS4wIHx8IGZyYWN0KGR5QykgPiAwLjApIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IGR5VGV4QyA9IGR5QyAqICR7ZGVwdGh9LjAgKyBkO1xuXG4gICAgICAgICAgLy8gUmVhZCBkeShkeVIsIGR5QywgZCkuXG4gICAgICAgICAgdmVjMiBkeVVWID0gKHZlYzIoZHlUZXhDLCBkeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgLy8gUmVhZCBtYXhQb3MoZHlSLCBkeUMsIGQpLlxuICAgICAgICAgIGZsb2F0IG1heFBvc1ZhbHVlID1cbiAgICAgICAgICAgICAgJHtmU2l6ZSAqIGZTaXplIC0gMX0uMCAtIHRleHR1cmUyRChtYXhQb3MsIGR5VVYpLnI7XG5cbiAgICAgICAgICAvLyBHZXQgdGhlIGN1cnJlbnQgdmFsdWUsIGNoZWNrIGl0IGFnYWluc3QgdGhlIHZhbHVlIGZyb20gdGhlXG4gICAgICAgICAgLy8gcG9zaXRpb24gbWF0cml4LlxuICAgICAgICAgIGZsb2F0IGN1clBvc1ZhbHVlID0gd1IgKiAke2ZTaXplfS4wICsgd0M7XG4gICAgICAgICAgZmxvYXQgbWFzayA9IGZsb2F0KG1heFBvc1ZhbHVlID09IGN1clBvc1ZhbHVlID8gMS4wIDogMC4wKTtcblxuICAgICAgICAgIGRvdFByb2QgKz0gZHlWYWx1ZSAqIG1hc2s7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXhQb29sQmFja3Byb3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBkeVRleDogV2ViR0xUZXh0dXJlLFxuICAgIG1heFBvc2l0aW9uc1RleDogV2ViR0xUZXh0dXJlLCByZXN1bHRUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlUkNbMF0sIHJlc3VsdFRleFNoYXBlUkNbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoZHlUZXgsICdkeScsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUobWF4UG9zaXRpb25zVGV4LCAnbWF4UG9zJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgcG9vbF9ncHUgZnJvbSAnLi9wb29sX2dwdSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xQb3NpdGlvbnNTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgdHJ1ZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgZmFsc2UpO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyLCBjb21wdXRlTWF4UG9zaXRpb25zOiBib29sZWFuKSB7XG4gIHJldHVybiBwb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlclBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21heCcsIGNvbXB1dGVNYXhQb3NpdGlvbnMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWF4UG9vbENvbW1vbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcG9vbF9ncHUucG9vbENvbW1vbihncGdwdSwgcHJvZ3JhbSwgeCwgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vcG9vbF9ncHUnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNaW5Qb29sU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdtaW4nLCBmYWxzZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtaW5Qb29sKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBjb21wT3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdmFyeWluZyB2ZWMyIG91dHB1dENvbHVtblJvdztcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4QSwgaGFsZkNSIC8gYURpbUNSKS5yO1xuICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgYURpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGFEaW1DUi54OyBjICs9IDEuMCkge1xuICAgICAgICAgIHZlYzIgY3IgPSB2ZWMyKGMsIHIpO1xuICAgICAgICAgIHZlYzIgdXYgPSAoY3IgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGNhbmRpZGF0ZSA9IHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICAgICAgICBpZiAoaXNOYU4oY2FuZGlkYXRlKSkge1xuICAgICAgICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChjYW5kaWRhdGUsIDAsIDAsIDApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YWx1ZSA9ICR7Y29tcE9wfSh2YWx1ZSwgY2FuZGlkYXRlKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNaW5GcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnbWluJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNYXhGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnbWF4Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtaW5NYXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWluTWF4UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCAxLCAxKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtaW5NYXhQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb259IGZyb20gJy4uL21hdGgnO1xuaW1wb3J0IHtBcnJheTJEfSBmcm9tICcuLi9uZGFycmF5JztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBzaGFkZXJfY29tcGlsZXIgZnJvbSAnLi9zaGFkZXJfY29tcGlsZXInO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXIoXG4gICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgb3V0OiBBcnJheTJELCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLFxuICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBzdHJpbmcge1xuICBjb25zdCBzaGFyZWREaW0gPVxuICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUiA/IGEuc2hhcGVbMV0gOiBhLnNoYXBlWzBdKTtcbiAgY29uc3QgYVNuaXBwZXQgPVxuICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyAnYVJvdywgaScgOiAnaSwgYVJvdyc7XG4gIGNvbnN0IGJTbmlwcGV0ID1cbiAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gJ2ksIGJDb2wnIDogJ2JDb2wsIGknO1xuXG4gIGNvbnN0IGlucHV0cyA9IFt7bmFtZTogJ21hdHJpeEEnLCBhcnJheTogYX0sIHtuYW1lOiAnbWF0cml4QicsIGFycmF5OiBifV07XG4gIGNvbnN0IHVzZXJDb2RlID0gYFxuICAgIGNvbnN0IGZsb2F0IHNoYXJlZERpbSA9ICR7c2hhcmVkRGltfS4wO1xuXG4gICAgZmxvYXQgZG90QVJvd0JDb2woZmxvYXQgYVJvdywgZmxvYXQgYkNvbCkge1xuICAgICAgZmxvYXQgcmVzdWx0ID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCBpID0gMC4wOyBpIDwgc2hhcmVkRGltOyBpICs9IDEuMCkge1xuICAgICAgICBmbG9hdCBhID0gZ2V0TWF0cml4QSgke2FTbmlwcGV0fSk7XG4gICAgICAgIGZsb2F0IGIgPSBnZXRNYXRyaXhCKCR7YlNuaXBwZXR9KTtcbiAgICAgICAgcmVzdWx0ICs9IChhICogYik7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgcmVzUkMgPSBnZXRPdXRwdXRDb29yZHMoKTtcbiAgICAgIHNldE91dHB1dChkb3RBUm93QkNvbChyZXNSQy54LCByZXNSQy55KSk7XG4gICAgfVxuICBgO1xuICByZXR1cm4gc2hhZGVyX2NvbXBpbGVyLm1ha2VTaGFkZXIoaW5wdXRzLCBvdXQsIHVzZXJDb2RlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG11bHRpcGx5TWF0cml4KFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIG11bHRpcGx5UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgb3V0VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIG91dFRleFNoYXBlWzBdLCBvdXRUZXhTaGFwZVsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obXVsdGlwbHlQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXROZWdVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNCgtdmFsdWUsIDAsIDAsIDApOyc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0TmVnVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5lZyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLFxuICAgIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgcHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE5lZ0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldE5lZ1VuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIsIHBvb2xUeXBlOiAnbWF4J3wnbWluJ3wnYXZnJywgY29tcHV0ZVBvc2l0aW9uczogYm9vbGVhbikge1xuICBpZiAocG9vbFR5cGUgPT09ICdhdmcnICYmIGNvbXB1dGVQb3NpdGlvbnMpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBjb21wdXRlIHBvc2l0aW9ucyBmb3IgYXZlcmFnZSBwb29sLicpO1xuICB9XG5cbiAgY29uc3QgZGVwdGggPSB4U2hhcGVSQ0RbMl07XG5cbiAgY29uc3QgeFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHhTaGFwZVJDRCk7XG5cbiAgbGV0IHJldHVyblZhbHVlID0gJ21pbk1heFZhbHVlJztcbiAgaWYgKGNvbXB1dGVQb3NpdGlvbnMpIHtcbiAgICByZXR1cm5WYWx1ZSA9ICdtaW5NYXhQb3NpdGlvbic7XG4gIH0gZWxzZSBpZiAocG9vbFR5cGUgPT09ICdhdmcnKSB7XG4gICAgcmV0dXJuVmFsdWUgPSAnYXZnVmFsdWUnO1xuICB9XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICAke0lTX05BTl9TSEFERVJfRlVOQ31cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgZDIpLlxuICAgICAgZmxvYXQgeVIgPSB5VGV4Q1IueTtcbiAgICAgIGZsb2F0IHlDID0gZmxvb3IoeVRleENSLnggLyAke2RlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQgPSBtb2QoeVRleENSLngsICR7ZGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAqIHZlYzIoJHtzdHJpZGV9LCAke3N0cmlkZX0pIC1cbiAgICAgICAgICB2ZWMyKCR7cGFkfS4wLCAke3BhZH0uMCk7XG4gICAgICBmbG9hdCB4UkNvcm5lciA9IHhSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgeENDb3JuZXIgPSB4UkNDb3JuZXIueTtcblxuICAgICAgLy8gbWF4L21pbiB4KD8sID8sIGQpIHRvIGdldCB5KHlSLCB5QywgZCkuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZFxuICAgICAgZmxvYXQgbWluTWF4VmFsdWUgPSAwLjA7XG4gICAgICBmbG9hdCBtaW5NYXhWYWx1ZUZvdW5kID0gMC4wO1xuICAgICAgZmxvYXQgbWluTWF4UG9zaXRpb24gPSAwLjA7XG4gICAgICBmbG9hdCBhdmdWYWx1ZSA9IDAuMDtcblxuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeFIgPSB4UkNvcm5lciArIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG4gICAgICAgICAgZmxvYXQgeEMgPSB4Q0Nvcm5lciArIHdDO1xuICAgICAgICAgIGZsb2F0IHhUZXhDID0geEMgKiAke2RlcHRofS4wICsgZDtcblxuICAgICAgICAgIHZlYzIgdGV4Q1IgPSB2ZWMyKHhUZXhDLCB4VGV4Uik7XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiB0aGUgcmVxdWVzdGVkIFVWIGlzIGludmFsaWQuXG4gICAgICAgICAgdmVjMiB1diA9ICh0ZXhDUiArIGhhbGZDUikgLyB4U2hhcGVDUjtcbiAgICAgICAgICBib29sIGxlc3NUaGFuWmVybyA9IGFueShsZXNzVGhhbih1diwgdmVjMigwLCAwKSkpO1xuICAgICAgICAgIGJvb2wgZ3JlYXRlclRoYW5PbmUgPSBhbnkoZ3JlYXRlclRoYW4odXYsIHZlYzIoMSwgMSkpKTtcbiAgICAgICAgICBib29sIG91dHNpZGUgPSBsZXNzVGhhblplcm8gfHwgZ3JlYXRlclRoYW5PbmU7XG4gICAgICAgICAgaWYgKG91dHNpZGUpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKHgsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsdWUsIDAsIDAsIDApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJHtwb29sVHlwZSA9PT0gJ2F2Zyd9KSB7XG4gICAgICAgICAgICBhdmdWYWx1ZSArPSB2YWx1ZSAvICR7ZlNpemUgKiBmU2l6ZX0uMDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSWYgYSBtaW4gLyBtYXggdmFsdWUgaGFzIGFscmVhZHkgYmVlbiBmb3VuZCwgdXNlIGl0LiBJZiBub3QsIHVzZVxuICAgICAgICAgICAgLy8gdGhlIGN1cnJlbnQgdmFsdWUuXG4gICAgICAgICAgICBmbG9hdCBjdXJyZW50TWluTWF4VmFsdWUgPSBtaXgoXG4gICAgICAgICAgICAgICAgdmFsdWUsIG1pbk1heFZhbHVlLCBtaW5NYXhWYWx1ZUZvdW5kKTtcbiAgICAgICAgICAgIGlmICh2YWx1ZSAke3Bvb2xUeXBlID09PSAnbWluJyA/ICc8PScgOiAnPj0nfSBjdXJyZW50TWluTWF4VmFsdWUpIHtcbiAgICAgICAgICAgICAgbWluTWF4VmFsdWUgPSB2YWx1ZTtcbiAgICAgICAgICAgICAgbWluTWF4VmFsdWVGb3VuZCA9IDEuMDtcbiAgICAgICAgICAgICAgaWYgKCR7Y29tcHV0ZVBvc2l0aW9uc30pIHtcbiAgICAgICAgICAgICAgICBtaW5NYXhQb3NpdGlvbiA9IHdSICogJHtmU2l6ZX0uMCArIHdDO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCR7cmV0dXJuVmFsdWV9LCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBvb2xDb21tb24oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4LCAneCcsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGFEaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBzdW0gPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGMsIHIpICsgaGFsZkNSKSAvIGFEaW1DUjtcbiAgICAgICAgICBzdW0gKz0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHN1bSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZWR1Y2VTdW0oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVkdWNlU3VtUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYU51bVJvd3M6IG51bWJlciwgYU51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKHJlZHVjZVN1bVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFJlZHVjZVN1bURvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgcHJvZ3JhbTogV2ViR0xQcm9ncmFtID1cbiAgICAgIGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucykpO1xuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSgxLCAxKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgcmVkdWNlU3VtKGdwZ3B1LCBwcm9ncmFtLCBhVGV4dHVyZSwgcm93cywgY29sdW1ucywgcmVzdWx0VGV4dHVyZSk7XG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUocmVzdWx0VGV4dHVyZSwgMSwgMSlbMF07XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHJlc3VsdFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFJlbHVVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgcmVzdWx0ID0gKHZhbHVlIDwgMC4wID8gMC4wIDogdmFsdWUpO1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQocmVzdWx0LCAwLCAwLCAwKTtcbiAgYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRSZWx1VW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbHUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVsdVByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCByZWx1UHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFJlbHVEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRSZWx1VW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFJlbmRlclJHQlNoYWRlcihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBkZXN0aW5hdGlvbldpZHRoOiBudW1iZXIpOiBXZWJHTFByb2dyYW0ge1xuICBjb25zdCBmcmFnbWVudFNoYWRlclNvdXJjZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgc291cmNlO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IGZsb2F0IGRlc3RpbmF0aW9uV2lkdGggPSAke2Rlc3RpbmF0aW9uV2lkdGh9LjA7XG4gICAgY29uc3QgZmxvYXQgYSA9IDEuMDtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHhyID0gZmxvb3IocmVzdWx0VVYucyAqIGRlc3RpbmF0aW9uV2lkdGgpICogMy4wO1xuICAgICAgdmVjMyB4ID0geHIgKyB2ZWMzKDAsIDEsIDIpO1xuXG4gICAgICBmbG9hdCBzb3VyY2VXaWR0aCA9IGRlc3RpbmF0aW9uV2lkdGggKiAzLjA7XG4gICAgICB2ZWMzIHUgPSAoeCArIDAuNSkgLyBzb3VyY2VXaWR0aDtcbiAgICAgIGZsb2F0IHYgPSAxLjAgLSByZXN1bHRVVi50O1xuXG4gICAgICBmbG9hdCByID0gdGV4dHVyZTJEKHNvdXJjZSwgdmVjMih1WzBdLCB2KSkucjtcbiAgICAgIGZsb2F0IGcgPSB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHVbMV0sIHYpKS5yO1xuICAgICAgZmxvYXQgYiA9IHRleHR1cmUyRChzb3VyY2UsIHZlYzIodVsyXSwgdikpLnI7XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQociwgZywgYiwgYSk7XG4gICAgfWA7XG5cbiAgcmV0dXJuIGdwZ3B1LmNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTb3VyY2UpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9DYW52YXMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVuZGVyU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlKSB7XG4gIHdlYmdsX3V0aWwuYmluZENhbnZhc1RvRnJhbWVidWZmZXIoZ3BncHUuZ2wpO1xuICByZW5kZXJUb0ZyYW1lYnVmZmVyKGdwZ3B1LCByZW5kZXJTaGFkZXIsIHNvdXJjZVRleCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb0ZyYW1lYnVmZmVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlbmRlclNoYWRlcjogV2ViR0xQcm9ncmFtLCBzb3VyY2VUZXg6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRQcm9ncmFtKHJlbmRlclNoYWRlcik7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShzb3VyY2VUZXgsICdzb3VyY2UnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHZlYzIgaW5wdXREaW1DUjtcbiAgICB1bmlmb3JtIHZlYzIgcmVzdWx0RGltQ1I7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHJlc3VsdENSID0gZmxvb3IocmVzdWx0VVYgKiByZXN1bHREaW1DUik7XG4gICAgICAvLyBpbmRleEluRmxhdCA9IHJvdyAqIHN0cmlkZSArIGNvbHVtbiwgd2hlcmUgc3RyaWRlID09IG51bU91dHB1dENvbHVtbnNcbiAgICAgIGZsb2F0IGluZGV4SW5GbGF0ID0gcmVzdWx0Q1IueSAqIHJlc3VsdERpbUNSLnggKyByZXN1bHRDUi54O1xuXG4gICAgICB2ZWMyIGlucHV0Q1IgPSB2ZWMyKFxuICAgICAgICBtb2QoaW5kZXhJbkZsYXQsIGlucHV0RGltQ1IueCksIC8vIGNvbCA9IGluZGV4SW5GbGF0ICUgbnVtSW5wdXRDb2x1bW5zXG4gICAgICAgIGZsb29yKGluZGV4SW5GbGF0IC8gaW5wdXREaW1DUi54KSAvLyByb3cgPSBpbmRleEluRmxhdCAvIG51bUlucHV0Q29sdW1uc1xuICAgICAgKSArIGhhbGZDUjtcblxuICAgICAgdmVjMiBpbnB1dFVWID0gaW5wdXRDUiAvIGlucHV0RGltQ1I7XG4gICAgICBnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQobWF0cml4QSwgaW5wdXRVVik7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXNoYXBlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlc2hhcGVQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHROdW1Sb3dzOiBudW1iZXIsIHJlc3VsdE51bUNvbHM6IG51bWJlcikge1xuICBjb25zdCBpbnB1dFNpemUgPSBhTnVtUm93cyAqIGFOdW1Db2xzO1xuICBjb25zdCBvdXRwdXRTaXplID0gcmVzdWx0TnVtQ29scyAqIHJlc3VsdE51bVJvd3M7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgaW5wdXRTaXplID09PSBvdXRwdXRTaXplLFxuICAgICAgYFRoZSBpbnB1dCBzaXplICgke2lucHV0U2l6ZX0pIGFuZCBvdXRwdXQgc2l6ZSAoJHtvdXRwdXRTaXplfSkgYCArXG4gICAgICAgICAgYG11c3QgbWF0Y2hgKTtcblxuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcmVzdWx0TnVtUm93cywgcmVzdWx0TnVtQ29scyk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVzaGFwZVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcblxuICBjb25zdCBpbnB1dERpbUNSTG9jYXRpb24gPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2lucHV0RGltQ1InKTtcbiAgZ3BncHUuZ2wudW5pZm9ybTJmKGlucHV0RGltQ1JMb2NhdGlvbiwgYU51bUNvbHMsIGFOdW1Sb3dzKTtcblxuICBjb25zdCByZXN1bHREaW1DUkxvY2F0aW9uID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdyZXN1bHREaW1DUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYocmVzdWx0RGltQ1JMb2NhdGlvbiwgcmVzdWx0TnVtQ29scywgcmVzdWx0TnVtUm93cyk7XG5cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgaW5wdXRTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLFxuICAgIG91dHB1dERpbWVuc2lvbnNSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIGFsaWduQ29ybmVyczogYm9vbGVhbik6IHN0cmluZyB7XG4gIGNvbnN0IGRlcHRoID0gaW5wdXRTaGFwZVJDRFsyXTtcblxuICBjb25zdCBpbnB1dFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGlucHV0U2hhcGVSQ0QpO1xuXG4gIGNvbnN0IGVmZmVjdGl2ZUlucHV0U2hhcGVSQ0QgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgW2lucHV0U2hhcGVSQ0RbMF0gLSAxLCBpbnB1dFNoYXBlUkNEWzFdIC0gMSwgZGVwdGhdIDpcbiAgICAgIGlucHV0U2hhcGVSQ0Q7XG5cbiAgY29uc3QgZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0QgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgW291dHB1dERpbWVuc2lvbnNSb3dDb2xbMF0gLSAxLCBvdXRwdXREaW1lbnNpb25zUm93Q29sWzFdIC0gMSwgZGVwdGhdIDpcbiAgICAgIFtvdXRwdXREaW1lbnNpb25zUm93Q29sWzBdLCBvdXRwdXREaW1lbnNpb25zUm93Q29sWzFdLCBkZXB0aF07XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIGNvbnN0IHZlYzIgaW5wdXRTaGFwZUNSID0gdmVjMigke2lucHV0U2hhcGVSQ0RbMV19LCAke2lucHV0U2hhcGVSQ0RbMF19KTtcbiAgICBjb25zdCB2ZWMyIGlucHV0U2hhcGVUZXhDUiA9IHZlYzIoXG4gICAgICAgICR7aW5wdXRUZXhTaGFwZVJDWzFdfSwgJHtpbnB1dFRleFNoYXBlUkNbMF19KTtcblxuICAgIGNvbnN0IHZlYzIgZWZmZWN0aXZlSW5wdXRPdmVyT3V0cHV0UmF0aW9DUiA9IHZlYzIoXG4gICAgICAgICR7ZWZmZWN0aXZlSW5wdXRTaGFwZVJDRFsxXSAvIGVmZmVjdGl2ZU91dHB1dFNoYXBlUkNEWzFdfSxcbiAgICAgICAgJHtlZmZlY3RpdmVJbnB1dFNoYXBlUkNEWzBdIC8gZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0RbMF19KTtcblxuICAgIGZsb2F0IHNhbXBsZUlucHV0KGZsb2F0IGNvbCwgZmxvYXQgcm93LCBmbG9hdCBkKSB7XG4gICAgICB2ZWMyIHV2ID0gKHZlYzIoY29sICogJHtkZXB0aH0uMCArIGQsIHJvdykgKyBoYWxmQ1IpIC8gaW5wdXRTaGFwZVRleENSO1xuICAgICAgcmV0dXJuIHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICB9XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQpLlxuICAgICAgdmVjMiB5Q1IgPSB2ZWMyKGZsb29yKHlUZXhDUi54IC8gJHtkZXB0aH0uMCksIHlUZXhDUi55KTtcbiAgICAgIGZsb2F0IGQgPSBtb2QoeVRleENSLngsICR7ZGVwdGh9LjApO1xuXG4gICAgICAvLyBGcmFjdGlvbmFsIHNvdXJjZSBpbmRleC5cbiAgICAgIHZlYzIgc291cmNlRnJhY0luZGV4Q1IgPSB5Q1IgKiBlZmZlY3RpdmVJbnB1dE92ZXJPdXRwdXRSYXRpb0NSO1xuXG4gICAgICAvLyBDb21wdXRlIHRoZSBmb3VyIGludGVnZXIgaW5kaWNlcy5cbiAgICAgIHZlYzIgc291cmNlRmxvb3JDUiA9IGZsb29yKHNvdXJjZUZyYWNJbmRleENSKTtcbiAgICAgIHZlYzIgc291cmNlQ2VpbENSID0gbWluKGlucHV0U2hhcGVDUiAtIDEuMCwgY2VpbChzb3VyY2VGcmFjSW5kZXhDUikpO1xuXG4gICAgICBmbG9hdCB0b3BMZWZ0ID0gc2FtcGxlSW5wdXQoc291cmNlRmxvb3JDUlswXSwgc291cmNlRmxvb3JDUlsxXSwgZCk7XG4gICAgICBmbG9hdCBib3R0b21MZWZ0ID0gc2FtcGxlSW5wdXQoc291cmNlRmxvb3JDUlswXSwgc291cmNlQ2VpbENSWzFdLCBkKTtcbiAgICAgIGZsb2F0IHRvcFJpZ2h0ID0gc2FtcGxlSW5wdXQoc291cmNlQ2VpbENSWzBdLCBzb3VyY2VGbG9vckNSWzFdLCBkKTtcbiAgICAgIGZsb2F0IGJvdHRvbVJpZ2h0ID0gc2FtcGxlSW5wdXQoc291cmNlQ2VpbENSWzBdLCBzb3VyY2VDZWlsQ1JbMV0sIGQpO1xuXG4gICAgICB2ZWMyIGZyYWNDUiA9IHNvdXJjZUZyYWNJbmRleENSIC0gc291cmNlRmxvb3JDUjtcblxuICAgICAgZmxvYXQgdG9wID0gdG9wTGVmdCArICh0b3BSaWdodCAtIHRvcExlZnQpICogZnJhY0NSWzBdO1xuICAgICAgZmxvYXQgYm90dG9tID0gYm90dG9tTGVmdCArIChib3R0b21SaWdodCAtIGJvdHRvbUxlZnQpICogZnJhY0NSWzBdO1xuICAgICAgZmxvYXQgbmV3VmFsdWUgPSB0b3AgKyAoYm90dG9tIC0gdG9wKSAqIGZyYWNDUlsxXTtcblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChuZXdWYWx1ZSwgMC4wLCAwLjAsIDAuMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXNpemVCaWxpbmVhcihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZXNpemVCaWxpbmVhclByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHJlc2l6ZUJpbGluZWFyUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbmRhcnJheSc7XG5cbmV4cG9ydCB0eXBlIElucHV0ID0ge1xuICBuYW1lOiBzdHJpbmc7IGFycmF5OiBOREFycmF5O1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXJLZXkoaW5wdXRzOiBOREFycmF5W10sIG91dHB1dDogTkRBcnJheSk6IHN0cmluZyB7XG4gIGNvbnN0IGlucyA9IGlucHV0cy5tYXAoeCA9PiB4LnNoYXBlICsgJ18nICsgeC5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgcmV0dXJuIGlucy5qb2luKCdfJykgKyAnXycgKyBvdXRwdXQuc2hhcGUgKyAnXycgKyBvdXRwdXQuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXIoXG4gICAgaW5wdXRzOiBJbnB1dFtdLCBvdXRwdXQ6IE5EQXJyYXksIHVzZXJDb2RlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBpbnB1dFByZWZpeFNuaXBwZXQgPVxuICAgICAgaW5wdXRzLm1hcCh4ID0+IGB1bmlmb3JtIHNhbXBsZXIyRCAke3gubmFtZX07YCkuam9pbignXFxuJyk7XG4gIGNvbnN0IGlucHV0U2FtcGxpbmdTbmlwcGV0ID1cbiAgICAgIGlucHV0cy5tYXAoeCA9PiBnZXRJbnB1dFNhbXBsaW5nU25pcHBldCh4KSkuam9pbignXFxuJyk7XG4gIGNvbnN0IG91dFRleFNoYXBlID0gb3V0cHV0LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gIGNvbnN0IG91dHB1dFNhbXBsaW5nU25pcHBldCA9XG4gICAgICBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQob3V0cHV0LnNoYXBlLCBvdXRUZXhTaGFwZSk7XG4gIGNvbnN0IHNvdXJjZSA9IFtcbiAgICBTSEFERVJfUFJFRklYLCBpbnB1dFByZWZpeFNuaXBwZXQsIFNBTVBMRV8yRF9TTklQUEVULCBpbnB1dFNhbXBsaW5nU25pcHBldCxcbiAgICBvdXRwdXRTYW1wbGluZ1NuaXBwZXQsIHVzZXJDb2RlXG4gIF0uam9pbignXFxuJyk7XG4gIHJldHVybiBzb3VyY2U7XG59XG5cbmZ1bmN0aW9uIGdldElucHV0U2FtcGxpbmdTbmlwcGV0KGlucHV0OiBJbnB1dCkge1xuICBjb25zdCBhcnIgPSBpbnB1dC5hcnJheTtcbiAgY29uc3Qgc2hhcGUgPSBhcnIuc2hhcGU7XG4gIGNvbnN0IHRleFNoYXBlID0gYXJyLmdldFRleHR1cmVTaGFwZVJDKHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0pO1xuICBzd2l0Y2ggKHNoYXBlLmxlbmd0aCkge1xuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiBnZXRTYW1wbGVyMkQoaW5wdXQubmFtZSwgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGUpO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYCR7YXJyLnJhbmt9LUQgaW5wdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQoXG4gICAgb3V0U2hhcGU6IG51bWJlcltdLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIHN3aXRjaCAob3V0U2hhcGUubGVuZ3RoKSB7XG4gICAgY2FzZSAyOlxuICAgICAgcmV0dXJuIGdldE91dHB1dDJEQ29vcmRzKG91dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIG91dFRleFNoYXBlKTtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGAke291dFNoYXBlLmxlbmd0aH0tRCBvdXRwdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5jb25zdCBTSEFERVJfUFJFRklYID0gYFxuICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcbiAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICB2b2lkIHNldE91dHB1dChmbG9hdCB2YWwpIHtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbCwgMCwgMCwgMCk7XG4gIH1cbmA7XG5cbmNvbnN0IFNBTVBMRV8yRF9TTklQUEVUID0gYFxuICBmbG9hdCBzYW1wbGUyRChzYW1wbGVyMkQgdGV4dHVyZSwgZmxvYXQgdGV4TnVtUiwgZmxvYXQgdGV4TnVtQywgZmxvYXQgbnVtQyxcbiAgICAgIGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgZmxvYXQgaW5kZXggPSBkb3QodmVjMihyb3csIGNvbCksIHZlYzIobnVtQywgMS4wKSk7XG4gICAgZmxvYXQgdGV4UiA9IGZsb29yKGluZGV4IC8gdGV4TnVtQyk7XG4gICAgZmxvYXQgdGV4QyA9IG1vZChpbmRleCwgdGV4TnVtQyk7XG4gICAgdmVjMiB1diA9ICh2ZWMyKHRleEMsIHRleFIpICsgaGFsZkNSKSAvIHZlYzIodGV4TnVtQywgdGV4TnVtUik7XG4gICAgcmV0dXJuIHRleHR1cmUyRCh0ZXh0dXJlLCB1dikucjtcbiAgfVxuYDtcblxuZnVuY3Rpb24gZ2V0T3V0cHV0MkRDb29yZHMoXG4gICAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIHRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGlmICh1dGlsLmFycmF5c0VxdWFsKHNoYXBlLCB0ZXhTaGFwZSkpIHtcbiAgICByZXR1cm4gYFxuICAgICAgdmVjMiBnZXRPdXRwdXRDb29yZHMoKSB7XG4gICAgICAgIHJldHVybiBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgfVxuICAgIGA7XG4gIH1cbiAgcmV0dXJuIGBcbiAgICB2ZWMyIGdldE91dHB1dENvb3JkcygpIHtcbiAgICAgIHZlYzIgcmVzVGV4UkMgPSBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgZmxvYXQgaW5kZXggPSBkb3QocmVzVGV4UkMsIHZlYzIoJHt0ZXhTaGFwZVsxXX0uMCwgMS4wKSk7XG4gICAgICBmbG9hdCByID0gZmxvb3IoaW5kZXggLyAke3NoYXBlWzFdfS4wKTtcbiAgICAgIGZsb2F0IGMgPSBtb2QoaW5kZXgsICR7c2hhcGVbMV19LjApO1xuICAgICAgcmV0dXJuIHZlYzIociwgYyk7XG4gICAgfVxuICBgO1xufVxuXG5mdW5jdGlvbiBnZXRTYW1wbGVyMkQoXG4gICAgdGV4TmFtZTogc3RyaW5nLCBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgY29uc3QgZnVuY05hbWUgPSAnZ2V0JyArIHRleE5hbWUuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyB0ZXhOYW1lLnNsaWNlKDEpO1xuICBjb25zdCB0UiA9IHRleFNoYXBlWzBdO1xuICBjb25zdCB0QyA9IHRleFNoYXBlWzFdO1xuICBpZiAodXRpbC5hcnJheXNFcXVhbChzaGFwZSwgdGV4U2hhcGUpKSB7XG4gICAgcmV0dXJuIGBcbiAgICAgIGZsb2F0ICR7ZnVuY05hbWV9KGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgICAgIHZlYzIgdXYgPSAodmVjMihjb2wsIHJvdykgKyBoYWxmQ1IpIC8gdmVjMigke3RDfS4wLCAke3RSfS4wKTtcbiAgICAgICAgcmV0dXJuIHRleHR1cmUyRCgke3RleE5hbWV9LCB1dikucjtcbiAgICAgIH1cbiAgICBgO1xuICB9XG4gIHJldHVybiBgXG4gICAgZmxvYXQgJHtmdW5jTmFtZX0oZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICAgIHJldHVybiBzYW1wbGUyRCgke3RleE5hbWV9LCAke3RSfS4wLCAke3RDfS4wLCAke3NoYXBlWzFdfS4wLCByb3csIGNvbCk7XG4gICAgfVxuICBgO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRTaWdtb2lkVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gJ2dsX0ZyYWdDb2xvciA9IHZlYzQoMS4wIC8gKDEuMCArIGV4cCgtMS4wICogdmFsdWUpKSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNpZ21vaWRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U2lnbW9pZFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaWdtb2lkKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHNpZ21vaWRQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc2lnbW9pZFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTaWdtb2lkRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoXG4gICAgICBhLCByb3dzLCBjb2x1bW5zLCBnZXRTaWdtb2lkVW5hcnlPcCgpKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFN0ZXBVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgcmVzID0gdmFsdWUgPT0gdmFsdWUgPyAodmFsdWUgPiAwLjAgPyAxLjAgOiAwLjApIDogdmFsdWU7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyZXMsIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFN0ZXBVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc3RlcChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBzdGVwUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHN0ZXBQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkU3RlcERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFN0ZXBVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2NvbHVtbnMsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICBtYXRyaXhTaXplOiBudW1iZXIsIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIG1hdHJpeFNpemUgKiBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb2xvck1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbY29sdW1ucyAqIDQsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWF0cml4U2l6ZUZyb21VbnBhY2tlZEFycmF5U2l6ZShcbiAgICB1bnBhY2tlZFNpemU6IG51bWJlciwgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAodW5wYWNrZWRTaXplICUgY2hhbm5lbHNQZXJUZXh0dXJlICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAndW5wYWNrZWRTaXplICgnICsgdW5wYWNrZWRTaXplICsgJykgbXVzdCBiZSBhIG11bHRpcGxlIG9mICcgK1xuICAgICAgICBjaGFubmVsc1BlclRleHR1cmUpO1xuICB9XG4gIHJldHVybiB1bnBhY2tlZFNpemUgLyBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBlbmNvZGVNYXRyaXhUb1VucGFja2VkQXJyYXkoXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXksIHVucGFja2VkQXJyYXk6IEZsb2F0MzJBcnJheSxcbiAgICBjaGFubmVsc1BlclRleHR1cmU6IG51bWJlcikge1xuICBjb25zdCByZXF1aXJlZFNpemUgPVxuICAgICAgZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBpZiAodW5wYWNrZWRBcnJheS5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd1bnBhY2tlZEFycmF5IGxlbmd0aCAoJyArIHVucGFja2VkQXJyYXkubGVuZ3RoICtcbiAgICAgICAgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgc3JjID0gMDsgc3JjIDwgbWF0cml4Lmxlbmd0aDsgKytzcmMpIHtcbiAgICB1bnBhY2tlZEFycmF5W2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICBkc3QgKz0gY2hhbm5lbHNQZXJUZXh0dXJlO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICB1bnBhY2tlZEFycmF5OiBGbG9hdDMyQXJyYXksIG1hdHJpeDogRmxvYXQzMkFycmF5LFxuICAgIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldE1hdHJpeFNpemVGcm9tVW5wYWNrZWRBcnJheVNpemUoXG4gICAgICB1bnBhY2tlZEFycmF5Lmxlbmd0aCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcbiAgaWYgKG1hdHJpeC5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgbGV0IGRzdCA9IDA7XG4gIGZvciAobGV0IHNyYyA9IDA7IHNyYyA8IHVucGFja2VkQXJyYXkubGVuZ3RoOyBzcmMgKz0gY2hhbm5lbHNQZXJUZXh0dXJlKSB7XG4gICAgbWF0cml4W2RzdCsrXSA9IHVucGFja2VkQXJyYXlbc3JjXTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtNYXRoLmNlaWwoY29sdW1ucyAvIDIpLCBNYXRoLmNlaWwocm93cyAvIDIpXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBbdywgaF0gPSBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgcmV0dXJuIHcgKiBoICogNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgcGFja2VkUkdCQTogRmxvYXQzMkFycmF5KSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUocm93cywgY29sdW1ucyk7XG4gIGlmIChwYWNrZWRSR0JBLmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ3BhY2tlZFJHQkEgbGVuZ3RoICgnICsgcGFja2VkUkdCQS5sZW5ndGggK1xuICAgICAgICAnKSBtdXN0IGJlID49ICcgKyByZXF1aXJlZFNpemUpO1xuICB9XG4gIC8qXG4gICAgVW5wYWNrZWQgbWF0cml4LCByb3ctbWFqb3Igb3JkZXIgaW4gRmxvYXQzMkFycmF5WzE2XTogIEEgQiBDIERcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRSBGIEcgSFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJIEogSyBMXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE0gTiBPIFBcblxuICAgIFBhY2tlZCBtYXRyaXgsIDJ4MiBSR0JBMzIgdGV4dHVyZSAobWVtb3J5IHZpZXcpOiAgICAgICBBQkVGIENER0ggSUpNTiBLTE9QXG5cbiAgICBQYWNrZWQgbWF0cml4LCAyeDIgUkdCQTMyIHRleHR1cmUgKG1hdHJpeCB2aWV3KTogICAgICAgQUJ8Q0RcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRUZ8R0hcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0rLS1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSUp8S0xcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTU58T1BcbiAgICovXG4gIGNvbnN0IFt0ZXh0dXJlV2lkdGgsIHRleHR1cmVIZWlnaHRdID1cbiAgICAgIGdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBvZGRXaWR0aCA9IChjb2x1bW5zICUgMikgPT09IDE7XG4gIGNvbnN0IG9kZEhlaWdodCA9IChyb3dzICUgMikgPT09IDE7XG4gIGNvbnN0IHdpZHRoSW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihjb2x1bW5zIC8gMik7XG4gIGNvbnN0IGhlaWdodEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3Iocm93cyAvIDIpO1xuXG4gIC8vIGxvb3Agb3ZlciBmdWxsIDJ4MiBibG9ja3NcbiAge1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IChvZGRXaWR0aCA/IDQgOiAwKTtcbiAgICBjb25zdCBvbmVSb3cgPSBjb2x1bW5zO1xuICAgIGxldCBkc3QgPSAwO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIGNvbnN0IG1hdHJpeFNyY1JvdyA9IChibG9ja1kgKiAyICogY29sdW1ucyk7XG4gICAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgICAgY29uc3QgbWF0cml4U3JjQ29sID0gYmxvY2tYICogMjtcbiAgICAgICAgY29uc3Qgc3JjID0gbWF0cml4U3JjUm93ICsgbWF0cml4U3JjQ29sO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAxXSA9IG1hdHJpeFtzcmMgKyAxXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAyXSA9IG1hdHJpeFtzcmMgKyBvbmVSb3ddO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDNdID0gbWF0cml4W3NyYyArIG9uZVJvdyArIDFdO1xuICAgICAgICBkc3QgKz0gNDtcbiAgICAgIH1cbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBkb3duIGZpbmFsIG9kZCBjb2x1bW5cbiAgaWYgKG9kZFdpZHRoKSB7XG4gICAgbGV0IHNyYyA9IGNvbHVtbnMgLSAxO1xuICAgIGxldCBkc3QgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGNvbnN0IHNyY1N0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgcGFja2VkUkdCQVtkc3RdID0gbWF0cml4W3NyY107XG4gICAgICBwYWNrZWRSR0JBW2RzdCArIDJdID0gbWF0cml4W3NyYyArIGNvbHVtbnNdO1xuICAgICAgc3JjICs9IHNyY1N0cmlkZTtcbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBhY3Jvc3MgZmluYWwgcm93XG4gIGlmIChvZGRIZWlnaHQpIHtcbiAgICBsZXQgc3JjID0gKHJvd3MgLSAxKSAqIGNvbHVtbnM7XG4gICAgbGV0IGRzdCA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgZHN0ICs9IDI7XG4gICAgfVxuICB9XG5cbiAgLy8gZmlsbCBpbiBib3R0b20tcmlnaHQgdGV4ZWxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIHBhY2tlZFJHQkFbcGFja2VkUkdCQS5sZW5ndGggLSA0XSA9IG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV07XG4gIH1cblxuICByZXR1cm4gcGFja2VkUkdCQTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU1hdHJpeEZyb21QYWNrZWRSR0JBKFxuICAgIHBhY2tlZFJHQkE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCByZXF1aXJlZFNpemUgPSByb3dzICogY29sdW1ucztcbiAgaWYgKHJlcXVpcmVkU2l6ZSA8IG1hdHJpeC5sZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgY29uc3Qgb2RkV2lkdGggPSAoY29sdW1ucyAlIDIpID09PSAxO1xuICBjb25zdCBvZGRIZWlnaHQgPSAocm93cyAlIDIpID09PSAxO1xuICBjb25zdCB3aWR0aEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3IoY29sdW1ucyAvIDIpO1xuICBjb25zdCBoZWlnaHRJbkZ1bGxCbG9ja3MgPSBNYXRoLmZsb29yKHJvd3MgLyAyKTtcbiAgY29uc3QgW3RleHR1cmVXaWR0aCwgdGV4dHVyZUhlaWdodF0gPVxuICAgICAgZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG5cbiAgLy8gbG9vcCBvdmVyIGZ1bGwgMngyIGJsb2Nrc1xuICB7XG4gICAgY29uc3Qgc3JjU3RyaWRlID0gb2RkV2lkdGggPyA0IDogMDtcbiAgICBjb25zdCBkc3RTdHJpZGUgPSBjb2x1bW5zICsgKG9kZFdpZHRoID8gMSA6IDApO1xuICAgIGxldCBzcmMgPSAwO1xuICAgIGxldCBkc3RSb3cxID0gMDtcbiAgICBsZXQgZHN0Um93MiA9IGNvbHVtbnM7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgZm9yIChsZXQgYmxvY2tYID0gMDsgYmxvY2tYIDwgd2lkdGhJbkZ1bGxCbG9ja3M7ICsrYmxvY2tYKSB7XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICB9XG4gICAgICBzcmMgKz0gc3JjU3RyaWRlO1xuICAgICAgZHN0Um93MSArPSBkc3RTdHJpZGU7XG4gICAgICBkc3RSb3cyICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGRvd24gZmluYWwgY29sdW1uXG4gIGlmIChvZGRXaWR0aCkge1xuICAgIGxldCBzcmMgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGxldCBkc3QgPSBjb2x1bW5zIC0gMTtcbiAgICBjb25zdCBzcmNTdHJpZGUgPSB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIG1hdHJpeFtkc3RdID0gcGFja2VkUkdCQVtzcmNdO1xuICAgICAgbWF0cml4W2RzdCArIGNvbHVtbnNdID0gcGFja2VkUkdCQVtzcmMgKyAyXTtcbiAgICAgIHNyYyArPSBzcmNTdHJpZGU7XG4gICAgICBkc3QgKz0gZHN0U3RyaWRlO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvb3AgYWNyb3NzIGZpbmFsIHJvd1xuICBpZiAob2RkSGVpZ2h0KSB7XG4gICAgbGV0IHNyYyA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGxldCBkc3QgPSAocm93cyAtIDEpICogY29sdW1ucztcbiAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIHNyYyArPSAyO1xuICAgIH1cbiAgfVxuXG4gIC8vIGZpbGwgaW4gYm90dG9tLXJpZ2h0IGNlbGxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV0gPSBwYWNrZWRSR0JBW3BhY2tlZFJHQkEubGVuZ3RoIC0gNF07XG4gIH1cblxuICByZXR1cm4gbWF0cml4O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGNsYXNzIFRleHR1cmVNYW5hZ2VyIHtcbiAgcHJpdmF0ZSBudW1Vc2VkVGV4dHVyZXMgPSAwO1xuICBwcml2YXRlIG51bUZyZWVUZXh0dXJlcyA9IDA7XG4gIHByaXZhdGUgZnJlZVRleHR1cmVzOiB7W3NoYXBlOiBzdHJpbmddOiBXZWJHTFRleHR1cmVbXX0gPSB7fTtcbiAgcHJpdmF0ZSBsb2dFbmFibGVkID0gZmFsc2U7XG4gIHByaXZhdGUgdXNlZFRleHR1cmVDb3VudDoge1tzaGFwZTogc3RyaW5nXTogbnVtYmVyfSA9IHt9O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZ3BncHU6IEdQR1BVQ29udGV4dCkge31cblxuICBhY3F1aXJlVGV4dHVyZShzaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKTogV2ViR0xUZXh0dXJlIHtcbiAgICBjb25zdCBzaGFwZUtleSA9IGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGVSQyk7XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy5mcmVlVGV4dHVyZXMpKSB7XG4gICAgICB0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0gPSBbXTtcbiAgICB9XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy51c2VkVGV4dHVyZUNvdW50KSkge1xuICAgICAgdGhpcy51c2VkVGV4dHVyZUNvdW50W3NoYXBlS2V5XSA9IDA7XG4gICAgfVxuICAgIHRoaXMudXNlZFRleHR1cmVDb3VudFtzaGFwZUtleV0rKztcblxuICAgIGlmICh0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0ubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5udW1GcmVlVGV4dHVyZXMtLTtcbiAgICAgIHRoaXMubnVtVXNlZFRleHR1cmVzKys7XG4gICAgICB0aGlzLmxvZygpO1xuICAgICAgcmV0dXJuIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5zaGlmdCgpITtcbiAgICB9XG4gICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMrKztcbiAgICB0aGlzLmxvZygpO1xuXG4gICAgcmV0dXJuIHRoaXMuZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShzaGFwZVJDWzBdLCBzaGFwZVJDWzFdKTtcbiAgfVxuXG4gIHJlbGVhc2VUZXh0dXJlKHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb25zdCBzaGFwZUtleSA9IGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGUpO1xuICAgIGlmICghKHNoYXBlS2V5IGluIHRoaXMuZnJlZVRleHR1cmVzKSkge1xuICAgICAgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldID0gW107XG4gICAgfVxuICAgIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5wdXNoKHRleHR1cmUpO1xuICAgIHRoaXMubnVtRnJlZVRleHR1cmVzKys7XG4gICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMtLTtcbiAgICB0aGlzLnVzZWRUZXh0dXJlQ291bnRbc2hhcGVLZXldLS07XG4gICAgdGhpcy5sb2coKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9nKCkge1xuICAgIGlmICghdGhpcy5sb2dFbmFibGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHRvdGFsID0gdGhpcy5udW1GcmVlVGV4dHVyZXMgKyB0aGlzLm51bVVzZWRUZXh0dXJlcztcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgJ0ZyZWUvVXNlZCcsIHRoaXMubnVtRnJlZVRleHR1cmVzICsgJyAvICcgKyB0aGlzLm51bVVzZWRUZXh0dXJlcyxcbiAgICAgICAgYCgke3RvdGFsfSlgKTtcbiAgfVxuXG4gIGdldE51bVVzZWRUZXh0dXJlcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLm51bVVzZWRUZXh0dXJlcztcbiAgfVxuXG4gIGdldE51bUZyZWVUZXh0dXJlcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLm51bUZyZWVUZXh0dXJlcztcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgZm9yIChjb25zdCBzaGFwZSBpbiB0aGlzLmZyZWVUZXh0dXJlcykge1xuICAgICAgaWYgKHRoaXMuZnJlZVRleHR1cmVzLmhhc093blByb3BlcnR5KHNoYXBlKSkge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlXS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIHRoaXMuZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZSh0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZV1baV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGVSb3dzQ29sOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgcmV0dXJuIHNoYXBlUm93c0NvbFswXSArICdfJyArIHNoYXBlUm93c0NvbFsxXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuLyoqXG4gKiBTaW5lXG4gKi9cbmZ1bmN0aW9uIGdldFNpblVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHNpbih2YWx1ZSksIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2luRnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFNpblVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaW4oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgc2luUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHNpblByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTaW5Eb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRTaW5VbmFyeU9wKCkpO1xufVxuXG4vKipcbiAqIFRhbmhcbiAqL1xuZnVuY3Rpb24gZ2V0VGFuaFVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCBlMnggPSBleHAoLTIuMCAqIHZhbHVlKTtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCgxLjAgLSBlMngpIC8gKDEuMCArIGUyeCksIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGFuaEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRUYW5oVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhbmgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgdGFuaFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCB0YW5oUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFRhbmhEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRUYW5oVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyZXN1bHRPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXhBLCByZXN1bHRVVikucjtcbiAgICAgICR7cmVzdWx0T3B9XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmFyeU9wKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHVuYXJ5T3BQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIHJvd3MsIGNvbHVtbnMpO1xuICBncGdwdS5zZXRQcm9ncmFtKHVuYXJ5T3BQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRVbmFyeU9wRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICByZXN1bHRPcDogc3RyaW5nKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UocmVzdWx0T3ApO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU3JjKTtcbiAgY29uc3QgYVRleHR1cmU6IFdlYkdMVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHJlc3VsdFRleHR1cmU6IFdlYkdMVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4dHVyZSwgcm93cywgY29sdW1ucywgYSk7XG4gIHVuYXJ5T3AoZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCByb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxubGV0IFVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUgPSBmYWxzZTtcbmxldCBXRUJHTDJfRU5BQkxFRDogYm9vbGVhbnx1bmRlZmluZWQgPSBudWxsITtcbmxldCBNQVhfVEVYVFVSRV9TSVpFOiBudW1iZXIgPSBudWxsITtcblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcblxuZXhwb3J0IGludGVyZmFjZSBXZWJHTENvbnRleHRBdHRyaWJ1dGVzIHtcbiAgYWxwaGE/OiBib29sZWFuO1xuICBhbnRpYWxpYXM/OiBib29sZWFuO1xuICBwcmVtdWx0aXBsaWVkQWxwaGE/OiBib29sZWFuO1xuICBwcmVzZXJ2ZURyYXdpbmdCdWZmZXI/OiBib29sZWFuO1xuICBkZXB0aD86IGJvb2xlYW47XG4gIHN0ZW5jaWw/OiBib29sZWFuO1xuICBmYWlsSWZNYWpvclBlcmZvcm1hbmNlQ2F2ZWF0PzogYm9vbGVhbjtcbn1cblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBjb25zdCBJU19OQU5fU0hBREVSX0ZVTkMgPSBgXG5ib29sIGlzTmFOKGZsb2F0IHZhbCkge1xuICByZXR1cm4gdmFsID09IHZhbCA/IGZhbHNlIDogdHJ1ZTtcbn1cbmA7XG5cbmV4cG9ydCBpbnRlcmZhY2UgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbiB7IGxvc2VDb250ZXh0KCk6IHZvaWQ7IH1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dChhdHRyaWJ1dGVzOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzKTpcbiAgICBXZWJHTFJlbmRlcmluZ0NvbnRleHQge1xuICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgY2FudmFzLndpZHRoID0gMTtcbiAgY2FudmFzLmhlaWdodCA9IDE7XG4gIHJldHVybiBjcmVhdGVXZWJHTFJlbmRlcmluZ0NvbnRleHRGcm9tQ2FudmFzKGNhbnZhcywgYXR0cmlidXRlcyk7XG59XG5cbi8qKlxuICogRm9yY2UgdGhlIGxpYnJhcnkgdG8gcHJlZmVyIFdlYkdMIDEuMCBpbnN0ZWFkIG9mIFdlYkdMIDIuMCBldmVuIHdoZW4gV2ViR0xcbiAqIDIuMCBpcyBhdmFpbGFibGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwcmVmZXJXZWJHTDEoKSB7XG4gIFVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUgPSBmYWxzZTtcbiAgV0VCR0wyX0VOQUJMRUQgPSB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUHJlZmVyIFdlYkdMIDIuMCB0byBXZWJHTCAxLjAuIFRoaXMgaXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZWZlcldlYkdMMigpIHtcbiAgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IHRydWU7XG4gIFdFQkdMMl9FTkFCTEVEID0gdW5kZWZpbmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNXZWJHTDJFbmFibGVkKCkge1xuICBpZiAoIVVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBpZiAoV0VCR0wyX0VOQUJMRUQgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHRlbXBDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICBjb25zdCBnbCA9IHRlbXBDYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wyJyk7XG4gICAgaWYgKGdsICE9IG51bGwpIHtcbiAgICAgIFdFQkdMMl9FTkFCTEVEID0gdHJ1ZTtcblxuICAgICAgY29uc3QgbG9zZUNvbnRleHRFeHRlbnNpb24gPVxuICAgICAgICAgIGdldEV4dGVuc2lvbk9yVGhyb3coXG4gICAgICAgICAgICAgIGdsIGFzIFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgJ1dFQkdMX2xvc2VfY29udGV4dCcpIGFzXG4gICAgICAgICAgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgICAgIGxvc2VDb250ZXh0RXh0ZW5zaW9uLmxvc2VDb250ZXh0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIFdFQkdMMl9FTkFCTEVEID0gZmFsc2U7XG4gICAgfVxuICB9XG4gIHJldHVybiBXRUJHTDJfRU5BQkxFRDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoXG4gICAgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCxcbiAgICBhdHRyaWJ1dGVzOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzKTogV2ViR0xSZW5kZXJpbmdDb250ZXh0IHtcbiAgbGV0IGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIGlmIChpc1dlYkdMMkVuYWJsZWQoKSkge1xuICAgIGdsID0gY2FudmFzLmdldENvbnRleHQoJ3dlYmdsMicsIGF0dHJpYnV0ZXMpIGFzIFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgfSBlbHNlIHtcbiAgICBnbCA9IChjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wnLCBhdHRyaWJ1dGVzKSB8fFxuICAgICAgICAgIGNhbnZhcy5nZXRDb250ZXh0KCdleHBlcmltZW50YWwtd2ViZ2wnLCBhdHRyaWJ1dGVzKSkgYXNcbiAgICAgICAgV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB9XG5cbiAgaWYgKGdsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoaXMgYnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IFdlYkdMLicpO1xuICB9XG4gIHJldHVybiBnbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGxBbmRDaGVjazxUPihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmdW5jOiAoKSA9PiBUKTogVCB7XG4gIGNvbnN0IHJldHVyblZhbHVlID0gZnVuYygpO1xuICBjaGVja1dlYkdMRXJyb3IoZ2wpO1xuICByZXR1cm4gcmV0dXJuVmFsdWU7XG59XG5cbmxldCB3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQgPSBmYWxzZTtcblxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZURlYnVnV2ViR0xFcnJvckNoZWNraW5nKGVuYWJsZWQ6IGJvb2xlYW4pIHtcbiAgd2ViR0xEZWJ1Z0Vycm9yQ2hlY2tpbmdFbmFibGVkID0gZW5hYmxlZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNoZWNrV2ViR0xFcnJvcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gIGlmICh3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQpIHtcbiAgICBjb25zdCBlcnJvciA9IGdsLmdldEVycm9yKCk7XG4gICAgaWYgKGVycm9yICE9PSBnbC5OT19FUlJPUikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdXZWJHTCBFcnJvcjogJyArIGdldFdlYkdMRXJyb3JNZXNzYWdlKGdsLCBlcnJvcikpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0V2ViR0xFcnJvck1lc3NhZ2UoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgc3RhdHVzOiBudW1iZXIpOiBzdHJpbmcge1xuICBzd2l0Y2ggKHN0YXR1cykge1xuICAgIGNhc2UgZ2wuTk9fRVJST1I6XG4gICAgICByZXR1cm4gJ05PX0VSUk9SJztcbiAgICBjYXNlIGdsLklOVkFMSURfRU5VTTpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9FTlVNJztcbiAgICBjYXNlIGdsLklOVkFMSURfVkFMVUU6XG4gICAgICByZXR1cm4gJ0lOVkFMSURfVkFMVUUnO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9PUEVSQVRJT046XG4gICAgICByZXR1cm4gJ0lOVkFMSURfT1BFUkFUSU9OJztcbiAgICBjYXNlIGdsLklOVkFMSURfRlJBTUVCVUZGRVJfT1BFUkFUSU9OOlxuICAgICAgcmV0dXJuICdJTlZBTElEX0ZSQU1FQlVGRkVSX09QRVJBVElPTic7XG4gICAgY2FzZSBnbC5PVVRfT0ZfTUVNT1JZOlxuICAgICAgcmV0dXJuICdPVVRfT0ZfTUVNT1JZJztcbiAgICBjYXNlIGdsLkNPTlRFWFRfTE9TVF9XRUJHTDpcbiAgICAgIHJldHVybiAnQ09OVEVYVF9MT1NUX1dFQkdMJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICdVbmtub3duIGVycm9yIGNvZGUgJyArIHN0YXR1cztcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RXh0ZW5zaW9uT3JUaHJvdyhcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBleHRlbnNpb25OYW1lOiBzdHJpbmcpOiB7fSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDx7fT4oXG4gICAgICBnbCwgKCkgPT4gZ2wuZ2V0RXh0ZW5zaW9uKGV4dGVuc2lvbk5hbWUpLFxuICAgICAgJ0V4dGVuc2lvbiBcIicgKyBleHRlbnNpb25OYW1lICsgJ1wiIG5vdCBzdXBwb3J0ZWQgb24gdGhpcyBicm93c2VyLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVmVydGV4U2hhZGVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHZlcnRleFNoYWRlclNvdXJjZTogc3RyaW5nKTogV2ViR0xTaGFkZXIge1xuICBjb25zdCB2ZXJ0ZXhTaGFkZXI6IFdlYkdMU2hhZGVyID0gdGhyb3dJZk51bGw8V2ViR0xTaGFkZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVNoYWRlcihnbC5WRVJURVhfU0hBREVSKSxcbiAgICAgICdVbmFibGUgdG8gY3JlYXRlIHZlcnRleCBXZWJHTFNoYWRlci4nKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5zaGFkZXJTb3VyY2UodmVydGV4U2hhZGVyLCB2ZXJ0ZXhTaGFkZXJTb3VyY2UpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jb21waWxlU2hhZGVyKHZlcnRleFNoYWRlcikpO1xuICBpZiAoZ2wuZ2V0U2hhZGVyUGFyYW1ldGVyKHZlcnRleFNoYWRlciwgZ2wuQ09NUElMRV9TVEFUVVMpID09PSBmYWxzZSkge1xuICAgIGNvbnNvbGUubG9nKGdsLmdldFNoYWRlckluZm9Mb2codmVydGV4U2hhZGVyKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gY29tcGlsZSB2ZXJ0ZXggc2hhZGVyLicpO1xuICB9XG4gIHJldHVybiB2ZXJ0ZXhTaGFkZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGcmFnbWVudFNoYWRlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmcmFnbWVudFNoYWRlclNvdXJjZTogc3RyaW5nKTogV2ViR0xTaGFkZXIge1xuICBjb25zdCBmcmFnbWVudFNoYWRlcjogV2ViR0xTaGFkZXIgPSB0aHJvd0lmTnVsbDxXZWJHTFNoYWRlcj4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlU2hhZGVyKGdsLkZSQUdNRU5UX1NIQURFUiksXG4gICAgICAnVW5hYmxlIHRvIGNyZWF0ZSBmcmFnbWVudCBXZWJHTFNoYWRlci4nKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5zaGFkZXJTb3VyY2UoZnJhZ21lbnRTaGFkZXIsIGZyYWdtZW50U2hhZGVyU291cmNlKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuY29tcGlsZVNoYWRlcihmcmFnbWVudFNoYWRlcikpO1xuICBpZiAoZ2wuZ2V0U2hhZGVyUGFyYW1ldGVyKGZyYWdtZW50U2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0U2hhZGVySW5mb0xvZyhmcmFnbWVudFNoYWRlcikpO1xuICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGNvbXBpbGUgZnJhZ21lbnQgc2hhZGVyLicpO1xuICB9XG4gIHJldHVybiBmcmFnbWVudFNoYWRlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVByb2dyYW0oZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMUHJvZ3JhbSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTFByb2dyYW0+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVByb2dyYW0oKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xQcm9ncmFtLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbGlua1Byb2dyYW0oZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtKSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wubGlua1Byb2dyYW0ocHJvZ3JhbSkpO1xuICBpZiAoZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5MSU5LX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0UHJvZ3JhbUluZm9Mb2cocHJvZ3JhbSkpO1xuICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGxpbmsgdmVydGV4IGFuZCBmcmFnbWVudCBzaGFkZXJzLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVByb2dyYW0oXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtKSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmFsaWRhdGVQcm9ncmFtKHByb2dyYW0pKTtcbiAgaWYgKGdsLmdldFByb2dyYW1QYXJhbWV0ZXIocHJvZ3JhbSwgZ2wuVkFMSURBVEVfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRQcm9ncmFtSW5mb0xvZyhwcm9ncmFtKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdTaGFkZXIgcHJvZ3JhbSB2YWxpZGF0aW9uIGZhaWxlZC4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU3RhdGljVmVydGV4QnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGRhdGE6IEZsb2F0MzJBcnJheSk6IFdlYkdMQnVmZmVyIHtcbiAgY29uc3QgYnVmZmVyOiBXZWJHTEJ1ZmZlciA9IHRocm93SWZOdWxsPFdlYkdMQnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVCdWZmZXIoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xCdWZmZXInKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYnVmZmVyRGF0YShnbC5BUlJBWV9CVUZGRVIsIGRhdGEsIGdsLlNUQVRJQ19EUkFXKSk7XG4gIHJldHVybiBidWZmZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdGF0aWNJbmRleEJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBkYXRhOiBVaW50MTZBcnJheSk6IFdlYkdMQnVmZmVyIHtcbiAgY29uc3QgYnVmZmVyOiBXZWJHTEJ1ZmZlciA9IHRocm93SWZOdWxsPFdlYkdMQnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVCdWZmZXIoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xCdWZmZXInKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLmJ1ZmZlckRhdGEoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGRhdGEsIGdsLlNUQVRJQ19EUkFXKSk7XG4gIHJldHVybiBidWZmZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBxdWVyeU1heFRleHR1cmVTaXplKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBudW1iZXIge1xuICBpZiAoTUFYX1RFWFRVUkVfU0laRSAhPSBudWxsKSB7XG4gICAgcmV0dXJuIE1BWF9URVhUVVJFX1NJWkU7XG4gIH1cbiAgTUFYX1RFWFRVUkVfU0laRSA9XG4gICAgICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsIS5nZXRQYXJhbWV0ZXIoZ2whLk1BWF9URVhUVVJFX1NJWkUpKTtcbiAgcmV0dXJuIE1BWF9URVhUVVJFX1NJWkU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDaGFubmVsc1BlclRleHR1cmUoKTogbnVtYmVyIHtcbiAgaWYgKGlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgcmV0dXJuIDQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVUZXh0dXJlKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTFRleHR1cmUge1xuICByZXR1cm4gdGhyb3dJZk51bGw8V2ViR0xUZXh0dXJlPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVUZXh0dXJlKCksICdVbmFibGUgdG8gY3JlYXRlIFdlYkdMVGV4dHVyZS4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlVGV4dHVyZVNpemUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpIHtcbiAgY29uc3QgbWF4VGV4dHVyZVNpemU6IG51bWJlciA9IHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2wpO1xuICBpZiAoKHdpZHRoIDw9IDApIHx8IChoZWlnaHQgPD0gMCkpIHtcbiAgICBjb25zdCByZXF1ZXN0ZWQgPSAnWycgKyB3aWR0aCArICd4JyArIGhlaWdodCArICddJztcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlcXVlc3RlZCB0ZXh0dXJlIHNpemUgJyArIHJlcXVlc3RlZCArICcgaXMgaW52YWxpZC4nKTtcbiAgfVxuICBpZiAoKHdpZHRoID4gbWF4VGV4dHVyZVNpemUpIHx8IChoZWlnaHQgPiBtYXhUZXh0dXJlU2l6ZSkpIHtcbiAgICBjb25zdCByZXF1ZXN0ZWQgPSAnWycgKyB3aWR0aCArICd4JyArIGhlaWdodCArICddJztcbiAgICBjb25zdCBtYXggPSAnWycgKyBtYXhUZXh0dXJlU2l6ZSArICd4JyArIG1heFRleHR1cmVTaXplICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ1JlcXVlc3RlZCB0ZXh0dXJlIHNpemUgJyArIHJlcXVlc3RlZCArXG4gICAgICAgICcgZ3JlYXRlciB0aGFuIFdlYkdMIG1heGltdW0gb24gdGhpcyBicm93c2VyIC8gR1BVICcgKyBtYXggKyAnLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGcmFtZWJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xGcmFtZWJ1ZmZlciB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTEZyYW1lYnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVGcmFtZWJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEZyYW1lYnVmZmVyLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFZlcnRleEJ1ZmZlclRvUHJvZ3JhbUF0dHJpYnV0ZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGF0dHJpYnV0ZTogc3RyaW5nLFxuICAgIGJ1ZmZlcjogV2ViR0xCdWZmZXIsIGFycmF5RW50cmllc1Blckl0ZW06IG51bWJlciwgaXRlbVN0cmlkZUluQnl0ZXM6IG51bWJlcixcbiAgICBpdGVtT2Zmc2V0SW5CeXRlczogbnVtYmVyKSB7XG4gIGNvbnN0IGxvYyA9IGdsLmdldEF0dHJpYkxvY2F0aW9uKHByb2dyYW0sIGF0dHJpYnV0ZSk7XG4gIGlmIChsb2MgPT09IC0xKSB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoXG4gICAgICAgICdVbmFibGUgdG8gZ2V0IGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCIgb24gV2ViR0xQcm9ncmFtLicpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAoZXJyb3IgYXMgYW55KS5uYW1lZFZlcnRleEF0dHJpYnV0ZU5vdEZvdW5kID0gYXR0cmlidXRlO1xuICAgIHRocm93IGVycm9yO1xuICB9XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5BUlJBWV9CVUZGRVIsIGJ1ZmZlcikpO1xuICBjYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnZlcnRleEF0dHJpYlBvaW50ZXIoXG4gICAgICAgICAgbG9jLCBhcnJheUVudHJpZXNQZXJJdGVtLCBnbC5GTE9BVCwgZmFsc2UsIGl0ZW1TdHJpZGVJbkJ5dGVzLFxuICAgICAgICAgIGl0ZW1PZmZzZXRJbkJ5dGVzKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkobG9jKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVGV4dHVyZVVuaXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmVVbml0KTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hY3RpdmVUZXh0dXJlKGdsLlRFWFRVUkUwICsgdGV4dHVyZVVuaXQpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmJpbmRUZXh0dXJlVW5pdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmVVbml0KTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hY3RpdmVUZXh0dXJlKGdsLlRFWFRVUkUwICsgdGV4dHVyZVVuaXQpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRQcm9ncmFtVW5pZm9ybUxvY2F0aW9uT3JUaHJvdyhcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sXG4gICAgdW5pZm9ybU5hbWU6IHN0cmluZyk6IFdlYkdMVW5pZm9ybUxvY2F0aW9uIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMVW5pZm9ybUxvY2F0aW9uPihcbiAgICAgIGdsLCAoKSA9PiBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgdW5pZm9ybU5hbWUpLFxuICAgICAgJ3VuaWZvcm0gXCInICsgdW5pZm9ybU5hbWUgKyAnXCIgbm90IHByZXNlbnQgaW4gcHJvZ3JhbS4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRUZXh0dXJlVG9Qcm9ncmFtVW5pZm9ybVNhbXBsZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgdW5pZm9ybVNhbXBsZXJOYW1lOiBzdHJpbmcsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBiaW5kVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmUsIHRleHR1cmVVbml0KSk7XG4gIGNvbnN0IHNhbXBsZXJMb2NhdGlvbiA9XG4gICAgICBnZXRQcm9ncmFtVW5pZm9ybUxvY2F0aW9uT3JUaHJvdyhnbCwgcHJvZ3JhbSwgdW5pZm9ybVNhbXBsZXJOYW1lKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC51bmlmb3JtMWkoc2FtcGxlckxvY2F0aW9uLCB0ZXh0dXJlVW5pdCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZENhbnZhc1RvRnJhbWVidWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgbnVsbCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnZpZXdwb3J0KDAsIDAsIGdsLmNhbnZhcy53aWR0aCwgZ2wuY2FudmFzLmhlaWdodCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNjaXNzb3IoMCwgMCwgZ2wuY2FudmFzLndpZHRoLCBnbC5jYW52YXMuaGVpZ2h0KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgZnJhbWVidWZmZXI6IFdlYkdMRnJhbWVidWZmZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgICAgZ2wuRlJBTUVCVUZGRVIsIGdsLkNPTE9SX0FUVEFDSE1FTlQwLCBnbC5URVhUVVJFXzJELCB0ZXh0dXJlLCAwKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmJpbmRDb2xvclRleHR1cmVGcm9tRnJhbWVidWZmZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgZnJhbWVidWZmZXI6IFdlYkdMRnJhbWVidWZmZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgICAgZ2wuRlJBTUVCVUZGRVIsIGdsLkNPTE9SX0FUVEFDSE1FTlQwLCBnbC5URVhUVVJFXzJELCBudWxsLCAwKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUZyYW1lYnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgY29uc3Qgc3RhdHVzID0gZ2wuY2hlY2tGcmFtZWJ1ZmZlclN0YXR1cyhnbC5GUkFNRUJVRkZFUik7XG4gIGlmIChzdGF0dXMgIT09IGdsLkZSQU1FQlVGRkVSX0NPTVBMRVRFKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnRXJyb3IgYmluZGluZyBmcmFtZWJ1ZmZlcjogJyArIGdldEZyYW1lYnVmZmVyRXJyb3JNZXNzYWdlKGdsLCBzdGF0dXMpKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhbWVidWZmZXJFcnJvck1lc3NhZ2UoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgc3RhdHVzOiBudW1iZXIpOiBzdHJpbmcge1xuICBzd2l0Y2ggKHN0YXR1cykge1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9BVFRBQ0hNRU5UOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9NSVNTSU5HX0FUVEFDSE1FTlQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfTUlTU0lOR19BVFRBQ0hNRU5UJztcbiAgICBjYXNlIGdsLkZSQU1FQlVGRkVSX0lOQ09NUExFVEVfRElNRU5TSU9OUzpcbiAgICAgIHJldHVybiAnRlJBTUVCVUZGRVJfSU5DT01QTEVURV9ESU1FTlNJT05TJztcbiAgICBjYXNlIGdsLkZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9VTlNVUFBPUlRFRCc7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiAndW5rbm93biBlcnJvciAnICsgc3RhdHVzO1xuICB9XG59XG5cbmZ1bmN0aW9uIHRocm93SWZOdWxsPFQ+KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJldHVyblRPck51bGw6ICgpID0+IFQgfCBudWxsLFxuICAgIGZhaWx1cmVNZXNzYWdlOiBzdHJpbmcpOiBUIHtcbiAgY29uc3QgdE9yTnVsbDogVHxudWxsID0gY2FsbEFuZENoZWNrKGdsLCAoKSA9PiByZXR1cm5UT3JOdWxsKCkpO1xuICBpZiAodE9yTnVsbCA9PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGZhaWx1cmVNZXNzYWdlKTtcbiAgfVxuICByZXR1cm4gdE9yTnVsbCBhcyBUO1xufVxuXG5mdW5jdGlvbiB2YWxpZGF0ZVRleHR1cmVVbml0KGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgY29uc3QgbWF4VGV4dHVyZVVuaXQgPSBnbC5NQVhfQ09NQklORURfVEVYVFVSRV9JTUFHRV9VTklUUyAtIDE7XG4gIGNvbnN0IGdsVGV4dHVyZVVuaXQgPSB0ZXh0dXJlVW5pdCArIGdsLlRFWFRVUkUwO1xuICBpZiAoZ2xUZXh0dXJlVW5pdCA8IGdsLlRFWFRVUkUwIHx8IGdsVGV4dHVyZVVuaXQgPiBtYXhUZXh0dXJlVW5pdCkge1xuICAgIGNvbnN0IHRleHR1cmVVbml0UmFuZ2UgPSAnW2dsLlRFWFRVUkUwLCBnbC5URVhUVVJFJyArIG1heFRleHR1cmVVbml0ICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcigndGV4dHVyZVVuaXQgbXVzdCBiZSBpbiAnICsgdGV4dHVyZVVuaXRSYW5nZSArICcuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFRleHR1cmVTaGFwZUZyb21Mb2dpY2FsU2hhcGUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbG9naWNhbFNoYXBlOiBudW1iZXJbXSxcbiAgICBwcmVmZXJyZWRUZXhTaGFwZT86IFtudW1iZXIsIG51bWJlcl0pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgbWF4VGV4U2l6ZSA9IHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2wpO1xuICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKGxvZ2ljYWxTaGFwZSk7XG4gIGlmIChwcmVmZXJyZWRUZXhTaGFwZSAhPSBudWxsKSB7XG4gICAgY29uc3Qgc2l6ZVByZWZlcnJlZCA9IHV0aWwuc2l6ZUZyb21TaGFwZShwcmVmZXJyZWRUZXhTaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHNpemUgPT09IHNpemVQcmVmZXJyZWQsXG4gICAgICAgIGBTaXplIG9mIHNoYXBlICgke3NpemV9KSBtdXN0IG1hdGNoIHNpemUgb2YgYCArXG4gICAgICAgICAgICBgcHJlZmVycmVkU2hhcGUgKCR7c2l6ZVByZWZlcnJlZH0pYCk7XG4gICAgaWYgKHByZWZlcnJlZFRleFNoYXBlWzBdIDw9IG1heFRleFNpemUgJiZcbiAgICAgICAgcHJlZmVycmVkVGV4U2hhcGVbMV0gPD0gbWF4VGV4U2l6ZSkge1xuICAgICAgcmV0dXJuIHByZWZlcnJlZFRleFNoYXBlO1xuICAgIH1cbiAgfVxuXG4gIGlmIChsb2dpY2FsU2hhcGUubGVuZ3RoIDw9IDEgJiYgc2l6ZSA8PSBtYXhUZXhTaXplKSB7XG4gICAgcmV0dXJuIFtzaXplLCAxXTtcbiAgfSBlbHNlIGlmIChcbiAgICAgIGxvZ2ljYWxTaGFwZS5sZW5ndGggPT09IDIgJiYgbG9naWNhbFNoYXBlWzBdIDw9IG1heFRleFNpemUgJiZcbiAgICAgIGxvZ2ljYWxTaGFwZVsxXSA8PSBtYXhUZXhTaXplKSB7XG4gICAgcmV0dXJuIGxvZ2ljYWxTaGFwZSBhcyBbbnVtYmVyLCBudW1iZXJdO1xuICB9IGVsc2UgaWYgKFxuICAgICAgbG9naWNhbFNoYXBlLmxlbmd0aCA9PT0gMyAmJiBsb2dpY2FsU2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgbG9naWNhbFNoYXBlWzFdICogbG9naWNhbFNoYXBlWzJdIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gW2xvZ2ljYWxTaGFwZVswXSwgbG9naWNhbFNoYXBlWzFdICogbG9naWNhbFNoYXBlWzJdXTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gdXRpbC5zaXplVG9TcXVhcmlzaFNoYXBlKHNpemUpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7QWRkTm9kZSwgQXJnTWF4RXF1YWxzTm9kZSwgQXJnTWF4Tm9kZSwgQ29uY2F0M0ROb2RlLCBDb252b2x1dGlvbjJETm9kZSwgRGl2aWRlTm9kZSwgRXhwTm9kZSwgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUsIEdyYXBoLCBMb2dOb2RlLCBNYXRNdWxOb2RlLCBNYXhQb29sTm9kZSwgTWVhblNxdWFyZWRDb3N0Tm9kZSwgTXVsdGlwbHlOb2RlLCBOb2RlLCBSZWR1Y2VTdW1Ob2RlLCBSZUxVTm9kZSwgUmVzaGFwZU5vZGUsIFNpZ21vaWROb2RlLCBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUsIFNvZnRtYXhOb2RlLCBTcGxpdE5vZGUsIFNxdWFyZU5vZGUsIFN1YnRyYWN0Tm9kZSwgVGFuSE5vZGUsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge0FkZH0gZnJvbSAnLi9vcHMvYWRkJztcbmltcG9ydCB7QXJnTWF4fSBmcm9tICcuL29wcy9hcmdtYXgnO1xuaW1wb3J0IHtBcmdNYXhFcXVhbHN9IGZyb20gJy4vb3BzL2FyZ21heGVxdWFscyc7XG5pbXBvcnQge0NvbmNhdDNEfSBmcm9tICcuL29wcy9jb25jYXQzZCc7XG5pbXBvcnQge0NvbnZvbHV0aW9uMkR9IGZyb20gJy4vb3BzL2NvbnZvbHV0aW9uJztcbmltcG9ydCB7RGl2aWRlfSBmcm9tICcuL29wcy9kaXZpZGUnO1xuaW1wb3J0IHtSZUxVLCBTaWdtb2lkLCBTcXVhcmUsIFRhbkh9IGZyb20gJy4vb3BzL2VsZW1lbnRfd2lzZV9hY3RpdmF0aW9uJztcbmltcG9ydCB7TWVhblNxdWFyZWRDb3N0fSBmcm9tICcuL29wcy9lbGVtZW50X3dpc2VfY29zdCc7XG5pbXBvcnQge0V4cH0gZnJvbSAnLi9vcHMvZXhwJztcbmltcG9ydCB7TGluZWFyQ29tYmluYXRpb259IGZyb20gJy4vb3BzL2xpbmVhcl9jb21iaW5hdGlvbic7XG5pbXBvcnQge0xvZ30gZnJvbSAnLi9vcHMvbG9nJztcbmltcG9ydCB7TWF0TXVsfSBmcm9tICcuL29wcy9tYXRtdWwnO1xuaW1wb3J0IHtNYXhQb29sfSBmcm9tICcuL29wcy9tYXhfcG9vbCc7XG5pbXBvcnQge011bHRpcGx5fSBmcm9tICcuL29wcy9tdWx0aXBseSc7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtSZWR1Y2VTdW19IGZyb20gJy4vb3BzL3JlZHVjZV9zdW0nO1xuaW1wb3J0IHtSZXNoYXBlfSBmcm9tICcuL29wcy9yZXNoYXBlJztcbmltcG9ydCB7U29mdG1heCwgU29mdG1heENyb3NzRW50cm9weUNvc3R9IGZyb20gJy4vb3BzL3NvZnRtYXgnO1xuaW1wb3J0IHtTcGxpdH0gZnJvbSAnLi9vcHMvc3BsaXQnO1xuaW1wb3J0IHtTdWJ0cmFjdH0gZnJvbSAnLi9vcHMvc3VidHJhY3QnO1xuXG5leHBvcnQgZnVuY3Rpb24gZW1pdEZyb21HcmFwaE5vZGVzKG5vZGVzOiBOb2RlW10pOiBPcGVyYXRpb25bXSB7XG4gIGNvbnN0IG9wczogT3BlcmF0aW9uW10gPSBbXTtcbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IEFycmF5LnByb3RvdHlwZS5wdXNoLmFwcGx5KG9wcywgZW1pdE9wRnJvbU5vZGUobm9kZSkpKTtcbiAgcmV0dXJuIG9wcztcbn1cblxuZnVuY3Rpb24gZW1pdE9wRnJvbU5vZGUobm9kZTogTm9kZSk6IE9wZXJhdGlvbltdIHtcbiAgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZXNoYXBlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFJlc2hhcGUobm9kZS5pbnB1dHNbUmVzaGFwZU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE1hdE11bE5vZGUpIHtcbiAgICBjb25zdCB4MSA9IG5vZGUuaW5wdXRzW01hdE11bE5vZGUuWDFdO1xuICAgIGNvbnN0IHgyID0gbm9kZS5pbnB1dHNbTWF0TXVsTm9kZS5YMl07XG4gICAgcmV0dXJuIFtuZXcgTWF0TXVsKHgxLCB4Miwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQ29udm9sdXRpb24yRE5vZGUpIHtcbiAgICBjb25zdCB3ID0gbm9kZS5pbnB1dHNbQ29udm9sdXRpb24yRE5vZGUuV107XG4gICAgY29uc3QgeCA9IG5vZGUuaW5wdXRzW0NvbnZvbHV0aW9uMkROb2RlLlhdO1xuICAgIGNvbnN0IGIgPSBub2RlLmlucHV0c1tDb252b2x1dGlvbjJETm9kZS5CXTtcbiAgICByZXR1cm4gW25ldyBDb252b2x1dGlvbjJEKFxuICAgICAgICB3LCB4LCBiLCBub2RlLm91dHB1dCwgbm9kZS5maWVsZFNpemUsIG5vZGUub3V0cHV0RGVwdGgsIG5vZGUuc3RyaWRlLFxuICAgICAgICBub2RlLnplcm9QYWQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTWF4UG9vbE5vZGUpIHtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbTWF4UG9vbE5vZGUuWF07XG4gICAgcmV0dXJuIFtuZXcgTWF4UG9vbChcbiAgICAgICAgeCwgbm9kZS5vdXRwdXQsIG5vZGUuZmllbGRTaXplLCBub2RlLnN0cmlkZSwgbm9kZS56ZXJvUGFkKV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEV4cE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBFeHAobm9kZS5pbnB1dHNbRXhwTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTG9nTm9kZSkge1xuICAgIHJldHVybiBbbmV3IExvZyhub2RlLmlucHV0c1tMb2dOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZUxVTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFJlTFUobm9kZS5pbnB1dHNbUmVMVU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFRhbkhOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgVGFuSChub2RlLmlucHV0c1tUYW5ITm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU2lnbW9pZE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTaWdtb2lkKG5vZGUuaW5wdXRzW1NpZ21vaWROb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUpIHtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlLlhdO1xuICAgIGNvbnN0IHRhcmdldCA9IG5vZGUuaW5wdXRzW1NvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZS5UQVJHRVRdO1xuICAgIHJldHVybiBbbmV3IFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0KHgsIHRhcmdldCwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU29mdG1heE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTb2Z0bWF4KG5vZGUuaW5wdXRzW1NvZnRtYXhOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNZWFuU3F1YXJlZENvc3ROb2RlKSB7XG4gICAgY29uc3QgbGFiZWwgPSBub2RlLmlucHV0c1tNZWFuU3F1YXJlZENvc3ROb2RlLkxBQkVMXTtcbiAgICBjb25zdCBwcmVkaWN0aW9uID0gbm9kZS5pbnB1dHNbTWVhblNxdWFyZWRDb3N0Tm9kZS5QUkVESUNUSU9OXTtcbiAgICByZXR1cm4gW25ldyBNZWFuU3F1YXJlZENvc3QobGFiZWwsIHByZWRpY3Rpb24sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEFyZ01heEVxdWFsc05vZGUpIHtcbiAgICByZXR1cm4gW25ldyBBcmdNYXhFcXVhbHMoXG4gICAgICAgIG5vZGUuaW5wdXRzW0FyZ01heEVxdWFsc05vZGUuWDFdLCBub2RlLmlucHV0c1tBcmdNYXhFcXVhbHNOb2RlLlgyXSxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQXJnTWF4Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IEFyZ01heChub2RlLngsIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEZ1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgTGluZWFyQ29tYmluYXRpb24oXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLlQxXSxcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuVDJdLFxuICAgICAgICBub2RlLmlucHV0c1tGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZS5DMV0sXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLkMyXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQ29uY2F0M0ROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQ29uY2F0M0QoXG4gICAgICAgIG5vZGUuaW5wdXRzW0NvbmNhdDNETm9kZS5YMV0sIG5vZGUuaW5wdXRzW0NvbmNhdDNETm9kZS5YMl0sIG5vZGUuYXhpcyxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU3F1YXJlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNxdWFyZShub2RlLmlucHV0c1tTcXVhcmVOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBBZGROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQWRkKFxuICAgICAgICBub2RlLmlucHV0c1tBZGROb2RlLlQxXSwgbm9kZS5pbnB1dHNbQWRkTm9kZS5UMl0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFN1YnRyYWN0Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFN1YnRyYWN0KFxuICAgICAgICBub2RlLmlucHV0c1tTdWJ0cmFjdE5vZGUuVDFdLCBub2RlLmlucHV0c1tTdWJ0cmFjdE5vZGUuVDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNdWx0aXBseU5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBNdWx0aXBseShcbiAgICAgICAgbm9kZS5pbnB1dHNbTXVsdGlwbHlOb2RlLlQxXSwgbm9kZS5pbnB1dHNbTXVsdGlwbHlOb2RlLlQyXSxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRGl2aWRlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IERpdmlkZShcbiAgICAgICAgbm9kZS5pbnB1dHNbRGl2aWRlTm9kZS5UMV0sIG5vZGUuaW5wdXRzW0RpdmlkZU5vZGUuVDJdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTcGxpdE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTcGxpdChub2RlLmlucHV0c1tTcGxpdE5vZGUuWF0sIG5vZGUub3V0cHV0cyldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZWR1Y2VTdW1Ob2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVkdWNlU3VtKG5vZGUuaW5wdXRzW1JlZHVjZVN1bU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAoZ3JhcGhfdXRpbC5pc0lucHV0Tm9kZShub2RlKSkge1xuICAgIHJldHVybiBbXTtcbiAgfSBlbHNlIHtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgdGhyb3cgRXJyb3IoJ1Vuc3VwcG9ydGVkIG5vZGUgdHlwZTogJyArIChub2RlLmNvbnN0cnVjdG9yIGFzIGFueSkubmFtZSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBZGQgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIGR5U2l6ZVNjYWxhcjogU2NhbGFyO1xuXG4gIC8qKiBFbGVtZW50LXdpc2UgYWRkIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLiAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDJUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyUGx1c0FycmF5KHgxLCB4Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoeDIsIHgxKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYWRkKHgxLCB4Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDFUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBkeSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLngyVGVuc29yLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5kaXZpZGUoc3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwgZHkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmR5U2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXggZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQW4gQXJnTWF4IG9wZXJhdGlvbi5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5hcmdNYXgoeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FyZ01heCBiYWNrcHJvcCB1bmltcGxlbWVudGVkJyk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFyZ01heEVxdWFscyBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBbiBBcmdNYXhFcXVhbHMgb3BlcmF0aW9uLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmFyZ01heEVxdWFscyh4MSwgeDIpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdBcmdNYXhFcXVhbHMgYmFja3Byb3AgdW5pbXBsZW1lbnRlZCcpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4uL21hdGgvY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTNELCBOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQ29uY2F0M0QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQSBDb25jYXQgM0Qgb3BlcmF0aW9uLlxuICAgKlxuICAgKiBDb25jYXRzIHR3byAzRCB0ZW5zb3JzIGFsb25nIGFuIGF4aXMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLCBwcml2YXRlIGF4aXM6IG51bWJlcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSwgYXhpcyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgY29uY2F0UmVzdWx0ID0gbWF0aC5jb25jYXQzRCh4MSwgeDIsIHRoaXMuYXhpcyk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChjb25jYXRSZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbmNhdDNEIGJhY2twcm9wIG5vdCBpbXBsZW1lbnRlZC4nKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb252b2x1dGlvbjJEIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSB6ZXJvUGFkOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYSBjb252b2x1dGlvbiBvcCB3aXRoIHRoZSBzcGVjaWZpZWQgcHJvcGVydGllcy5cbiAgICpcbiAgICogQHBhcmFtIGlucHV0U2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBpbnB1dCBuZGFycmF5LlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBmaWx0ZXIgKHJvd3MvY29scyBvZiBzbGlkaW5nIHdpbmRvdykuXG4gICAqIEBwYXJhbSBvdXRwdXREZXB0aCBUaGUgZGVwdGggb2YgdGhlIG91dHB1dCAoTnVtYmVyIG9mIGZpbHRlcnMpLlxuICAgKiBAcGFyYW0gc3RyaWRlIEhvdyBtYW55IHBpeGVscyB0byBzaGlmdCB0aGUgZmlsdGVyIGJ5IHdoZW4gc2xpZGluZy5cbiAgICogICAgIERlZmF1bHRzIHRvIDEuXG4gICAqIEBwYXJhbSB6ZXJvUGFkIEhvdyBtYW55IHBpeGVscyB0byBwYWQgdGhlIGlucHV0IGZyb20gZWFjaCBzaWRlLiBEZWZhdWx0cyB0b1xuICAgKiAgICAgYSB2YWx1ZSBzbyB0aGF0IHRoZSByb3dzIGFuZCBjb2x1bW5zIG9mIHRoZSBvdXRwdXQgbmRhcnJheSBpc1xuICAgKiAgICAgdGhlIHNhbWUgYXMgdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSB3ZWlnaHRzIE9wdGlvbmFsLiBUaGUgd2VpZ2h0cyBvZiB0aGUgZmlsdGVycy5cbiAgICogQHBhcmFtIGJpYXNlcyBPcHRpb25hbC4gVGhlIGJpYXMgdGVybXMgb2YgdGhlIGZpbHRlcnMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgd1RlbnNvcjogVGVuc29yLCBwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBiVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBmaWVsZFNpemU6IG51bWJlcixcbiAgICAgIHByaXZhdGUgb3V0cHV0RGVwdGg6IG51bWJlciwgcHJpdmF0ZSBzdHJpZGUgPSAxLCB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLmFzc2VydFdlaWdodHNTaGFwZSh3VGVuc29yLnNoYXBlKTtcbiAgICB0aGlzLnplcm9QYWQgPSB6ZXJvUGFkICE9IG51bGwgP1xuICAgICAgICB6ZXJvUGFkIDpcbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKFxuICAgICAgICAgICAgdGhpcy54VGVuc29yLnNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgdGhpcy5maWVsZFNpemUsXG4gICAgICAgICAgICB0aGlzLnN0cmlkZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuaXNJbnQodGhpcy56ZXJvUGFkKSxcbiAgICAgICAgYFRoZSB6ZXJvIHBhZGRpbmcgKCR7dGhpcy56ZXJvUGFkfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB3ZWlnaHRzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLndUZW5zb3IpIGFzIEFycmF5NEQ7XG4gICAgY29uc3QgYmlhc2VzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmJUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLmNvbnYyZCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3Qgd2VpZ2h0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy53VGVuc29yKSBhcyBBcnJheTREO1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcikgYXMgQXJyYXkzRDtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCB7ZHcsIGRiLCBkeH0gPVxuICAgICAgICAgIG1hdGguY29udjJkQmFja1Byb3AoeCwgZHksIHdlaWdodHMsIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMud1RlbnNvciwga2VlcChkdykpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuYlRlbnNvciwga2VlcChkYikpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChkeCkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3NlcnRXZWlnaHRzU2hhcGUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzU2hhcGVbMF0gPT09IHRoaXMuZmllbGRTaXplICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbMV0gPT09IHRoaXMuZmllbGRTaXplICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbMl0gPT09IHRoaXMueFRlbnNvci5zaGFwZVsyXSAmJlxuICAgICAgICAgICAgd2VpZ2h0c1NoYXBlWzNdID09PSB0aGlzLm91dHB1dERlcHRoLFxuICAgICAgICBgd2VpZ2h0cyBtdXN0IGJlIG9mIHNoYXBlIFske3RoaXMuZmllbGRTaXplfSwke3RoaXMuZmllbGRTaXplfSxgICtcbiAgICAgICAgICAgIGAke3RoaXMueFRlbnNvci5zaGFwZVsyXX0sJHt0aGlzLm91dHB1dERlcHRofV0gYnV0IHRoZXkgYXJlIG9mYCArXG4gICAgICAgICAgICBgc2hhcGUgWyR7d2VpZ2h0c1NoYXBlfV1gKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIERpdmlkZSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgb25lczogTkRBcnJheTtcblxuICAvKipcbiAgICogRWxlbWVudC13aXNlIGRpdmlkZSBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJEaXZpZGVkQnlBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIodDEsIHQyKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguZGl2aWRlKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBjb25zdCB4MUlzU2NhbGFyID0gdXRpbC5pc1NjYWxhclNoYXBlKHgxLnNoYXBlKTtcbiAgICBjb25zdCB4MklzU2NhbGFyID0gdXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGlmICh4MUlzU2NhbGFyKSB7XG4gICAgICAgICAgY29uc3QgZGl2ID0gbWF0aC5kaXZpZGUoZHksIHgyKTtcblxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc3VtKGRpdikpKTtcblxuICAgICAgICAgIGRpdi5kaXNwb3NlKCk7XG4gICAgICAgIH0gZWxzZSBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLmFycmF5RGl2aWRlZEJ5U2NhbGFyKGR5LCB4MikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcChtYXRoLmRpdmlkZShkeSwgeDIpKSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgLy8gZHgyID0gLTEgKiB4MSAqIHgyIF4gLTIuXG4gICAgICAgIGNvbnN0IHgyU3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIHgyKTtcblxuICAgICAgICBsZXQgeDFPdmVyWDJTcXVhcmVkOiBOREFycmF5O1xuICAgICAgICBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIHgxT3ZlclgyU3F1YXJlZCA9IG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH0gZWxzZSBpZiAoeDFJc1NjYWxhcikge1xuICAgICAgICAgIHgxT3ZlclgyU3F1YXJlZCA9IG1hdGguc2NhbGFyRGl2aWRlZEJ5QXJyYXkoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5kaXZpZGUoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBkeDIgPSBtYXRoLm5lZyh4MU92ZXJYMlNxdWFyZWQpO1xuICAgICAgICBjb25zdCBkeVRpbWVzRGVyaXZhdGl2ZSA9IG1hdGguZWxlbWVudFdpc2VNdWwoZHksIGR4Mik7XG5cbiAgICAgICAgaWYgKHgySXNTY2FsYXIpIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChtYXRoLnN1bShkeVRpbWVzRGVyaXZhdGl2ZSkpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChkeVRpbWVzRGVyaXZhdGl2ZSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7QWN0aXZhdGlvbkZ1bmN0aW9uLCBSZUxVRnVuYywgU2lnbW9pZEZ1bmMsIFNxdWFyZUZ1bmMsIFRhbkhGdW5jfSBmcm9tICcuLi9tYXRoL2FjdGl2YXRpb25fZnVuY3Rpb25zJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJvdGVjdGVkIHhUZW5zb3I6IFRlbnNvciwgcHJvdGVjdGVkIHlUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgZnVuYzogQWN0aXZhdGlvbkZ1bmN0aW9uKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHRoaXMuZnVuYy5vdXRwdXQobWF0aCwgeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICAvLyBkRS9keF9pID0gc3VtX2ogZEUvZHlfaiAqIGR5X2ovZHhfaVxuICAgIC8vICAgICAgICAgPSBkRS9keV9pICogZHlfaS9keF9pXG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcbiAgICBjb25zdCB5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBkeWR4ID0gdGhpcy5mdW5jLmRlcihtYXRoLCB4LCB5KTtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bChkeSwgZHlkeCkpKTtcbiAgICAgIGR5ZHguZGlzcG9zZSgpO1xuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVMVSBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFJlTFVGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgVGFuSCBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFRhbkhGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU2lnbW9pZCBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFNpZ21vaWRGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3F1YXJlIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgU3F1YXJlRnVuYygpKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7RWxlbWVudFdpc2VDb3N0RnVuY3Rpb24sIFNxdWFyZUNvc3RGdW5jfSBmcm9tICcuLi9tYXRoL2Nvc3RfZnVuY3Rpb25zJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEVsZW1lbnRXaXNlQ29zdDxUIGV4dGVuZHMgTkRBcnJheT4gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIG9uZU92ZXJOU2NhbGFyOiBTY2FsYXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcm90ZWN0ZWQgeDFUZW5zb3I6IFRlbnNvciwgcHJvdGVjdGVkIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcm90ZWN0ZWQgeVRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgZnVuYzogRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24pIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMub25lT3Zlck5TY2FsYXIgPSBTY2FsYXIubmV3KDEgLyB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGVsZW1lbnRXaXNlQ29zdCA9IHRoaXMuZnVuYy5jb3N0KG1hdGgsIHgxLCB4Mik7XG4gICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShlbGVtZW50V2lzZUNvc3QpO1xuICAgICAgY29uc3QgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHRoaXMub25lT3Zlck5TY2FsYXIsIHN1bSk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAodGhpcy5mdW5jLmRlcihtYXRoLCB4MSwgeDIpKSk7XG4gICAgICB9XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcCh0aGlzLmZ1bmMuZGVyKG1hdGgsIHgyLCB4MSkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5mdW5jLmRpc3Bvc2UoKTtcbiAgICB0aGlzLm9uZU92ZXJOU2NhbGFyLmRpc3Bvc2UoKTtcbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1lYW5TcXVhcmVkQ29zdCBleHRlbmRzIEVsZW1lbnRXaXNlQ29zdDxBcnJheTFEPiB7XG4gIGNvbnN0cnVjdG9yKHgxVGVuc29yOiBUZW5zb3IsIHgyVGVuc29yOiBUZW5zb3IsIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKHgxVGVuc29yLCB4MlRlbnNvciwgeVRlbnNvciwgbmV3IFNxdWFyZUNvc3RGdW5jKCkpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRXhwIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEV4cG9uZW50YXRpb24gb3BlcmF0aW9uIC0gZV54LlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKG1hdGguZXhwKHgpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54VGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54VGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeSwgZHkpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTGluZWFyQ29tYmluYXRpb24gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQSAyLXRlbnNvciBsaW5lYXIgY29tYmluYXRpb24gb3BlcmF0aW9uLlxuICAgKlxuICAgKiBDb21iaW5lcyB0ZW5zb3JzIHgxIGFuZCB4MiAob2YgdGhlIHNhbWUgc2hhcGUpIHdpdGggd2VpZ2h0cyBjMSAmIGMyO1xuICAgKiBDb21wdXRlcyBjMSp4MSArIGMyKngyLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgYzFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBjMlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBvdXRUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgYzEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzFUZW5zb3IpLmFzU2NhbGFyKCk7XG4gICAgY29uc3QgYzIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzJUZW5zb3IpLmFzU2NhbGFyKCk7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMub3V0VGVuc29yLCBrZWVwKG1hdGguc2NhbGVkQXJyYXlBZGQoYzEsIHgxLCBjMiwgeDIpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBjMSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMVRlbnNvcik7XG4gICAgY29uc3QgYzIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShjMSwgZHkpKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShjMiwgZHkpKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMuYzFUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGRvdFByb2R1Y3QxID0gbWF0aC5lbGVtZW50V2lzZU11bCh4MSwgZHkpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5jMVRlbnNvciwga2VlcChtYXRoLnN1bShkb3RQcm9kdWN0MSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy5jMlRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZG90UHJvZHVjdDIgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHgyLCBkeSk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmMyVGVuc29yLCBrZWVwKG1hdGguc3VtKGRvdFByb2R1Y3QyKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTG9nIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIE5hdHVyYWwgbG9nIG9wZXJhdGlvbiAtIGxuKHgpXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5sb2coeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnhUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5kaXZpZGUoZHksIHgpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgTkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1hdE11bCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHgyLnNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgICAgdGhpcy55VGVuc29yLCBrZWVwKG1hdGgubWF0TXVsKHgxIGFzIEFycmF5MkQsIHgyIGFzIEFycmF5MkQpKSk7XG4gICAgICB9IGVsc2UgaWYgKHgxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB4Mi5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICAgIGtlZXAobWF0aC5tYXRyaXhUaW1lc1ZlY3Rvcih4MSBhcyBBcnJheTJELCB4MiBhcyBBcnJheTFEKSkpO1xuICAgICAgfSBlbHNlIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDEgJiYgeDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKG1hdGgudmVjdG9yVGltZXNNYXRyaXgoeDEgYXMgQXJyYXkxRCwgeDIgYXMgQXJyYXkyRCkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBsZXQgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGxldCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgbGV0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB4MSA9IHgxLnJlc2hhcGUoWzEsIHgxLnNpemVdKTtcbiAgICAgIGR5ID0gZHkucmVzaGFwZShbMSwgZHkuc2l6ZV0pO1xuICAgIH1cbiAgICBpZiAoeDIuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB4MiA9IHgyLnJlc2hhcGUoW3gyLnNpemUsIDFdKTtcbiAgICAgIGR5ID0gZHkucmVzaGFwZShbZHkuc2l6ZSwgMV0pO1xuICAgIH1cblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIC8vIHkgPSB4MSAqIHgyXG4gICAgICAvLyBkeDEgPSBkeSAqIHgyVFxuICAgICAgLy8gZHgyID0geDFUICogZHlcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGR4MSA9IG1hdGgubWF0TXVsKFxuICAgICAgICAgICAgZHkgYXMgQXJyYXkyRCwgeDIgYXMgQXJyYXkyRCwgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICAgICAgICAgIE1hdHJpeE9yaWVudGF0aW9uLlRSQU5TUE9TRUQpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLngxVGVuc29yLFxuICAgICAgICAgICAga2VlcCh0aGlzLngxVGVuc29yLnNoYXBlLmxlbmd0aCA9PT0gMSA/IGR4MS5hczFEKCkgOiBkeDEpKTtcbiAgICAgIH1cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGR4MiA9IG1hdGgubWF0TXVsKFxuICAgICAgICAgICAgeDEgYXMgQXJyYXkyRCwgZHkgYXMgQXJyYXkyRCwgTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRCxcbiAgICAgICAgICAgIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLngyVGVuc29yLFxuICAgICAgICAgICAga2VlcCh0aGlzLngyVGVuc29yLnNoYXBlLmxlbmd0aCA9PT0gMSA/IGR4Mi5hczFEKCkgOiBkeDIpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MkQsIEFycmF5M0QsIE5EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNYXhQb29sIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBwYWQ6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgZmllbGRTaXplOiBudW1iZXIsIHByaXZhdGUgc3RyaWRlID0gMSwgcGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIGlmIChwYWQgIT0gbnVsbCkge1xuICAgICAgdGhpcy5wYWQgPSBwYWQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucGFkID0gY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKFxuICAgICAgICAgIHhUZW5zb3Iuc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB0aGlzLmZpZWxkU2l6ZSxcbiAgICAgICAgICB0aGlzLnN0cmlkZSk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuaXNJbnQodGhpcy5wYWQpLFxuICAgICAgICBgVGhlIHplcm8gcGFkZGluZyAoJHt0aGlzLnBhZH0pIG11c3QgYmUgYW4gaW50ZWdlci4gQ2hhbmdlIHRoZSBgICtcbiAgICAgICAgICAgIGBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgIGtlZXAobWF0aC5tYXhQb29sKHgsIHRoaXMuZmllbGRTaXplLCB0aGlzLnN0cmlkZSwgdGhpcy5wYWQpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgQXJyYXkzRDtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnhUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLm1heFBvb2xCYWNrcHJvcChcbiAgICAgICAgICAgICAgZHksIHgsIHRoaXMuZmllbGRTaXplLCB0aGlzLnN0cmlkZSwgdGhpcy5wYWQpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNdWx0aXBseSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2UgbXVsdGlwbHkgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDJUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB0MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgdDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0MiwgdDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5lbGVtZW50V2lzZU11bCh0MSwgdDIpO1xuICAgICAgfVxuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAocmVzdWx0KSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLngxVGVuc29yLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IG11bCA9IG1hdGguZWxlbWVudFdpc2VNdWwoZHksIHgyKTtcblxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc3VtKG11bCkpKTtcblxuICAgICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4Mi5zaGFwZSkpIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KHgyLCBkeSkpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcChtYXRoLmVsZW1lbnRXaXNlTXVsKHgyLCBkeSkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDJUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3QgbXVsID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgeDEpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5zdW0obXVsKSkpO1xuXG4gICAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgxLnNoYXBlKSkge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MlRlbnNvciwga2VlcChtYXRoLnNjYWxhclRpbWVzQXJyYXkoeDEsIGR5KSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeDEsIGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgT3BlcmF0aW9uIHtcbiAgYWJzdHJhY3QgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApOlxuICAgICAgdm9pZDtcblxuICBhYnN0cmFjdCBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKTogdm9pZDtcblxuICBkaXNwb3NlVHJhbnNpZW50QXJyYXlzKFxuICAgICAgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7fVxuXG4gIGRpc3Bvc2UoKSB7fVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZWR1Y2VTdW0gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKiogRWxlbWVudC13aXNlIGFkZCBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzIHNjYWxhci4gKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgb3V0VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2gob3V0VGVuc29yLnNoYXBlLCBbXSk7XG4gIH1cblxuICBwcml2YXRlIG9uZXM6IE5EQXJyYXk7XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLm91dFRlbnNvciwga2VlcChtYXRoLnN1bSh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGlmICghZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuICAgICAgaWYgKHRoaXMub25lcyA9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IHhBcnJheSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54KTtcbiAgICAgICAgdGhpcy5vbmVzID0gTkRBcnJheS56ZXJvc0xpa2UoeEFycmF5KTtcbiAgICAgICAgdGhpcy5vbmVzLmZpbGwoMSk7XG4gICAgICB9XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54LCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShkeSwgdGhpcy5vbmVzKSkpO1xuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBSZXNoYXBlPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgY29uc3QgeFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoeFRlbnNvci5zaGFwZSk7XG4gICAgY29uc3QgeVNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoeVRlbnNvci5zaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHhTaXplID09PSB5U2l6ZSxcbiAgICAgICAgYFRoZSBpbnB1dCBzaXplICgke3hTaXplfSkgYW5kIG91dHB1dCBzaXplICgke3lTaXplfSkgbXVzdCBtYXRjaGApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIFQxO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5yZXNoYXBlPFQxLCBUMj4oeCwgdGhpcy55VGVuc29yLnNoYXBlKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgVDI7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy54VGVuc29yLCBrZWVwKG1hdGgucmVzaGFwZTxUMiwgVDE+KGR5LCB0aGlzLnhUZW5zb3Iuc2hhcGUpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG5leHBvcnQgY2xhc3MgU29mdG1heCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbG9naXRzVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgb3V0cHV0OiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBsb2dpdHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubG9naXRzVGVuc29yKSBhcyBBcnJheTFEO1xuICAgIHJldHVybiBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0cHV0LCBrZWVwKG1hdGguc29mdG1heChsb2dpdHMpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcCgpIHtcbiAgICB0aHJvdyBFcnJvcignU29mdG1heCBiYWNrcHJvcCBpcyBub3QgeWV0IGltcGxlbWVudGVkJyk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIGxvZ2l0c1RlbnNvcjogVGVuc29yLCBwcml2YXRlIGxhYmVsVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5zb2Z0bWF4VGVuc29yID0gbmV3IFRlbnNvcihsb2dpdHNUZW5zb3Iuc2hhcGUpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBsb2dpdHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubG9naXRzVGVuc29yKSBhcyBBcnJheTFEO1xuICAgIGNvbnN0IGxhYmVsID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmxhYmVsVGVuc29yKSBhcyBBcnJheTFEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3Qgc29mdG1heFJlc3VsdCA9IG1hdGguc29mdG1heChsb2dpdHMpO1xuXG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMuc29mdG1heFRlbnNvciwga2VlcChzb2Z0bWF4UmVzdWx0KSk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICBrZWVwKGNyb3NzRW50cm9weUNvc3QobWF0aCwgc29mdG1heFJlc3VsdCwgbGFiZWwsIHRoaXMuZXBzaWxvbikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBzb2Z0bWF4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnNvZnRtYXhUZW5zb3IpO1xuICAgIGNvbnN0IGxhYmVsID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmxhYmVsVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmxvZ2l0c1RlbnNvciwga2VlcChtYXRoLnN1Yihzb2Z0bWF4LCBsYWJlbCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2VUcmFuc2llbnRBcnJheXMoXG4gICAgICBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBpbmZlcmVuY2VBcnJheXMuZGlzcG9zZUFycmF5KHRoaXMuc29mdG1heFRlbnNvcik7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIHRoaXMuZXBzaWxvbi5kaXNwb3NlKCk7XG4gIH1cblxuICBwcml2YXRlIHNvZnRtYXhUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSBlcHNpbG9uID0gU2NhbGFyLm5ldygxZS01KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyb3NzRW50cm9weUNvc3QoXG4gICAgbWF0aDogTkRBcnJheU1hdGgsIHk6IEFycmF5MUQsIHRhcmdldDogQXJyYXkxRCwgZXBzaWxvbjogU2NhbGFyKTogU2NhbGFyIHtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB5LnNpemUgPT09IHRhcmdldC5zaXplLCAnVGhlIG91dHB1dCBhbmQgdGFyZ2V0IG11c3QgYmUgdGhlIHNhbWUgc2l6ZScpO1xuXG4gIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICBjb25zdCB5UGx1c0VwcyA9IG1hdGguc2NhbGFyUGx1c0FycmF5KGVwc2lsb24sIHkpO1xuICAgIGNvbnN0IGxvZ091dHB1dCA9IG1hdGgubG9nKHlQbHVzRXBzKTtcbiAgICBjb25zdCB0YXJMb2dPdXRwdXQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHRhcmdldCwgbG9nT3V0cHV0KTtcbiAgICBjb25zdCBjb3N0VmVjdG9yID0gbWF0aC5uZWcodGFyTG9nT3V0cHV0KTtcbiAgICByZXR1cm4gbWF0aC5zdW0oY29zdFZlY3Rvcik7XG4gIH0pO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIFNwbGl0IG9wcyBhcmUgdXNlZCB0byBhY2N1bXVsYXRlIGJhY2twcm9wIGRlcml2YXRpdmVzIHdoZW4gYSBub2RlJ3Mgb3V0cHV0XG4gKiB0ZW5zb3IgaXMgY29uc3VtZWQgYnkgbXVsdGlwbGUgbm9kZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcGxpdCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgaW5wdXQ6IFRlbnNvciwgcHJpdmF0ZSBvdXRwdXRzOiBUZW5zb3JbXSkge1xuICAgIHN1cGVyKCk7XG4gICAgb3V0cHV0cy5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGlucHV0LnNoYXBlLCBvdXRwdXQuc2hhcGUpO1xuICAgIH0pO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBpbnB1dEFycmF5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmlucHV0KTtcbiAgICB0aGlzLm91dHB1dHMuZm9yRWFjaChvdXRwdXQgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChvdXRwdXQsIGlucHV0QXJyYXkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGlmICghZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLmlucHV0KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCBkeCA9IG1hdGguYWRkKFxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dHB1dHNbMF0pLFxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dHB1dHNbMV0pKTtcbiAgICAgIC8vIFN1bSBhY3Jvc3MgYWxsIHRoZSBkZXJpdmF0aXZlcyBvZiB0aGUgY29uc3VtZXJzIG9mIHRoaXMgbm9kZS5cbiAgICAgIHRoaXMub3V0cHV0cy5zbGljZSgyKS5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICAgIGR4ID0gbWF0aC5hZGQoZHgsIGdyYWRpZW50QXJyYXlzLmdldChvdXRwdXQpKTtcbiAgICAgIH0pO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuaW5wdXQsIGtlZXAoZHgpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG5leHBvcnQgY2xhc3MgU3VidHJhY3QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIGR5U2l6ZVNjYWxhcjogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2Ugc3VidHJhY3Qgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yLCBwcml2YXRlIG91dFRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0MS5zaGFwZSwgdDIuc2hhcGUpLFxuICAgICAgICAnT25lIG9mIHQxIG9yIHQyIG11c3QgYmUgYSBzY2FsYXIsIG9yIHQxIGFuZCB0MiBtdXN0IGhhdmUgJyArXG4gICAgICAgICAgICAndGhlIHNhbWUgc2hhcGUnKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDEpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQyKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCByZXN1bHQ6IE5EQXJyYXk7XG4gICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQxLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhck1pbnVzQXJyYXkodDEsIHQyKTtcbiAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQyLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLmFycmF5TWludXNTY2FsYXIodDEsIHQyKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc3ViKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQxKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy50Mik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy50MSkpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLnQxLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMudDEsIGtlZXAobWF0aC5kaXZpZGUoc3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy50MSwga2VlcChkeSkpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMudDIpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy50Mi5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShkeSk7XG4gICAgICAgICAgY29uc3QgbmVnU3VtID0gbWF0aC5uZWcoc3VtKTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMudDIsIGtlZXAobWF0aC5kaXZpZGUobmVnU3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy50Miwga2VlcChtYXRoLm5lZyhkeSkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgIT0gbnVsbCkge1xuICAgICAgdGhpcy5keVNpemVTY2FsYXIuZGlzcG9zZSgpO1xuICAgIH1cbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7U2Vzc2lvblJ1bnRpbWV9IGZyb20gJy4vc2Vzc2lvbic7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgT3B0aW1pemVyIHtcbiAgcHJvdGVjdGVkIHZhcmlhYmxlTm9kZXM6IFZhcmlhYmxlTm9kZVtdO1xuICBwcm90ZWN0ZWQgc3BlY2lmaWVkVmFyaWFibGVOb2RlczogVmFyaWFibGVOb2RlW118bnVsbDtcblxuICBjb25zdHJ1Y3RvcihzcGVjaWZpZWRWYXJpYWJsZUxpc3Q/OiBOb2RlW10pIHtcbiAgICBpZiAoc3BlY2lmaWVkVmFyaWFibGVMaXN0ICE9IG51bGwpIHtcbiAgICAgIHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcyA9IHNwZWNpZmllZFZhcmlhYmxlTGlzdCBhcyBWYXJpYWJsZU5vZGVbXTtcbiAgICB9XG4gIH1cblxuICBhYnN0cmFjdCBiZWZvcmVCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGFic3RyYWN0IGFmdGVyRXhhbXBsZShcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgYWZ0ZXJCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGFic3RyYWN0IGRpc3Bvc2UoKTogdm9pZDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuLyoqXG4gKiBEZWZhdWx0IGNvbXBhcmlzb24gZnVuY3Rpb24gZm9yIHRoZSBwcmlvcml0eSBxdWV1ZS5cbiAqIEBwYXJhbSBhIFRoZSBmaXJzdCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0gYiBUaGUgc2Vjb25kIGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqIEByZXR1cm4gXCJhID4gYlwiIHJldHVybnMgPiAwLiBcImEgPCBiXCIgcmV0dXJucyA8IDAuIFwiYSA9PT0gYlwiIHJldHVybnMgMC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRDb21wYXJlPFQ+KGE6IFQsIGI6IFQpOiBudW1iZXIge1xuICBpZiAoYSA9PT0gYikge1xuICAgIHJldHVybiAwO1xuICB9IGVsc2UgaWYgKGEgPCBiKSB7XG4gICAgcmV0dXJuIC0xO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiAxO1xuICB9XG59XG5cbi8qKlxuICogQSBDb21wYXJhdG9yIGlzIGEgdXNlci1wcm92aWRlZCBmdW5jdGlvbiB0aGF0IGNvbXBhcmVzIHR3byBUIGluc3RhbmNlcy4gVGhlXG4gKiBjb252ZW50aW9uIGZvciBkZWZhdWx0Q29tcGFyZSBpcyBleHBlY3RlZCB0byBiZSBmb2xsb3dlZCB0byBtYWludGFpbiB0aGVcbiAqIGJpbmFyeSBtaW4taGVhcCBpbnRlZ3JpdHkuXG4gKiBAcGFyYW0gYSBUaGUgZmlyc3QgZWxlbWVudCB0byBjb21wYXJlLlxuICogQHBhcmFtIGIgVGhlIHNlY29uZCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKi9cbmV4cG9ydCB0eXBlIENvbXBhcmF0b3I8VD4gPSAoYTogVCwgYjogVCkgPT4gbnVtYmVyO1xuXG4vKipcbiAqIEluZGV4T2JzZXJ2ZXIgaXMgYSB1c2VyLXByb3ZpZGVkIGNhbGxiYWNrIHRoYXQgaW5mb3JtcyB0aGUgY2FsbGVyIHdoZW4gYW5cbiAqIGVsZW1lbnQgaW4gdGhlIHByaW9yaXR5IHF1ZXVlJ3MgYmluYXJ5IG1pbi1oZWFwIGhhcyBiZWVuIHJlbG9jYXRlZC5cbiAqIEBwYXJhbSB0IFRoZSBlbGVtZW50IHRoYXQgd2FzIHJlbG9jYXRlZC5cbiAqIEBwYXJhbSBuZXdJbmRleCBUaGUgbmV3IGxvY2F0aW9uIGluIHRoZSBiaW5hcnkgbWluLWhlYXAgb2YgdGhlIGVsZW1lbnQuXG4gKi9cbmV4cG9ydCB0eXBlIEluZGV4T2JzZXJ2ZXI8VD4gPSAodDogVCwgbmV3SW5kZXg6IG51bWJlcikgPT4gdm9pZDtcblxuLyoqXG4gKiBBIHByaW9yaXR5IHF1ZXVlLCBpbXBsZW1lbnRlZCBpbiB0ZXJtcyBvZiBhIGJpbmFyeSBtaW4taGVhcC4gTG93ZXIgcHJpb3JpdHlcbiAqIG51bWJlcnMgYXJlIGNvbnNpZGVyZWQgaGlnaGVyIHByaW9yaXR5LlxuICogZW5xdWV1ZSwgZGVxdWV1ZSwgYW5kIHVwZGF0ZSBhcmUgYWxsIE8obG9nIE4pIHdpdGggcmVzcGVjdCB0byB0aGUgbnVtYmVyIG9mXG4gKiBlbGVtZW50cyBpbiB0aGUgcXVldWUuXG4gKi9cbmV4cG9ydCBjbGFzcyBQcmlvcml0eVF1ZXVlPFQ+IHtcbiAgcHJpdmF0ZSBoZWFwOiBUW10gPSBbXTtcblxuICAvKipcbiAgICogQHBhcmFtIGNvbXBhcmF0b3IgQSBmdW5jdGlvbiB0aGF0IGNvbXBhcmVzIHR3byBxdWV1ZSBlbGVtZW50cy5cbiAgICogQHBhcmFtIGluZGV4T2JzZXJ2ZXIgQW4gb3B0aW9uYWwgY2FsbGJhY2sgcmFpc2VkIHdoZW4gdGhlIHByaW9yaXR5IHF1ZXVlXG4gICAqIGNoYW5nZXMgdGhlIG9yZGVyIG9mIGVsZW1lbnRzIGluIGl0cyBtaW4taGVhcC4gVXNlZnVsIGZvciB0cmFja2luZyB0aGVcbiAgICogcG9zaXRpb25zIG9mIGVsZW1lbnRzIHRoYXQgbmVlZCB1cGRhdGluZy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBjb21wYXJhdG9yOiBDb21wYXJhdG9yPFQ+LFxuICAgICAgcHJpdmF0ZSBpbmRleE9ic2VydmVyPzogSW5kZXhPYnNlcnZlcjxUPikge31cblxuICAvKipcbiAgICogQWRkIGFuIGVsZW1lbnQgdG8gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcGFyYW0gdCBUaGUgZWxlbWVudCB0byBlbnF1ZXVlLlxuICAgKi9cbiAgZW5xdWV1ZSh0OiBUKSB7XG4gICAgdGhpcy5oZWFwLnB1c2godCk7XG4gICAgdGhpcy5vbkluZGV4Q2hhbmdlZCh0LCB0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gICAgdGhpcy5zaWZ0VXAodGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZSBhbiBlbGVtZW50IGZyb20gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcmV0dXJuIFRoZSBlbGVtZW50IGluIHRoZSBwcmlvcml0eSBxdWV1ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5XG4gICAqIChsb3dlc3QgbnVtZXJpYyBwcmlvcml0eSB2YWx1ZSkuXG4gICAqL1xuICBkZXF1ZXVlKCk6IFQge1xuICAgIGlmICh0aGlzLmVtcHR5KCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZGVxdWV1ZSBjYWxsZWQgb24gZW1wdHkgcHJpb3JpdHkgcXVldWUuJyk7XG4gICAgfVxuICAgIGNvbnN0IHQgPSB0aGlzLmhlYXBbMF07XG4gICAgdGhpcy5zd2FwKDAsIHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICB0aGlzLmhlYXAucG9wKCk7XG4gICAgdGhpcy5zaWZ0RG93bigwKTtcbiAgICByZXR1cm4gdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIGFuIGVsZW1lbnQgYXQgdGhlIHNwZWNpZmllZCBpbmRleC4gVGhpcyBjYW4gYmUgYSBmdWxsIGVsZW1lbnRcbiAgICogcmVwbGFjZW1lbnQsIG9yIGl0IGNhbiBiZSBhbiBpbi1wbGFjZSB1cGRhdGUuIFRoZSBwcmlvcml0eSBpcyBhc3N1bWVkIHRvIGJlXG4gICAqIGNoYW5nZWQsIGFuZCB0aGUgaW50ZXJuYWwgc3RvcmFnZSBpcyB1cGRhdGVkLiBUaGlzIGZ1bmN0aW9uIGlzIG9ubHkgdXNlZnVsXG4gICAqIGlmIHRoZSBzdG9yYWdlIGluZGV4IG9mIHRoZSB1cGRhdGVkIGVsZW1lbnQgaXMga25vd247IGNvbnN0cnVjdCB0aGVcbiAgICogUHJpb3JpdHlRdWV1ZSB3aXRoIGFuIEluZGV4T2JzZXJ2ZXIgdG8gdHJhY2sgZWxlbWVudCBsb2NhdGlvbnMuXG4gICAqIEBwYXJhbSBuZXdUIFRoZSBuZXcgZWxlbWVudCB0byByZXBsYWNlIGluIHRoZSBwcmlvcml0eSBxdWV1ZS5cbiAgICogQHBhcmFtIGluZGV4IFRoZSBpbmRleCB0byBpbnNlcnQgdGhlIG5ldyBlbGVtZW50IGludG8uXG4gICAqL1xuICB1cGRhdGUobmV3VDogVCwgaW5kZXg6IG51bWJlcikge1xuICAgIC8qIElmIHRoZSBlbGVtZW50IGlzIGF0IHRoZSB2ZXJ5IGVuZCBvZiB0aGUgaGVhcCwgbm8gc2lmdGluZyBpcyBuZWNlc3NhcnksXG4gICAgICogaXQgY2FuIGJlIHNhZmVseSByZW1vdmVkLiAqL1xuICAgIGNvbnN0IGxhc3QgPSAoaW5kZXggPT09IHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICBpZiAoIWxhc3QpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIH1cbiAgICB0aGlzLmhlYXAucG9wKCk7XG4gICAgaWYgKCFsYXN0KSB7XG4gICAgICAvKiBUaGUgZWxlbWVudCBhdCAnaW5kZXgnIGhhcyBiZWVuIHJlbW92ZWQsIGFuZCByZXBsYWNlZCB3aXRoIHdoYXRldmVyIHdhc1xuICAgICAgICogYXQgdGhlIGVuZCBvZiB0aGUgaGVhcC4gU2luY2UgdGhhdCBlbGVtZW50IG1pZ2h0IGhhdmUgY29tZSBmcm9tIGFcbiAgICAgICAqIGRpZmZlcmVudCBzdWJ0cmVlIChhbmQgbm90IGJlIGEgZGlyZWN0IGRlc2NlbmRhbnQgb2YgdGhlIG5vZGUgYXRcbiAgICAgICAqICdpbmRleCcpLCB3ZSBtaWdodCBuZWVkIHRvIHNpZnQgdGhpcyBuZXcgdmFsdWUgdXAgaW5zdGVhZCBvZiBkb3duLiBUZXN0XG4gICAgICAgKiBib3RoIGRpcmVjdGlvbnMsIGFuZCBzaWZ0IHRvIHdoZXJldmVyIHRoZSBub2RlIG5lZWRzIHRvIGdvLlxuICAgICAgICovXG4gICAgICBpZiAodGhpcy5zaWZ0VXBJbmRleChpbmRleCkgIT09IC0xKSB7XG4gICAgICAgIHRoaXMuc2lmdFVwKGluZGV4KTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KSAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5zaWZ0RG93bihpbmRleCk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZW5xdWV1ZShuZXdUKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmVkaWNhdGUgZm9yIHRlc3Rpbmcgd2hldGhlciB0aGUgUHJpb3JpdHlRdWV1ZSBpcyBlbXB0eS5cbiAgICogQHJldHVybiBUcnVlIGlmIHRoZSBQcmlvcml0eVF1ZXVlIGlzIGVtcHR5LCBvdGhlcndpc2UgRmFsc2UuXG4gICAqL1xuICBlbXB0eSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5oZWFwLmxlbmd0aCA9PT0gMDtcbiAgfVxuXG4gIHByaXZhdGUgb25JbmRleENoYW5nZWQodDogVCwgbmV3SW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmluZGV4T2JzZXJ2ZXIpIHtcbiAgICAgIHRoaXMuaW5kZXhPYnNlcnZlcih0LCBuZXdJbmRleCk7XG4gICAgfVxuICB9XG5cbiAgLypcbiAgICogU3RhbmRhcmQgemVyby1pbmRleGVkIGJpbmFyeSBoZWFwIGFycmF5IGxheW91dDpcbiAgICogICBQYXJlbnQoTikgPSBGbG9vcigoTiAtIDEpIC8gMilcbiAgICogICBMZWZ0Q2hpbGQoTikgPSAoTiAqIDIpICsgMVxuICAgKiAgIFJpZ2h0Q2hpbGQoTikgPSAoTiAqIDIpICsgMlxuICAgKi9cblxuICBwcml2YXRlIGdldFBhcmVudEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmIChpbmRleCA9PT0gMCkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICByZXR1cm4gTWF0aC5mbG9vcigoaW5kZXggLSAxKSAvIDIpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRMZWZ0Q2hpbGRJbmRleChpbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBjYW5kaWRhdGUgPSBpbmRleCAqIDIgKyAxO1xuICAgIHJldHVybiBjYW5kaWRhdGUgPCB0aGlzLmhlYXAubGVuZ3RoID8gY2FuZGlkYXRlIDogLTE7XG4gIH1cblxuICBwcml2YXRlIGdldFJpZ2h0Q2hpbGRJbmRleChpbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBjYW5kaWRhdGUgPSBpbmRleCAqIDIgKyAyO1xuICAgIHJldHVybiBjYW5kaWRhdGUgPCB0aGlzLmhlYXAubGVuZ3RoID8gY2FuZGlkYXRlIDogLTE7XG4gIH1cblxuICBwcml2YXRlIHNpZnRVcEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IHBhcmVudEluZGV4ID0gdGhpcy5nZXRQYXJlbnRJbmRleChpbmRleCk7XG4gICAgaWYgKHBhcmVudEluZGV4ID09PSAtMSkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICBpZiAodGhpcy5jb21wYXJlKHBhcmVudEluZGV4LCBpbmRleCkgPiAwKSB7XG4gICAgICByZXR1cm4gcGFyZW50SW5kZXg7XG4gICAgfVxuICAgIHJldHVybiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgc2lmdFVwKGluZGV4OiBudW1iZXIpIHtcbiAgICBsZXQgc2lmdEluZGV4ID0gdGhpcy5zaWZ0VXBJbmRleChpbmRleCk7XG4gICAgd2hpbGUgKHNpZnRJbmRleCAhPT0gLTEpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgc2lmdEluZGV4KTtcbiAgICAgIGluZGV4ID0gc2lmdEluZGV4O1xuICAgICAgc2lmdEluZGV4ID0gdGhpcy5zaWZ0VXBJbmRleChpbmRleCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0RG93bkluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmIChpbmRleCA+PSB0aGlzLmhlYXAubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGxldCBsYXJnZXN0Q2hpbGRJbmRleCA9IGluZGV4O1xuICAgIGNvbnN0IGxlZnRDaGlsZEluZGV4ID0gdGhpcy5nZXRMZWZ0Q2hpbGRJbmRleChpbmRleCk7XG4gICAgaWYgKChsZWZ0Q2hpbGRJbmRleCAhPT0gLTEpICYmXG4gICAgICAgICh0aGlzLmNvbXBhcmUobGVmdENoaWxkSW5kZXgsIGxhcmdlc3RDaGlsZEluZGV4KSA8IDApKSB7XG4gICAgICBsYXJnZXN0Q2hpbGRJbmRleCA9IGxlZnRDaGlsZEluZGV4O1xuICAgIH1cbiAgICBjb25zdCByaWdodENoaWxkSW5kZXggPSB0aGlzLmdldFJpZ2h0Q2hpbGRJbmRleChpbmRleCk7XG4gICAgaWYgKChyaWdodENoaWxkSW5kZXggIT09IC0xKSAmJlxuICAgICAgICAodGhpcy5jb21wYXJlKHJpZ2h0Q2hpbGRJbmRleCwgbGFyZ2VzdENoaWxkSW5kZXgpIDwgMCkpIHtcbiAgICAgIGxhcmdlc3RDaGlsZEluZGV4ID0gcmlnaHRDaGlsZEluZGV4O1xuICAgIH1cbiAgICByZXR1cm4gKGxhcmdlc3RDaGlsZEluZGV4ID09PSBpbmRleCkgPyAtMSA6IGxhcmdlc3RDaGlsZEluZGV4O1xuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0RG93bihpbmRleDogbnVtYmVyKSB7XG4gICAgbGV0IHNpZnRJbmRleCA9IHRoaXMuc2lmdERvd25JbmRleChpbmRleCk7XG4gICAgd2hpbGUgKHNpZnRJbmRleCAhPT0gLTEpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgc2lmdEluZGV4KTtcbiAgICAgIGluZGV4ID0gc2lmdEluZGV4O1xuICAgICAgc2lmdEluZGV4ID0gdGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGNvbXBhcmUoYUluZGV4OiBudW1iZXIsIGJJbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5jb21wYXJhdG9yKHRoaXMuaGVhcFthSW5kZXhdLCB0aGlzLmhlYXBbYkluZGV4XSk7XG4gIH1cblxuICBwcml2YXRlIHN3YXAoYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgICBjb25zdCB0ZW1wID0gdGhpcy5oZWFwW2FdO1xuICAgIHRoaXMuaGVhcFthXSA9IHRoaXMuaGVhcFtiXTtcbiAgICB0aGlzLmhlYXBbYl0gPSB0ZW1wO1xuICAgIHRoaXMub25JbmRleENoYW5nZWQodGhpcy5oZWFwW2FdLCBhKTtcbiAgICB0aGlzLm9uSW5kZXhDaGFuZ2VkKHRoaXMuaGVhcFtiXSwgYik7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHcmFwaCwgTm9kZSwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgb3BlcmF0aW9uX2VtaXR0ZXIgZnJvbSAnLi9vcGVyYXRpb25fZW1pdHRlcic7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCAqIGFzIHNlc3Npb25fdXRpbCBmcm9tICcuL3Nlc3Npb25fdXRpbCc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEZlZWRFbnRyeSBhc3NvY2lhdGVzIGEgdGVuc29yIHdpdGggdXNlci1wcm92aWRlZCBOREFycmF5IGRhdGEuXG4gKi9cbmV4cG9ydCB0eXBlIEZlZWRFbnRyeSA9IHtcbiAgdGVuc29yOiBUZW5zb3IsXG4gIGRhdGE6IE5EQXJyYXl8SW5wdXRQcm92aWRlclxufTtcblxuLyoqXG4gKiBBIEZlZWREaWN0aW9uYXJ5IGhvbGRzIGEgbWFwIGZyb20gdGVuc29ycyB0byB1c2VyLXByb3ZpZGVkIE5EQXJyYXlzLiBGZWVkXG4gKiBkaWN0aW9uYXJpZXMgcmVwcmVzZW50IHRoZSAnZW50cnkgcG9pbnRzJyBvZiBldmFsdWF0aW9uLCBzaW5jZSBncmFwaCBub2Rlc1xuICogdGhhdCBhcmUgcmVwbGFjZWQgYnkgZmVlZHMgZG9uJ3QgbmVlZCB0byBoYXZlIHRoZWlyIGlucHV0IG5vZGVzIGV2YWx1YXRlZC5cbiAqIEZlZWQgZGljdGlvbmFyaWVzIHVzdWFsbHkgcHJvdmlkZSBOREFycmF5IGRhdGEgZm9yIFBsYWNlaG9sZGVyIG5vZGVzLCBidXQgYW55XG4gKiBub2RlIGluIHRoZSBncmFwaCBjYW4gYmUgcmVwbGFjZWQgYnkgYSBmZWVkIGRpY3Rpb25hcnkgZW50cnkuXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRmVlZERpY3Rpb25hcnkge1xuICBkaWN0OiB7W3RlbnNvcklEOiBudW1iZXJdOiBGZWVkRW50cnl9ID0ge307XG5cbiAgLyoqXG4gICAqIE9wdGlvbmFsbHkgY29uc3RydWN0IGEgRmVlZERpY3Rpb25hcnkgZnJvbSBhbiBhcnJheSBvZiBlbnRyaWVzLlxuICAgKiBAcGFyYW0gZmVlZEVudHJpZXMgT3B0aW9uYWwgYXJyYXkgb2YgRmVlZEVudHJ5IG9iamVjdHMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihmZWVkRW50cmllcz86IEZlZWRFbnRyeVtdKSB7XG4gICAgaWYgKGZlZWRFbnRyaWVzKSB7XG4gICAgICBmZWVkRW50cmllcy5mb3JFYWNoKGVudHJ5ID0+IHRoaXMuZGljdFtlbnRyeS50ZW5zb3IuaWRdID0gZW50cnkpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZW51bSBDb3N0UmVkdWN0aW9uIHtcbiAgTk9ORSxcbiAgU1VNLFxuICBNRUFOXG59XG5cbi8qKlxuICogQSBTZXNzaW9uIG1haW50YWlucyB0aGUgcnVudGltZSBzdGF0ZSByZXF1aXJlZCB0byBlZmZpY2llbnRseSBldmFsdWF0ZSBub2Rlcy5cbiAqIE9uIHRoZWlyIG93biwgZ3JhcGggb2JqZWN0cyBhcmUgdmVyeSBsaWdodHdlaWdodCBsb2dpY2FsIHRvcG9sb2dpZXM7IHRoZXlcbiAqIGhhdmUgbm8gcmVsYXRpb25zaGlwIHdpdGggdGhlIEdQVS4gU2Vzc2lvbnMgZW5jYXBzdWxhdGUgdGhlIGV2YWx1YXRpb24gb2ZcbiAqIG5vZGVzLCB0aGUgbWFuYWdlbWVudCBvZiBHUFUgcmVzb3VyY2VzLCB0aGUgY2FjaGluZyBvZiBldmFsdWF0aW9uIHBhdGhzLCBhbmRcbiAqIGFueXRoaW5nIGVsc2UgcmVxdWlyZWQgdG8gZXZhbHVhdGUgb3IgdHJhaW4gYSBuZXR3b3JrLlxuICovXG5leHBvcnQgY2xhc3MgU2Vzc2lvbiB7XG4gIC8qKlxuICAgKiBAcGFyYW0gZ3JhcGggVGhlIGdyYXBoIHRvIGFzc29jaWF0ZSB3aXRoIHRoaXMgU2Vzc2lvbi5cbiAgICogQHBhcmFtIG1hdGggVGhlIE5EQXJyYXlNYXRoIGludGVyZmFjZSB0aGF0IHRoaXMgU2Vzc2lvbiBzaG91bGQgdXNlLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBncmFwaDogR3JhcGgsIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGgpIHt9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2UgYWxsIHN5c3RlbSByZXNvdXJjZXMgYXNzb2NpYXRlZCB3aXRoIHRoaXMgU2Vzc2lvbi5cbiAgICovXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5hY3RpdmF0aW9uQXJyYXlNYXAuZGlzcG9zZSgpO1xuICAgIE9iamVjdC5rZXlzKHRoaXMucnVudGltZUNhY2hlKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBydW50aW1lID0gdGhpcy5ydW50aW1lQ2FjaGVba2V5XTtcbiAgICAgIGlmIChydW50aW1lLm9wZXJhdGlvbnMpIHtcbiAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZGlzcG9zZSgpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnJ1bnRpbWVDYWNoZSA9IHt9O1xuICAgIGlmICh0aGlzLmJhdGNoU2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmJhdGNoU2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHRoaXMub25lU2NhbGFyLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZSBhIGxpc3Qgb2YgdGVuc29ycywgdXNpbmcgdGhlIHByb3ZpZGVkIGZlZWQgZW50cmllcyB0byBwcm92aWRlXG4gICAqIHVwc3RyZWFtIE5EQXJyYXkgaW5wdXQuXG4gICAqIFdoZW4gdXNpbmcgYSBgTkRBcnJheU1hdGhgIG9iamVjdCBpbiBzYWZlIG1vZGUgdGhpcyBtdXN0IGJlIHVzZWQgaW4gYVxuICAgKiBtYXRoLnNjb3BlKCkuXG4gICAqIEBwYXJhbSB0ZW5zb3JzIFRoZSBsaXN0IG9mIHRlbnNvcnMgdG8gZXZhbHVhdGUuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBMaXN0IG9mIGBGZWVkRW50cnlgIHRvIHJlYWQgd2hlbiByZXBsYWNpbmcgZ3JhcGhcbiAgICogdGVuc29ycyB3aXRoIE5EQXJyYXlzLlxuICAgKiBAcmV0dXJuIFRoZSBjb21wdXRlZCB2YWx1ZXMgb2YgdGhlIHRlbnNvcnMuXG4gICAqL1xuICBldmFsQWxsKHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10pOiBOREFycmF5W10ge1xuICAgIHJldHVybiB0aGlzLm1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgY29uc3QgZmVlZCA9IG5ldyBGZWVkRGljdGlvbmFyeShmZWVkRW50cmllcyk7XG4gICAgICBjb25zdCBydW50aW1lID0gdGhpcy5nZXRPckNyZWF0ZVJ1bnRpbWUodGVuc29ycywgZmVlZCk7XG5cbiAgICAgIGNvbnN0IGFjdGl2YXRpb25zID0gdGhpcy5hY3RpdmF0aW9uQXJyYXlNYXA7XG5cbiAgICAgIHNlc3Npb25fdXRpbC5kaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbk91dHB1dHMoXG4gICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VUcmFuc2llbnRPcGVyYXRpb25BcnJheXMoXG4gICAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLCB0aGlzLmFjdGl2YXRpb25BcnJheU1hcCwgdGhpcy5ncmFkaWVudEFycmF5TWFwKTtcblxuICAgICAgc2Vzc2lvbl91dGlsLmFkZFBlcnNpc3RlbnRBcnJheXNUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcbiAgICAgIHNlc3Npb25fdXRpbC5sb2FkSW5wdXRzRnJvbUZlZWREaWN0aW9uYXJ5VG9UZW5zb3JBcnJheU1hcChcbiAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgcnVudGltZS5vcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZmVlZEZvcndhcmQodGhpcy5tYXRoLCBhY3RpdmF0aW9ucykpO1xuXG4gICAgICBjb25zdCByZXN1bHRzID0gdGVuc29ycy5tYXAoeCA9PiBhY3RpdmF0aW9ucy5nZXQoeCkpO1xuICAgICAgdGVuc29ycy5mb3JFYWNoKHggPT4gYWN0aXZhdGlvbnMuZGVsZXRlKHgpKTtcblxuICAgICAgc2Vzc2lvbl91dGlsLnJlbGVhc2VGZWVkRGljdGlvbmFyeUlucHV0c0Zyb21UZW5zb3JBcnJheU1hcChcbiAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgcmV0dXJuIHJlc3VsdHM7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGUgYSB0ZW5zb3IsIHVzaW5nIHRoZSBwcm92aWRlZCBmZWVkIGVudHJpZXMgdG8gcHJvdmlkZVxuICAgKiB1cHN0cmVhbSBOREFycmF5IGlucHV0LlxuICAgKlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3IgdG8gZXZhbHVhdGUuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBMaXN0IG9mIGBGZWVkRW50cnlgIHRvIHJlYWQgd2hlbiByZXBsYWNpbmcgZ3JhcGhcbiAgICogdGVuc29ycyB3aXRoIE5EQXJyYXlzLlxuICAgKiBAcmV0dXJuIFRoZSBjb21wdXRlZCB2YWx1ZSBvZiB0aGUgdGVuc29yLlxuICAgKi9cbiAgZXZhbCh0ZW5zb3I6IFRlbnNvciwgZmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdKTogTkRBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZXZhbEFsbChbdGVuc29yXSwgZmVlZEVudHJpZXMpWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWlucyBhIGJhdGNoLlxuICAgKiBSZXR1cm5zIGEgcmVkdWNlZCBjb3N0IGlmIHRoZSBjb3N0UmVkdWN0aW9uIHBhcmFtZXRlciBpcyBzZXQuXG4gICAqIFdoZW4gdXNpbmcgYSBgTkRBcnJheU1hdGhgIG9iamVjdCBpbiBzYWZlIG1vZGUgdGhpcyBtdXN0IGJlIHVzZWQgaW4gYVxuICAgKiBtYXRoLnNjb3BlKCkuXG4gICAqIEBwYXJhbSBjb3N0VGVuc29yIEEgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgY29zdCB0byBvcHRpbWl6ZS4gU2hvdWxkIGJlIGFcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0gZmVlZEVudHJpZXMgRmVlZCBlbnRyaWVzIGZvciB0aGlzIHRyYWluIHJ1bi4gUHJvdmlkZXMgaW5wdXRzLlxuICAgKiBAcGFyYW0gYmF0Y2hTaXplIEJhdGNoIHNpemUgZm9yIHRoaXMgdHJhaW4gbG9vcC5cbiAgICogQHBhcmFtIG9wdGltaXplciBBbiBvcHRpbWl6ZXIgdG8gcGVyZm9ybSB3ZWlnaHQgdXBkYXRlcy5cbiAgICogQHBhcmFtIGNvc3RSZWR1Y3Rpb24gQW4gb3B0aW9uIHRvIGFsbG93IHRoZSB1c2VyIHRvIGdldCBhIHN1bW1lZCwgYXZlcmFnZWQsXG4gICAqIG9yIG5vIGNvc3QgYmFjay5cbiAgICogQHJldHVybiBUaGUgcmVkdWNlZCBjb3N0LCBpZiBjb3N0IHJlZHVjdGlvbiBpcyBub3QgTk9ORS4gVGhlIHVzZXIgaXNcbiAgICogcmVzcG9uc2libGUgZm9yIGRpc3Bvc2luZyB0aGUgY29zdCBOREFycmF5IGJldHdlZW4gdHJhaW4gbG9vcHMuXG4gICAqL1xuICB0cmFpbihcbiAgICAgIGNvc3RUZW5zb3I6IFRlbnNvciwgZmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLCBiYXRjaFNpemU6IG51bWJlcixcbiAgICAgIG9wdGltaXplcjogT3B0aW1pemVyLCBjb3N0UmVkdWN0aW9uID0gQ29zdFJlZHVjdGlvbi5OT05FKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc1NjYWxhclNoYXBlKGNvc3RUZW5zb3Iuc2hhcGUpLFxuICAgICAgICAnQ29zdCB0ZW5zb3IgZm9yIHRyYWluaW5nIG11c3QgYmUgYSBzY2FsYXIgdmFsdWUuJyk7XG5cbiAgICBpZiAodGhpcy5wcmV2QmF0Y2hTaXplICE9PSBiYXRjaFNpemUpIHtcbiAgICAgIHRoaXMucHJldkJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICAgIHRoaXMuYmF0Y2hTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhiYXRjaFNpemUpO1xuICAgIH1cblxuICAgIGNvbnN0IGZlZWQgPSBuZXcgRmVlZERpY3Rpb25hcnkoZmVlZEVudHJpZXMpO1xuICAgIHNlc3Npb25fdXRpbC50aHJvd0lmRmVlZERpY3Rpb25hcnlDb250YWluc05EQXJyYXlzKGZlZWQpO1xuXG4gICAgY29uc3QgcnVudGltZSA9IHRoaXMuZ2V0T3JDcmVhdGVSdW50aW1lKFtjb3N0VGVuc29yXSwgZmVlZCk7XG4gICAgY29uc3QgaW5mZXJlbmNlT3BlcmF0aW9ucyA9IHJ1bnRpbWUub3BlcmF0aW9ucztcbiAgICBjb25zdCBiYWNrUHJvcE9wZXJhdGlvbnMgPSBydW50aW1lLm9wZXJhdGlvbnMuc2xpY2UoKS5yZXZlcnNlKCk7XG4gICAgY29uc3QgYWN0aXZhdGlvbnMgPSB0aGlzLmFjdGl2YXRpb25BcnJheU1hcDtcbiAgICBjb25zdCBncmFkaWVudHMgPSB0aGlzLmdyYWRpZW50QXJyYXlNYXA7XG4gICAgZ3JhZGllbnRzLnNldChjb3N0VGVuc29yLCB0aGlzLm9uZVNjYWxhcik7XG5cbiAgICBzZXNzaW9uX3V0aWwuYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcblxuICAgIG9wdGltaXplci5iZWZvcmVCYXRjaChcbiAgICAgICAgdGhpcy5tYXRoLCBiYXRjaFNpemUsIHJ1bnRpbWUsIGFjdGl2YXRpb25zLCBncmFkaWVudHMpO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoa2VlcCwgdHJhY2spID0+IHtcbiAgICAgIGxldCBjb3N0ID0gdHJhY2soU2NhbGFyLm5ldygwKSk7XG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYmF0Y2hTaXplOyArK2kpIHtcbiAgICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcbiAgICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uSW5wdXRHcmFkaWVudHMoXG4gICAgICAgICAgICBydW50aW1lLm5vZGVzLCBncmFkaWVudHMpO1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZVRyYW5zaWVudE9wZXJhdGlvbkFycmF5cyhcbiAgICAgICAgICAgIHJ1bnRpbWUub3BlcmF0aW9ucywgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICAgICAgc2Vzc2lvbl91dGlsLmxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgICAgZmVlZCwgYWN0aXZhdGlvbnMsIHRoaXMubWF0aCk7XG5cbiAgICAgICAgaW5mZXJlbmNlT3BlcmF0aW9ucy5mb3JFYWNoKFxuICAgICAgICAgICAgb3AgPT4gb3AuZmVlZEZvcndhcmQodGhpcy5tYXRoLCBhY3RpdmF0aW9ucykpO1xuICAgICAgICBiYWNrUHJvcE9wZXJhdGlvbnMuZm9yRWFjaChcbiAgICAgICAgICAgIG9wID0+IG9wLmJhY2tQcm9wKHRoaXMubWF0aCwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cykpO1xuXG4gICAgICAgIG9wdGltaXplci5hZnRlckV4YW1wbGUodGhpcy5tYXRoLCBydW50aW1lLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgICBzZXNzaW9uX3V0aWwucmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgICAgICAgICAgZmVlZCwgYWN0aXZhdGlvbnMsIHRoaXMubWF0aCk7XG5cbiAgICAgICAgY29zdCA9IHRoaXMudXBkYXRlQ29zdEZvckV4YW1wbGUoXG4gICAgICAgICAgICBjb3N0LCBhY3RpdmF0aW9ucy5nZXQoY29zdFRlbnNvciksIGNvc3RSZWR1Y3Rpb24pO1xuICAgICAgfVxuXG4gICAgICBvcHRpbWl6ZXIuYWZ0ZXJCYXRjaChcbiAgICAgICAgICB0aGlzLm1hdGgsIGJhdGNoU2l6ZSwgcnVudGltZSwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICAgIHJldHVybiB0aGlzLnVwZGF0ZUNvc3RGb3JCYXRjaChjb3N0LCBjb3N0UmVkdWN0aW9uKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlQ29zdEZvckV4YW1wbGUoXG4gICAgICB0b3RhbENvc3Q6IFNjYWxhciwgY3VyckNvc3Q6IFNjYWxhcixcbiAgICAgIGNvc3RSZWR1Y3Rpb246IENvc3RSZWR1Y3Rpb24pOiBTY2FsYXIge1xuICAgIGlmIChjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLk1FQU4gfHxcbiAgICAgICAgY29zdFJlZHVjdGlvbiA9PT0gQ29zdFJlZHVjdGlvbi5TVU0pIHtcbiAgICAgIHJldHVybiB0aGlzLm1hdGguYWRkKHRvdGFsQ29zdCwgY3VyckNvc3QpO1xuICAgIH1cbiAgICByZXR1cm4gdG90YWxDb3N0O1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVDb3N0Rm9yQmF0Y2godG90YWxDb3N0OiBTY2FsYXIsIGNvc3RSZWR1Y3Rpb246IENvc3RSZWR1Y3Rpb24pOlxuICAgICAgU2NhbGFyIHtcbiAgICBpZiAoY29zdFJlZHVjdGlvbiA9PT0gQ29zdFJlZHVjdGlvbi5NRUFOKSB7XG4gICAgICByZXR1cm4gdGhpcy5tYXRoLmRpdmlkZSh0b3RhbENvc3QsIHRoaXMuYmF0Y2hTaXplU2NhbGFyKTtcbiAgICB9XG4gICAgcmV0dXJuIHRvdGFsQ29zdDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0T3JDcmVhdGVSdW50aW1lKHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkOiBGZWVkRGljdGlvbmFyeSk6XG4gICAgICBTZXNzaW9uUnVudGltZSB7XG4gICAgY29uc3Qga2V5ID0gdGhpcy5tYWtlUnVudGltZUNhY2hlS2V5KHRlbnNvcnMsIGZlZWQpO1xuICAgIGxldCBydW50aW1lID0gdGhpcy5ydW50aW1lQ2FjaGVba2V5XTtcbiAgICBpZiAocnVudGltZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBsZXQgbm9kZXMgPVxuICAgICAgICAgIHNlc3Npb25fdXRpbC5nZXRPcmRlcmVkRXZhbHVhdGlvblNldEZyb21FdmFsVGVuc29yKHRlbnNvcnMsIGZlZWQpO1xuICAgICAgLy8gSW4gaW5mZXJlbmNlIG1vZGUgc3BsaXQgbm9kZXMgYXJlIG5vdCBuZWVkZWQsIGJ1dCB0aGVpciBjb3N0IGlzXG4gICAgICAvLyBuZWdsaWdpYmxlLCBhbmQgYWx3YXlzIGFkZGluZyB0aGVtIGluIGFsbG93cyBmb3IgY2FjaGluZyBvZiAxIHJ1bnRpbWVcbiAgICAgIC8vIGZvciBib3RoIHRyYWluL2V2YWwuXG4gICAgICBub2RlcyA9IHNlc3Npb25fdXRpbC5hZGRTcGxpdE5vZGVzKG5vZGVzKTtcbiAgICAgIHNlc3Npb25fdXRpbC5yZW1vdmVGZWVkRGljdGlvbmFyeU5vZGVzRnJvbUV2YWx1YXRpb25TZXQoZmVlZCwgbm9kZXMpO1xuICAgICAgc2Vzc2lvbl91dGlsLnRocm93RXJyb3JJZkV2YWx1YXRpb25TZXRDb250YWluc1BsYWNlaG9sZGVyTm9kZXMobm9kZXMpO1xuICAgICAgY29uc3Qgb3BlcmF0aW9ucyA9IG9wZXJhdGlvbl9lbWl0dGVyLmVtaXRGcm9tR3JhcGhOb2Rlcyhub2Rlcyk7XG4gICAgICBydW50aW1lID0ge25vZGVzLCBvcGVyYXRpb25zfTtcbiAgICAgIHRoaXMucnVudGltZUNhY2hlW2tleV0gPSBydW50aW1lO1xuICAgIH1cblxuICAgIHJldHVybiBydW50aW1lO1xuICB9XG5cbiAgcHJpdmF0ZSBtYWtlUnVudGltZUNhY2hlS2V5KHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkOiBGZWVkRGljdGlvbmFyeSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRlbnNvcnMubWFwKHggPT4geC5pZCkuc29ydCgpLmpvaW4oJ18nKSArICdfXycgK1xuICAgICAgICBPYmplY3Qua2V5cyhmZWVkLmRpY3QpLnNvcnQoKS5qb2luKCdfJyk7XG4gIH1cblxuICAvKiogTWFwcyBlYWNoIG91dHB1dCB0ZW5zb3Igb2YgdGhlIGdyYXBoIHRvIGl0cyBhY3RpdmF0aW9uIHZhbHVlLiAqL1xuICBhY3RpdmF0aW9uQXJyYXlNYXAgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgLyoqIE1hcHMgZWFjaCB0ZW5zb3Igb2YgdGhlIGdyYXBoIHRvIGl0cyBkZXJpdmF0aXZlIHdydCB0aGUgY29zdCBmdW5jdGlvbi4gKi9cbiAgZ3JhZGllbnRBcnJheU1hcCA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICBwcml2YXRlIHJ1bnRpbWVDYWNoZToge1trZXk6IHN0cmluZ106IFNlc3Npb25SdW50aW1lfSA9IHt9O1xuICAvKiogQmF0Y2ggc2l6ZSBvZiB0aGUgcHJldmlvdXMgdHJhaW4oKSBjYWxsLiAqL1xuICBwcml2YXRlIHByZXZCYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBiYXRjaFNpemVTY2FsYXI6IFNjYWxhcjtcbiAgcHJpdmF0ZSBvbmVTY2FsYXIgPSBTY2FsYXIubmV3KDEpO1xufVxuXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IHR5cGUgU2Vzc2lvblJ1bnRpbWUgPSB7XG4gIG5vZGVzOiBOb2RlW107IG9wZXJhdGlvbnM6IE9wZXJhdGlvbltdO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtDb25zdGFudE5vZGUsIE5vZGUsIFBsYWNlaG9sZGVyTm9kZSwgU3BsaXROb2RlLCBUZW5zb3IsIFZhcmlhYmxlTm9kZX0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge0lucHV0UHJvdmlkZXJ9IGZyb20gJy4vaW5wdXRfcHJvdmlkZXInO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtGZWVkRGljdGlvbmFyeX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBhcnJheSBvZiBncmFwaCBub2RlcyB0aGF0IHN0b3AgdHJhdmVyc2FsLCBiYXNlZCBvbiB0aGUgY29udGVudHNcbiAqIG9mIHRoZSBwcm92aWRlZCBGZWVkRGljdGlvbmFyeS4gVGhpcyBpcyBhIHNpbXBsZSAxOjEgZXh0cmFjdGlvbiBvZiBub2RlcyBmcm9tXG4gKiB0aGUgRmVlZERpY3Rpb25hcnkuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGZlZWREaWN0aW9uYXJ5IFRoZSBGZWVkRGljdGlvbmFyeSB0byBzY2FuIGZvciB0ZXJtaW5hdGlvbiBub2Rlcy5cbiAqIEByZXR1cm4gYW4gYXJyYXkgb2YgTm9kZXMgd2hpY2ggaGFsdCB0cmF2ZXJzYWwgd2hlbiB2aXNpdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGVybWluYXRpbmdOb2Rlc0Zyb21GZWVkRGljdGlvbmFyeShcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpOiBOb2RlW10ge1xuICByZXR1cm4gT2JqZWN0LmtleXMoZmVlZERpY3Rpb25hcnkuZGljdClcbiAgICAgIC5tYXAodGVuc29ySUQgPT4gZmVlZERpY3Rpb25hcnkuZGljdFsrdGVuc29ySURdLnRlbnNvci5ub2RlKTtcbn1cblxuLyoqXG4gKiBHaXZlbiBhIHRlbnNvciBhbmQgYSBmZWVkIGRpY3Rpb25hcnksIGNvbXB1dGVzIHRoZSBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvXG4gKiBiZSBldmFsdWF0ZWQgdG8gcGVyZm9ybSBpbmZlcmVuY2UuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWxUZW5zb3JzIFRoZSBsaXN0IG9mIHRlbnNvcnMgdG8gZXZlbnR1YWxseSBiZSBldmFsdWF0ZWQuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIHBvcHVsYXRlZCBmZWVkIGRpY3Rpb25hcnkuXG4gKiBAcmV0dXJuIFRoZSBzZXQgb2Ygbm9kZXMgdG8gZXZhbHVhdGUsIGluIGV2YWx1YXRpb24gb3JkZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRlcmVkRXZhbHVhdGlvblNldEZyb21FdmFsVGVuc29yKFxuICAgIGV2YWxUZW5zb3JzOiBUZW5zb3JbXSwgZmVlZERpY3Rpb25hcnk6IEZlZWREaWN0aW9uYXJ5KTogTm9kZVtdIHtcbiAgY29uc3QgdGVybWluYXRpbmdOb2RlcyA9XG4gICAgICBnZXRUZXJtaW5hdGluZ05vZGVzRnJvbUZlZWREaWN0aW9uYXJ5KGZlZWREaWN0aW9uYXJ5KTtcbiAgY29uc3QgZXZhbE5vZGVzID0gZXZhbFRlbnNvcnMubWFwKHggPT4geC5ub2RlKTtcbiAgY29uc3QgdW5vcmRlcmVkRXZhbHVhdGlvblNldCA9XG4gICAgICBncmFwaF91dGlsLmdldFVub3JkZXJlZEV2YWx1YXRpb25TZXQoZXZhbE5vZGVzLCB0ZXJtaW5hdGluZ05vZGVzKTtcbiAgY29uc3Qgb3JkZXJlZEV2YWx1YXRpb25TZXQgPVxuICAgICAgZ3JhcGhfdXRpbC5nZXRPcmRlcmVkRXZhbHVhdGlvblNldCh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0KTtcbiAgcmV0dXJuIG9yZGVyZWRFdmFsdWF0aW9uU2V0O1xufVxuXG4vKipcbiAqIFRyYXZlcnNlcyB0aGUgcHJvdmlkZWQgbm9kZSBhcnJheSBhbmQgYWRkcyBhbGwgcGVyc2lzdGVudCBub2RlIE5EQXJyYXlzIHRvXG4gKiB0aGUgcHJvdmlkZWQgVGVuc29yQXJyYXlNYXAuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWx1YXRpb25TZXQgVGhlIGFycmF5IG9mIG5vZGVzIHRvIHNjYW4uXG4gKiBAcGFyYW0gdGVuc29yQXJyYXlNYXAgVGhlIG1hcCB0aGF0IHJlY2VpdmVzIHRoZSBOREFycmF5cyBmcm9tIHBlcnNpc3RlbnRcbiAqIG5vZGVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdLCB0ZW5zb3JBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgVmFyaWFibGVOb2RlIHx8IG5vZGUgaW5zdGFuY2VvZiBDb25zdGFudE5vZGUpIHtcbiAgICAgIHRlbnNvckFycmF5TWFwLnNldChub2RlLm91dHB1dCwgbm9kZS5kYXRhKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFZhcmlhYmxlTm9kZXNGcm9tRXZhbHVhdGlvblNldChldmFsdWF0aW9uU2V0OiBOb2RlW10pOlxuICAgIFZhcmlhYmxlTm9kZVtdIHtcbiAgY29uc3Qgbm9kZXM6IFZhcmlhYmxlTm9kZVtdID0gW107XG4gIGV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHtcbiAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFZhcmlhYmxlTm9kZSkge1xuICAgICAgbm9kZXMucHVzaChub2RlKTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gbm9kZXM7XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gdGhyb3dJZkZlZWREaWN0aW9uYXJ5Q29udGFpbnNOREFycmF5cyhcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpIHtcbiAgT2JqZWN0LmtleXMoZmVlZERpY3Rpb25hcnkuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgaWYgKGZlZWREaWN0aW9uYXJ5LmRpY3RbK3RlbnNvcklEXS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICd0cmFpbmluZyByZXF1aXJlcyBGZWVkRGljdGlvbmFyeSBlbnRyaWVzIHRvIGJlIElucHV0UHJvdmlkZXJzJyArXG4gICAgICAgICAgJ2FuZCBub3QgTkRBcnJheXMuJyk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkSW5wdXRzRnJvbUZlZWREaWN0aW9uYXJ5VG9UZW5zb3JBcnJheU1hcChcbiAgICBiYXRjaEZlZWQ6IEZlZWREaWN0aW9uYXJ5LCBhY3RpdmF0aW9uczogVGVuc29yQXJyYXlNYXAsIG1hdGg6IE5EQXJyYXlNYXRoKSB7XG4gIE9iamVjdC5rZXlzKGJhdGNoRmVlZC5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICBjb25zdCBmZWVkRW50cnkgPSBiYXRjaEZlZWQuZGljdFsrdGVuc29ySURdO1xuXG4gICAgbGV0IGRhdGE6IE5EQXJyYXk7XG4gICAgaWYgKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgZGF0YSA9IGZlZWRFbnRyeS5kYXRhIGFzIE5EQXJyYXk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHByb3ZpZGVyID0gZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcjtcbiAgICAgIGRhdGEgPSBwcm92aWRlci5nZXROZXh0Q29weShtYXRoKTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbChmZWVkRW50cnkudGVuc29yLnNoYXBlLCBkYXRhLnNoYXBlKSxcbiAgICAgICAgYEVycm9yIGxvYWRpbmcgRmVlZEVudHJ5OiBmZWVkaW5nIE5EQXJyYXkgb2Ygc2hhcGUgJHtkYXRhLnNoYXBlfSBgICtcbiAgICAgICAgICAgIGBkb2VzIG5vdCBtYXRjaCBUZW5zb3IgKGlkOiAke2ZlZWRFbnRyeS50ZW5zb3IuaWR9KSBzaGFwZTogYCArXG4gICAgICAgICAgICBgJHtmZWVkRW50cnkudGVuc29yLnNoYXBlfS5gKTtcbiAgICBhY3RpdmF0aW9ucy5zZXQoZmVlZEVudHJ5LnRlbnNvciwgZGF0YSk7XG4gIH0pO1xufVxuXG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgIGJhdGNoRmVlZDogRmVlZERpY3Rpb25hcnksIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCwgbWF0aDogTkRBcnJheU1hdGgpIHtcbiAgT2JqZWN0LmtleXMoYmF0Y2hGZWVkLmRpY3QpLmZvckVhY2godGVuc29ySUQgPT4ge1xuICAgIGNvbnN0IGZlZWRFbnRyeSA9IGJhdGNoRmVlZC5kaWN0Wyt0ZW5zb3JJRF07XG5cbiAgICBpZiAoIShmZWVkRW50cnkuZGF0YSBpbnN0YW5jZW9mIE5EQXJyYXkpKSB7XG4gICAgICBjb25zdCBwcm92aWRlciA9IGZlZWRFbnRyeS5kYXRhIGFzIElucHV0UHJvdmlkZXI7XG5cbiAgICAgIGNvbnN0IGZlZWRFbnRyeUFycmF5ID0gYWN0aXZhdGlvbnMuZ2V0KGZlZWRFbnRyeS50ZW5zb3IpO1xuICAgICAgcHJvdmlkZXIuZGlzcG9zZUNvcHkobWF0aCwgZmVlZEVudHJ5QXJyYXkpO1xuICAgIH1cblxuICAgIGFjdGl2YXRpb25zLmRlbGV0ZShmZWVkRW50cnkudGVuc29yKTtcbiAgfSk7XG59XG5cbi8qKlxuICogUmVtb3ZlcyBhbGwgbm9kZXMgZnJvbSB0aGUgcHJvdmlkZWQgTm9kZSBhcnJheSB3aG9zZSBvdXRwdXQgdGVuc29ycyBleGlzdCBpblxuICogdGhlIHByb3ZpZGVkIGZlZWQgZGljdGlvbmFyeS4gQWZ0ZXIgY2FsbGluZyB0aGlzLCB0aGUgTm9kZSBhcnJheSBzaG91bGRcbiAqIGNvbnRhaW4gemVybyBQbGFjZWhvbGRlciBub2Rlcywgb3IgdGhlIHVzZXIgaGFzIGZhaWxlZCB0byBwcm92aWRlIGEgZmVlZCBmb3JcbiAqIGEgUGxhY2Vob2xkZXIgbm9kZS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIEZlZWREaWN0aW9uYXJ5IHRvIHByb2Nlc3MuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gcmVtb3ZlIGlucHV0IG5vZGVzIGZyb20uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVGZWVkRGljdGlvbmFyeU5vZGVzRnJvbUV2YWx1YXRpb25TZXQoXG4gICAgZmVlZERpY3Rpb25hcnk6IEZlZWREaWN0aW9uYXJ5LCBldmFsdWF0aW9uU2V0OiBOb2RlW10pIHtcbiAgbGV0IGkgPSAwO1xuICB3aGlsZSAoaSA8IGV2YWx1YXRpb25TZXQubGVuZ3RoKSB7XG4gICAgY29uc3Qgbm9kZSA9IGV2YWx1YXRpb25TZXRbaV07XG4gICAgaWYgKGZlZWREaWN0aW9uYXJ5LmRpY3Rbbm9kZS5vdXRwdXQuaWRdICE9IG51bGwpIHtcbiAgICAgIGV2YWx1YXRpb25TZXQuc3BsaWNlKGksIDEpO1xuICAgIH0gZWxzZSB7XG4gICAgICArK2k7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogRGlzcG9zZXMgYW55IE5EQXJyYXlzIG9uIHRoZSB0ZW5zb3JBcnJheU1hcCBmcm9tIG9wZXJhdGlvbiBvdXRwdXRzIGFuZCBzZXRzXG4gKiB0aGUgdmFsdWUgdG8gbnVsbC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgc2V0IG9mIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSB0ZW5zb3JBcnJheU1hcCBUaGUgbWFwIHRvIGRpc3Bvc2UgYW5kIGluaXRpYWxpemUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbk91dHB1dHMoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdLCB0ZW5zb3JBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmICghZ3JhcGhfdXRpbC5pc0lucHV0Tm9kZShub2RlKSkge1xuICAgICAgaWYgKCFncmFwaF91dGlsLmlzUGFzc3Rocm91Z2hOb2RlKG5vZGUsIHRlbnNvckFycmF5TWFwKSkge1xuICAgICAgICB0ZW5zb3JBcnJheU1hcC5kaXNwb3NlQXJyYXkobm9kZS5vdXRwdXQpO1xuICAgICAgfVxuICAgICAgdGVuc29yQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBudWxsKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIERpc3Bvc2VzIGFueSBOREFycmF5cyBvbiB0aGUgdGVuc29yQXJyYXlNYXAgZnJvbSBkZXJpdmF0aXZlcyBvZiBvcGVyYXRpb25cbiAqIGlucHV0cyBhbmQgc2V0cyB0aGUgdmFsdWUgdG8gbnVsbC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgc2V0IG9mIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBncmFkaWVudHMgVGhlIGdyYWRpZW50IG1hcCB0byBkaXNwb3NlIGFuZCBpbml0aWFsaXplLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25JbnB1dEdyYWRpZW50cyhcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIGdyYWRpZW50czogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKS5mb3JFYWNoKGlucHV0TmFtZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dCA9IG5vZGUuaW5wdXRzW2lucHV0TmFtZV07XG4gICAgICBpZiAoZ3JhZGllbnRzLmdldChpbnB1dCwgdHJ1ZSkgIT09IGdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQsIHRydWUpKSB7XG4gICAgICAgIGdyYWRpZW50cy5kaXNwb3NlQXJyYXkoaW5wdXQpO1xuICAgICAgfVxuICAgICAgZ3JhZGllbnRzLnNldChpbnB1dCwgbnVsbCk7XG4gICAgfSk7XG4gIH0pO1xufVxuXG5cbi8qKlxuICogQ2FsbHMgdW5kZXJseWluZyBvcGVyYXRpb24gZGlzcG9zZVRyYW5zaWVudEFycmF5cyBtZXRob2RzIHdoaWNoIGNsZWFuIHVwIGFueVxuICogTkRBcnJheXMgd2hpY2ggb3BlcmF0aW9ucyBtYXkgaGF2ZSBjcmVhdGVkIGR1cmluZyBhIHJ1bi5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gb3BlcmF0aW9uTm9kZXMgVGhlIGFycmF5IG9mIE5vZGVzIHRvIHRyYXZlcnNlLlxuICogQHBhcmFtIG91dHB1dFRlbnNvciBUaGUgdGVuc29yIGJlaW5nIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBtYXAgVGhlIFRlbnNvckFycmF5TWFwIHRvIG9wZXJhdGUgb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlVHJhbnNpZW50T3BlcmF0aW9uQXJyYXlzKFxuICAgIG9wZXJhdGlvbnM6IE9wZXJhdGlvbltdLCBhY3RpdmF0aW9uczogVGVuc29yQXJyYXlNYXAsXG4gICAgZ3JhZGllbnRzOiBUZW5zb3JBcnJheU1hcCkge1xuICBvcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZGlzcG9zZVRyYW5zaWVudEFycmF5cyhhY3RpdmF0aW9ucywgZ3JhZGllbnRzKSk7XG59XG5cbi8qKlxuICogSXRlcmF0ZXMgdGhlIHByb3ZpZGVkIE5vZGUgYXJyYXkgYW5kIHRocm93cyBhbiBleGNlcHRpb24gaWYgdGhlcmUgYXJlIGFueVxuICogUGxhY2Vob2xkZXIgbm9kZXMgcHJlc2VudC4gQ2FsbCBhZnRlciB0aGUgZXZhbHVhdGlvbiBzZXQgaGFzIGJlZW4gcHJ1bmVkIHdpdGhcbiAqIHRoZSBhY2NvbXBhbnlpbmcgRmVlZERpY3Rpb25hcnkgdG8gZW5zdXJlIHRoYXQgYWxsIGlucHV0cyBoYXZlIGJlZW4gcmVzb2x2ZWQuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWx1YXRpb25TZXQgVGhlIGFycmF5IG9mIG5vZGVzIHRvIHNjYW4uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0aHJvd0Vycm9ySWZFdmFsdWF0aW9uU2V0Q29udGFpbnNQbGFjZWhvbGRlck5vZGVzKFxuICAgIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKG5vZGUgaW5zdGFuY2VvZiBQbGFjZWhvbGRlck5vZGUpIHtcbiAgICAgIGNvbnN0IHNoYXBlID0gJ1snICsgbm9kZS5vdXRwdXQuc2hhcGUuam9pbignLCAnKSArICddJztcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnUGxhY2Vob2xkZXIgbm9kZSBcIicgKyBub2RlLm5hbWUgKyAnXCIgJyArIHNoYXBlICtcbiAgICAgICAgICAnIG5vdCBwcmVzZW50IGluIGZlZWQgZGljdGlvbmFyeS4nKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEluamVjdHMgc3BsaXRzIG5vZGVzIGFmdGVyIGV2ZXJ5IG5vZGUgdGhhdCBoYXMgbXVsdGlwbGUgY29uc3VtZXJzLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBub2RlcyBUaGUgbm9kZSBsaXN0IGluIGV2YWx1YXRpb24gb3JkZXIuXG4gKiBAcmV0dXJuIFRoZSBub2RlIGxpc3Qgd2l0aCBzcGxpdCBub2RlcyBpbmplY3RlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFkZFNwbGl0Tm9kZXMobm9kZXM6IE5vZGVbXSk6IE5vZGVbXSB7XG4gIGNvbnN0IG5vZGVJZFRvTnVtQ29uc3VtZXJzOiBudW1iZXJbXSA9IFtdO1xuICBjb25zdCBub2RlSWRUb1NwbGl0Tm9kZToge1tub2RlSWQ6IG51bWJlcl06IFNwbGl0Tm9kZX0gPSB7fTtcblxuICAvLyBGaW5kIG5vZGVzIHRoYXQgaGF2ZSBtdWx0aXBsZSBjb25zdW1lcnMuXG4gIG5vZGVzLmZvckVhY2gobm9kZSA9PiB7XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKTtcbiAgICBrZXlzLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VGVuc29yID0gbm9kZS5pbnB1dHNba2V5XTtcbiAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRUZW5zb3Iubm9kZTtcbiAgICAgIGlmIChub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPT0gbnVsbCkge1xuICAgICAgICBub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPSAwO1xuICAgICAgfVxuICAgICAgbm9kZUlkVG9OdW1Db25zdW1lcnNbaW5wdXQuaWRdKys7XG4gICAgICBpZiAobm9kZUlkVG9OdW1Db25zdW1lcnNbaW5wdXQuaWRdID4gMSAmJlxuICAgICAgICAgIG5vZGVJZFRvU3BsaXROb2RlW2lucHV0LmlkXSA9PSBudWxsKSB7XG4gICAgICAgIG5vZGVJZFRvU3BsaXROb2RlW2lucHV0LmlkXSA9IG5ldyBTcGxpdE5vZGUoaW5wdXQuZ3JhcGgsIGlucHV0VGVuc29yKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG5cbiAgLy8gSW5qZWN0IGEgc3BsaXQgbm9kZSBhZnRlciBlYWNoIG5vZGUgdGhhdCBoYXMgbXVsdGlwbGUgY29uc3VtZXJzIGFuZFxuICAvLyByZXdpcmUgdGhlIGlucHV0cyBvZiB0aGUgY29uc3VtZXJzIHRvIGNvbnN1bWUgdGhlIG91dHB1dCB0ZW5zb3JzIG9mIHRoZVxuICAvLyBzcGxpdCBub2RlIGluc3RlYWQgb2YgdGhlIG9yaWdpbmFsIG5vZGUuIEVhY2ggY29uc3VtZXIgY29uc3VtZXMgYVxuICAvLyBkaWZmZXJlbnQgb3V0cHV0IHRlbnNvciBzbyB0aGF0IGRlcml2YXRpdmVzIGFyZSBub3Qgb3ZlcndyaXR0ZW4uXG4gIC8vIHgtLT55ICBiZWNvbWVzIHgtLT5zLS0+eSAgIHdoZXJlIHkgY29uc3VtZXMgdGhlIDFzdCBvdXRwdXQgdGVuc29yIG9mIHNcbiAgLy8gfC0tPnogICAgICAgICAgICAgIHwtLT56ICAgICBhbmQgeiBjb25zdW1lcyB0aGUgMm5kIG91dHB1dCB0ZW5zb3Igb2Ygc1xuICBjb25zdCBuZXdOb2RlczogTm9kZVtdID0gW107XG4gIG5vZGVzLmZvckVhY2gobm9kZSA9PiB7XG4gICAgbmV3Tm9kZXMucHVzaChub2RlKTtcbiAgICBpZiAobm9kZS5pZCBpbiBub2RlSWRUb1NwbGl0Tm9kZSkge1xuICAgICAgY29uc3Qgc3BsaXROb2RlID0gbm9kZUlkVG9TcGxpdE5vZGVbbm9kZS5pZF07XG4gICAgICBuZXdOb2Rlcy5wdXNoKHNwbGl0Tm9kZSk7XG4gICAgfVxuICAgIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhub2RlLmlucHV0cyk7XG4gICAga2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFRlbnNvciA9IG5vZGUuaW5wdXRzW2tleV07XG4gICAgICBjb25zdCBpbnB1dElkID0gaW5wdXRUZW5zb3Iubm9kZS5pZDtcbiAgICAgIGlmIChpbnB1dElkIGluIG5vZGVJZFRvU3BsaXROb2RlKSB7XG4gICAgICAgIG5vZGUuaW5wdXRzW2tleV0gPSBub2RlSWRUb1NwbGl0Tm9kZVtpbnB1dElkXS5nZXROZXdPdXRwdXRUZW5zb3IoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG4gIHJldHVybiBuZXdOb2Rlcztcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOb2RlLCBUZW5zb3IsIFZhcmlhYmxlTm9kZX0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCB7U2Vzc2lvblJ1bnRpbWV9IGZyb20gJy4vc2Vzc2lvbic7XG5pbXBvcnQgKiBhcyBzZXNzaW9uX3V0aWwgZnJvbSAnLi9zZXNzaW9uX3V0aWwnO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuZXhwb3J0IGNsYXNzIFNHRE9wdGltaXplciBleHRlbmRzIE9wdGltaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbGVhcm5pbmdSYXRlOiBudW1iZXIsIHNwZWNpZmllZFZhcmlhYmxlTGlzdD86IE5vZGVbXSkge1xuICAgIHN1cGVyKHNwZWNpZmllZFZhcmlhYmxlTGlzdCk7XG4gIH1cblxuICBiZWZvcmVCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRoaXMudmFyaWFibGVOb2RlcyA9IHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcyA9PSBudWxsID9cbiAgICAgICAgc2Vzc2lvbl91dGlsLmdldFZhcmlhYmxlTm9kZXNGcm9tRXZhbHVhdGlvblNldChydW50aW1lLm5vZGVzKSA6XG4gICAgICAgIHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcztcbiAgICBpZiAoYmF0Y2hTaXplICE9PSB0aGlzLnByZXZCYXRjaFNpemUpIHtcbiAgICAgIHRoaXMucHJldkJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICAgIHRoaXMuYyA9IFNjYWxhci5uZXcoLXRoaXMubGVhcm5pbmdSYXRlIC8gYmF0Y2hTaXplKTtcbiAgICB9XG4gICAgdGhpcy52YXJpYWJsZU5vZGVzLmZvckVhY2goXG4gICAgICAgIG5vZGUgPT4gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5zZXQoXG4gICAgICAgICAgICBub2RlLm91dHB1dCwgTkRBcnJheS56ZXJvcyhub2RlLm91dHB1dC5zaGFwZSkpKTtcbiAgfVxuXG4gIGFmdGVyRXhhbXBsZShcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgdGhpcy52YXJpYWJsZU5vZGVzIS5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICBjb25zdCBncmFkaWVudCA9IGdyYWRpZW50QXJyYXlNYXAuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgY29uc3QgYWNjdW11bGF0ZWRHcmFkaWVudCA9IHRoaXMudmFyaWFibGVHcmFkaWVudHMuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cy5zZXQoXG4gICAgICAgICAgICBub2RlLm91dHB1dCwga2VlcChtYXRoLmFkZChncmFkaWVudCwgYWNjdW11bGF0ZWRHcmFkaWVudCkpKTtcbiAgICAgICAgYWNjdW11bGF0ZWRHcmFkaWVudC5kaXNwb3NlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFmdGVyQmF0Y2goXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgYmF0Y2hTaXplOiBudW1iZXIsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICB0aGlzLnZhcmlhYmxlTm9kZXMhLmZvckVhY2gobm9kZSA9PiB7XG4gICAgICAgIGNvbnN0IG9sZFZhcmlhYmxlID0gYWN0aXZhdGlvbkFycmF5TWFwLmdldChub2RlLm91dHB1dCk7XG4gICAgICAgIGNvbnN0IGdyYWRpZW50ID0gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICBjb25zdCB2YXJpYWJsZSA9XG4gICAgICAgICAgICBtYXRoLnNjYWxlZEFycmF5QWRkKHRoaXMuYyEsIGdyYWRpZW50LCB0aGlzLm9uZSEsIG9sZFZhcmlhYmxlKTtcbiAgICAgICAgYWN0aXZhdGlvbkFycmF5TWFwLnNldChub2RlLm91dHB1dCwga2VlcCh2YXJpYWJsZSkpO1xuICAgICAgICBub2RlLmRhdGEgPSB2YXJpYWJsZTtcblxuICAgICAgICBvbGRWYXJpYWJsZS5kaXNwb3NlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRoaXMudmFyaWFibGVHcmFkaWVudHMuZGlzcG9zZSgpO1xuICAgIHRoaXMudmFyaWFibGVHcmFkaWVudHMgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuYyAhPSBudWxsKSB7XG4gICAgICB0aGlzLmMuZGlzcG9zZSgpO1xuICAgIH1cbiAgICB0aGlzLm9uZS5kaXNwb3NlKCk7XG4gIH1cblxuICBzZXRMZWFybmluZ1JhdGUobGVhcm5pbmdSYXRlOiBudW1iZXIpIHtcbiAgICB0aGlzLmxlYXJuaW5nUmF0ZSA9IGxlYXJuaW5nUmF0ZTtcbiAgfVxuXG4gIHByaXZhdGUgdmFyaWFibGVHcmFkaWVudHMgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgcHJpdmF0ZSBwcmV2QmF0Y2hTaXplOiBudW1iZXI7XG4gIHByaXZhdGUgb25lID0gU2NhbGFyLm5ldygxKTtcbiAgcHJpdmF0ZSBjOiBTY2FsYXI7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIFRlbnNvckFycmF5TWFwIGlzIGFuIGludGVybmFsIG1hcCBmcm9tIFRlbnNvciBJRHMgdG8gTkRBcnJheXMuIFNpbmNlIE5EQXJyYXlzXG4gKiBjYW4gYmUgYmFja2VkIGJ5IFdlYkdMIHRleHR1cmVzLCB0aGUgVGVuc29yQXJyYXlNYXAgaXMgb25seSB1c2VkIGluc2lkZSBvZiBhXG4gKiBTZXNzaW9uLlxuICovXG5leHBvcnQgY2xhc3MgVGVuc29yQXJyYXlNYXAge1xuICAvKipcbiAgICogQWRkIG9yIHJlcGxhY2UgYW4gZW50cnkgaW4gdGhlIG1hcC5cbiAgICogQHBhcmFtIHRlbnNvciBUaGUgdGVuc29yIGtleS5cbiAgICogQHBhcmFtIGFycmF5IFRoZSBOREFycmF5IHZhbHVlLCBjYW4gYmUgbnVsbC5cbiAgICovXG4gIHNldCh0ZW5zb3I6IFRlbnNvciwgYXJyYXk6IE5EQXJyYXl8bnVsbCkge1xuICAgIHRoaXMuZGljdFt0ZW5zb3IuaWRdID0gYXJyYXk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgTkRBcnJheSBhc3NvY2lhdGVkIHdpdGggdGhlIHByb3ZpZGVkIHRlbnNvci4gV2lsbCB0aHJvdyBhblxuICAgKiBleGNlcHRpb24gaWYgdGhlIHRlbnNvciBpcyBub3QgYSBrZXkgaW4gdGhlIG1hcCwgb3IgaWYgdGhlIGFzc29jaWF0ZWRcbiAgICogTkRBcnJheSBpcyBudWxsLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcGFyYW0gc2tpcENoZWNrcyBGYWxzZSBieSBkZWZhdWx0LiBJZiB0cnVlIHdpbGwgc2tpcCBhbGwgY2hlY2tzLlxuICAgKiBAcmV0dXJuIFRoZSBOREFycmF5IGFzc29jaWF0ZWQgd2l0aCB0aGUgdGVuc29yLlxuICAgKi9cbiAgZ2V0KHRlbnNvcjogVGVuc29yLCBza2lwQ2hlY2tzID0gZmFsc2UpOiBOREFycmF5IHtcbiAgICBpZiAoIXNraXBDaGVja3MgJiYgdGhpcy5kaWN0W3RlbnNvci5pZF0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgbm90IGluIGFycmF5IG1hcC4nKTtcbiAgICB9XG4gICAgY29uc3QgbmRhID0gdGhpcy5kaWN0W3RlbnNvci5pZF07XG4gICAgaWYgKCFza2lwQ2hlY2tzICYmIG5kYSA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgaGFzIG51bGwgYXJyYXkuJyk7XG4gICAgfVxuICAgIHJldHVybiBuZGEhO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSB0ZW5zb3IvTkRBcnJheSBwYWlyIGZyb20gdGhlIG1hcC5cbiAgICogQHBhcmFtIHRlbnNvciBUaGUgdGVuc29yIGtleS5cbiAgICovXG4gIGRlbGV0ZSh0ZW5zb3I6IFRlbnNvcikge1xuICAgIGRlbGV0ZSB0aGlzLmRpY3RbdGVuc29yLmlkXTtcbiAgfVxuXG4gIGRpc3Bvc2VBcnJheSh0ZW5zb3I6IFRlbnNvcikge1xuICAgIGlmICh0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG5kYSA9IHRoaXMuZGljdFt0ZW5zb3IuaWRdO1xuICAgIGlmIChuZGEgPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbmRhLmRpc3Bvc2UoKTtcbiAgICB0aGlzLmRpY3RbdGVuc29yLmlkXSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybiBUaGUgbnVtYmVyIG9mIHRlbnNvci9OREFycmF5IHBhaXJzIGluIHRoZSBtYXAuXG4gICAqL1xuICBzaXplKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHRoaXMuZGljdCkubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIEl0ZXJhdGUgb3ZlciBhbGwgY29udGFpbmVkIE5EQXJyYXkgdmFsdWVzIGFuZCBkaXNwb3NlIHRoZW0uXG4gICAqL1xuICBkaXNwb3NlKCkge1xuICAgIE9iamVjdC5rZXlzKHRoaXMuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgICBjb25zdCBuZGEgPSB0aGlzLmRpY3RbK3RlbnNvcklEXTtcbiAgICAgIGlmIChuZGEpIHtcbiAgICAgICAgbmRhLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLmRpY3QgPSB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUZXN0cyB0byBzZWUgaWYgYSB0ZW5zb3IgaGFzIGEgbnVsbCBhc3NvY2lhdGVkIHdpdGggaXQuIFRocm93c1xuICAgKiBpZiB0aGUgdGVuc29yIGlzIG5vdCBhIGtleSBpbiB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcmV0dXJuIFRydWUgaWYgdGhlIGFzc29jaWF0ZWQgTkRBcnJheSBpcyBudWxsLCBlbHNlIEZhbHNlLlxuICAgKi9cbiAgaGFzTnVsbEFycmF5KHRlbnNvcjogVGVuc29yKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndGVuc29yICcgKyB0ZW5zb3IuaWQgKyAnIG5vdCBpbiBhcnJheSBtYXAuJyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgZGljdDoge1t0ZW5zb3JJRDogbnVtYmVyXTogTkRBcnJheSB8IG51bGx9ID0ge307XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCB0eXBlIFZlY3RvciA9IG51bWJlcltdIHwgRmxvYXQ2NEFycmF5IHwgRmxvYXQzMkFycmF5IHwgSW50MzJBcnJheSB8XG4gICAgSW50OEFycmF5IHwgSW50MTZBcnJheTtcblxuLyoqIFNodWZmbGVzIHRoZSBhcnJheSB1c2luZyBGaXNoZXItWWF0ZXMgYWxnb3JpdGhtLiAqL1xuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuZXhwb3J0IGZ1bmN0aW9uIHNodWZmbGUoYXJyYXk6IGFueVtdfFVpbnQzMkFycmF5fEludDMyQXJyYXl8XG4gICAgICAgICAgICAgICAgICAgICAgICBGbG9hdDMyQXJyYXkpOiB2b2lkIHtcbiAgbGV0IGNvdW50ZXIgPSBhcnJheS5sZW5ndGg7XG4gIGxldCB0ZW1wID0gMDtcbiAgbGV0IGluZGV4ID0gMDtcbiAgLy8gV2hpbGUgdGhlcmUgYXJlIGVsZW1lbnRzIGluIHRoZSBhcnJheVxuICB3aGlsZSAoY291bnRlciA+IDApIHtcbiAgICAvLyBQaWNrIGEgcmFuZG9tIGluZGV4XG4gICAgaW5kZXggPSAoTWF0aC5yYW5kb20oKSAqIGNvdW50ZXIpIHwgMDtcbiAgICAvLyBEZWNyZWFzZSBjb3VudGVyIGJ5IDFcbiAgICBjb3VudGVyLS07XG4gICAgLy8gQW5kIHN3YXAgdGhlIGxhc3QgZWxlbWVudCB3aXRoIGl0XG4gICAgdGVtcCA9IGFycmF5W2NvdW50ZXJdO1xuICAgIGFycmF5W2NvdW50ZXJdID0gYXJyYXlbaW5kZXhdO1xuICAgIGFycmF5W2luZGV4XSA9IHRlbXA7XG4gIH1cbn1cblxuLyoqIENsYW1wcyBhIHZhbHVlIHRvIGEgc3BlY2lmaWVkIHJhbmdlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCB4OiBudW1iZXIsIG1heDogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4oeCwgbWF4KSk7XG59XG5cbi8qKiBSZXR1cm5zIGEgc2FtcGxlIGZyb20gYSB1bmlmb3JtIFthLCBiXSBkaXN0cmlidXRpb24uICovXG5leHBvcnQgZnVuY3Rpb24gcmFuZFVuaWZvcm0oYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgcmV0dXJuIE1hdGgucmFuZG9tKCkgKiAoYiAtIGEpICsgYTtcbn1cblxuLyoqXG4gKiBTYW1wbGVzIGZyb20gYSBnYXVzc2lhbiBkaXN0cmlidXRpb24uXG4gKlxuICogQHBhcmFtIG1lYW4gVGhlIG1lYW4uIERlZmF1bHQgaXMgMC5cbiAqIEBwYXJhbSBzdGREZXYgVGhlIHN0YW5kYXJkIGRldmlhdGlvbi4gRGVmYXVsdCBpcyAxLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmFuZEdhdXNzKG1lYW4gPSAwLCBzdGREZXYgPSAxLCB0cnVuY2F0ZWQgPSBmYWxzZSk6IG51bWJlciB7XG4gIGxldCB2MTogbnVtYmVyLCB2MjogbnVtYmVyLCBzOiBudW1iZXI7XG4gIGRvIHtcbiAgICB2MSA9IDIgKiBNYXRoLnJhbmRvbSgpIC0gMTtcbiAgICB2MiA9IDIgKiBNYXRoLnJhbmRvbSgpIC0gMTtcbiAgICBzID0gdjEgKiB2MSArIHYyICogdjI7XG4gIH0gd2hpbGUgKHMgPiAxKTtcblxuICBjb25zdCByZXN1bHQgPSBNYXRoLnNxcnQoLTIgKiBNYXRoLmxvZyhzKSAvIHMpICogdjE7XG4gIGlmICh0cnVuY2F0ZWQgJiYgcmVzdWx0ID4gMikge1xuICAgIHJldHVybiByYW5kR2F1c3MobWVhbiwgc3RkRGV2LCB0cnVlKTtcbiAgfVxuICByZXR1cm4gbWVhbiArIHN0ZERldiAqIHJlc3VsdDtcbn1cblxuLyoqIFJldHVybnMgc3F1YXJlZCBldWNsZWRpYW4gZGlzdGFuY2UgYmV0d2VlbiB0d28gdmVjdG9ycy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXN0U3F1YXJlZChhOiBWZWN0b3IsIGI6IFZlY3Rvcik6IG51bWJlciB7XG4gIGxldCByZXN1bHQgPSAwO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGEubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBkaWZmID0gYVtpXSAtIGJbaV07XG4gICAgcmVzdWx0ICs9IGRpZmYgKiBkaWZmO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnQoZXhwcjogYm9vbGVhbiwgbXNnOiBzdHJpbmcpIHtcbiAgaWYgKCFleHByKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydFNoYXBlc01hdGNoKFxuICAgIHNoYXBlQTogbnVtYmVyW10sIHNoYXBlQjogbnVtYmVyW10sIGVycm9yTWVzc2FnZVByZWZpeCA9ICcnKTogdm9pZCB7XG4gIGFzc2VydChcbiAgICAgIGFycmF5c0VxdWFsKHNoYXBlQSwgc2hhcGVCKSxcbiAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArIGBTaGFwZXMgJHtzaGFwZUF9IGFuZCAke3NoYXBlQn0gbXVzdCBtYXRjaGApO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gZmxhdHRlbihhcnI6IGFueVtdLCByZXQ/OiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgcmV0ID0gKHJldCA9PT0gdW5kZWZpbmVkID8gW10gOiByZXQpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyci5sZW5ndGg7ICsraSkge1xuICAgIGlmIChBcnJheS5pc0FycmF5KGFycltpXSkpIHtcbiAgICAgIGZsYXR0ZW4oYXJyW2ldLCByZXQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXQucHVzaChhcnJbaV0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmV0O1xufVxuXG5leHBvcnQgdHlwZSBBcnJheURhdGEgPSBudW1iZXJ8bnVtYmVyW118bnVtYmVyW11bXXxudW1iZXJbXVtdW118bnVtYmVyW11bXVtdW107XG5cbmV4cG9ydCBmdW5jdGlvbiBpbmZlclNoYXBlKGFycjogQXJyYXlEYXRhKTogbnVtYmVyW10ge1xuICBjb25zdCBzaGFwZTogbnVtYmVyW10gPSBbXTtcbiAgd2hpbGUgKGFyciBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgc2hhcGUucHVzaChhcnIubGVuZ3RoKTtcbiAgICBhcnIgPSBhcnJbMF07XG4gIH1cbiAgcmV0dXJuIHNoYXBlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2l6ZUZyb21TaGFwZShzaGFwZTogbnVtYmVyW10pOiBudW1iZXIge1xuICBpZiAoc2hhcGUubGVuZ3RoID09PSAwKSB7XG4gICAgLy8gU2NhbGFyLlxuICAgIHJldHVybiAxO1xuICB9XG4gIGxldCBzaXplID0gc2hhcGVbMF07XG4gIGZvciAobGV0IGkgPSAxOyBpIDwgc2hhcGUubGVuZ3RoOyBpKyspIHtcbiAgICBzaXplICo9IHNoYXBlW2ldO1xuICB9XG4gIHJldHVybiBzaXplO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNTY2FsYXJTaGFwZShzaGFwZTogbnVtYmVyW10pOiBib29sZWFuIHtcbiAgcmV0dXJuIHNoYXBlLmxlbmd0aCA9PT0gMDtcbn1cblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuZXhwb3J0IGZ1bmN0aW9uIGFycmF5c0VxdWFsKG4xOiBhbnlbXXxGbG9hdDMyQXJyYXksIG4yOiBhbnlbXXxGbG9hdDMyQXJyYXkpIHtcbiAgaWYgKG4xLmxlbmd0aCAhPT0gbjIubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjEubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAobjFbaV0gIT09IG4yW2ldKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG4gIHJldHVybiB0cnVlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNJbnQoYTogbnVtYmVyKTogYm9vbGVhbiB7XG4gIHJldHVybiBhICUgMSA9PT0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhbmgoeDogbnVtYmVyKTogbnVtYmVyIHtcbiAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICBpZiAoKE1hdGggYXMgYW55KS50YW5oICE9IG51bGwpIHtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgcmV0dXJuIChNYXRoIGFzIGFueSkudGFuaCh4KTtcbiAgfVxuICBpZiAoeCA9PT0gSW5maW5pdHkpIHtcbiAgICByZXR1cm4gMTtcbiAgfSBlbHNlIGlmICh4ID09PSAtSW5maW5pdHkpIHtcbiAgICByZXR1cm4gLTE7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgZTJ4ID0gTWF0aC5leHAoMiAqIHgpO1xuICAgIHJldHVybiAoZTJ4IC0gMSkgLyAoZTJ4ICsgMSk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpemVUb1NxdWFyaXNoU2hhcGUoc2l6ZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGZvciAobGV0IGEgPSBNYXRoLmZsb29yKE1hdGguc3FydChzaXplKSk7IGEgPiAxOyAtLWEpIHtcbiAgICBpZiAoc2l6ZSAlIGEgPT09IDApIHtcbiAgICAgIHJldHVybiBbYSwgc2l6ZSAvIGFdO1xuICAgIH1cbiAgfVxuICByZXR1cm4gWzEsIHNpemVdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2h1ZmZsZWRJbmRpY2VzKG46IG51bWJlcik6IFVpbnQzMkFycmF5IHtcbiAgY29uc3Qgc2h1ZmZsZWRJbmRpY2VzID0gbmV3IFVpbnQzMkFycmF5KG4pO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG47ICsraSkge1xuICAgIHNodWZmbGVkSW5kaWNlc1tpXSA9IGk7XG4gIH1cbiAgc2h1ZmZsZShzaHVmZmxlZEluZGljZXMpO1xuICByZXR1cm4gc2h1ZmZsZWRJbmRpY2VzO1xufVxuIl19 diff --git a/demos/homepage/Gemfile b/demos/homepage/Gemfile new file mode 100644 index 0000000000..8b53357a7e --- /dev/null +++ b/demos/homepage/Gemfile @@ -0,0 +1,26 @@ +source "https://rubygems.org" + +# Hello! This is where you manage which Jekyll version is used to run. +# When you want to use a different version, change it below, save the +# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: +# +# bundle exec jekyll serve +# +# This will help ensure the proper Jekyll version is running. +# Happy Jekylling! + +# This is the default theme for new Jekyll sites. You may change this to anything you like. +gem "minima", "~> 2.0" + +# If you want to use GitHub Pages, remove the "gem "jekyll"" above and +# uncomment the line below. To upgrade, run `bundle update github-pages`. +gem "github-pages", group: :jekyll_plugins + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-feed", "~> 0.6" +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +#gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + diff --git a/demos/homepage/Gemfile.lock b/demos/homepage/Gemfile.lock new file mode 100644 index 0000000000..147c3924b8 --- /dev/null +++ b/demos/homepage/Gemfile.lock @@ -0,0 +1,205 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.8) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + colorator (1.1.0) + ethon (0.10.1) + ffi (>= 1.3.0) + execjs (2.7.0) + faraday (0.12.2) + multipart-post (>= 1.2, < 3) + ffi (1.9.18) + forwardable-extended (2.6.0) + gemoji (3.0.0) + github-pages (146) + activesupport (= 4.2.8) + github-pages-health-check (= 1.3.5) + jekyll (= 3.4.5) + jekyll-avatar (= 0.4.2) + jekyll-coffeescript (= 1.0.1) + jekyll-default-layout (= 0.1.4) + jekyll-feed (= 0.9.2) + jekyll-gist (= 1.4.0) + jekyll-github-metadata (= 2.5.1) + jekyll-mentions (= 1.2.0) + jekyll-optional-front-matter (= 0.2.0) + jekyll-paginate (= 1.1.0) + jekyll-readme-index (= 0.1.0) + jekyll-redirect-from (= 0.12.1) + jekyll-relative-links (= 0.4.1) + jekyll-sass-converter (= 1.5.0) + jekyll-seo-tag (= 2.2.3) + jekyll-sitemap (= 1.0.0) + jekyll-swiss (= 0.4.0) + jekyll-theme-architect (= 0.0.4) + jekyll-theme-cayman (= 0.0.4) + jekyll-theme-dinky (= 0.0.4) + jekyll-theme-hacker (= 0.0.4) + jekyll-theme-leap-day (= 0.0.4) + jekyll-theme-merlot (= 0.0.4) + jekyll-theme-midnight (= 0.0.4) + jekyll-theme-minimal (= 0.0.4) + jekyll-theme-modernist (= 0.0.4) + jekyll-theme-primer (= 0.3.1) + jekyll-theme-slate (= 0.0.4) + jekyll-theme-tactile (= 0.0.4) + jekyll-theme-time-machine (= 0.0.4) + jekyll-titles-from-headings (= 0.2.0) + jemoji (= 0.8.0) + kramdown (= 1.13.2) + liquid (= 3.0.6) + listen (= 3.0.6) + mercenary (~> 0.3) + minima (= 2.1.1) + rouge (= 1.11.1) + terminal-table (~> 1.4) + github-pages-health-check (1.3.5) + addressable (~> 2.3) + net-dns (~> 0.8) + octokit (~> 4.0) + public_suffix (~> 2.0) + typhoeus (~> 0.7) + html-pipeline (2.6.0) + activesupport (>= 2) + nokogiri (>= 1.4) + i18n (0.8.6) + jekyll (3.4.5) + addressable (~> 2.4) + colorator (~> 1.0) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.3) + liquid (~> 3.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (~> 1.7) + safe_yaml (~> 1.0) + jekyll-avatar (0.4.2) + jekyll (~> 3.0) + jekyll-coffeescript (1.0.1) + coffee-script (~> 2.2) + jekyll-default-layout (0.1.4) + jekyll (~> 3.0) + jekyll-feed (0.9.2) + jekyll (~> 3.3) + jekyll-gist (1.4.0) + octokit (~> 4.2) + jekyll-github-metadata (2.5.1) + jekyll (~> 3.1) + octokit (~> 4.0, != 4.4.0) + jekyll-mentions (1.2.0) + activesupport (~> 4.0) + html-pipeline (~> 2.3) + jekyll (~> 3.0) + jekyll-optional-front-matter (0.2.0) + jekyll (~> 3.0) + jekyll-paginate (1.1.0) + jekyll-readme-index (0.1.0) + jekyll (~> 3.0) + jekyll-redirect-from (0.12.1) + jekyll (~> 3.3) + jekyll-relative-links (0.4.1) + jekyll (~> 3.3) + jekyll-sass-converter (1.5.0) + sass (~> 3.4) + jekyll-seo-tag (2.2.3) + jekyll (~> 3.3) + jekyll-sitemap (1.0.0) + jekyll (~> 3.3) + jekyll-swiss (0.4.0) + jekyll-theme-architect (0.0.4) + jekyll (~> 3.3) + jekyll-theme-cayman (0.0.4) + jekyll (~> 3.3) + jekyll-theme-dinky (0.0.4) + jekyll (~> 3.3) + jekyll-theme-hacker (0.0.4) + jekyll (~> 3.3) + jekyll-theme-leap-day (0.0.4) + jekyll (~> 3.3) + jekyll-theme-merlot (0.0.4) + jekyll (~> 3.3) + jekyll-theme-midnight (0.0.4) + jekyll (~> 3.3) + jekyll-theme-minimal (0.0.4) + jekyll (~> 3.3) + jekyll-theme-modernist (0.0.4) + jekyll (~> 3.3) + jekyll-theme-primer (0.3.1) + jekyll (~> 3.3) + jekyll-theme-slate (0.0.4) + jekyll (~> 3.3) + jekyll-theme-tactile (0.0.4) + jekyll (~> 3.3) + jekyll-theme-time-machine (0.0.4) + jekyll (~> 3.3) + jekyll-titles-from-headings (0.2.0) + jekyll (~> 3.3) + jekyll-watch (1.5.0) + listen (~> 3.0, < 3.1) + jemoji (0.8.0) + activesupport (~> 4.0) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (>= 3.0) + kramdown (1.13.2) + liquid (3.0.6) + listen (3.0.6) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9.7) + mercenary (0.3.6) + mini_portile2 (2.2.0) + minima (2.1.1) + jekyll (~> 3.3) + minitest (5.10.3) + multipart-post (2.0.0) + net-dns (0.8.0) + nokogiri (1.8.0) + mini_portile2 (~> 2.2.0) + octokit (4.7.0) + sawyer (~> 0.8.0, >= 0.5.3) + pathutil (0.14.0) + forwardable-extended (~> 2.6) + public_suffix (2.0.5) + rb-fsevent (0.10.2) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + rouge (1.11.1) + safe_yaml (1.0.4) + sass (3.5.1) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sawyer (0.8.1) + addressable (>= 2.3.5, < 2.6) + faraday (~> 0.8, < 1.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + typhoeus (0.8.0) + ethon (>= 0.8.0) + tzinfo (1.2.3) + thread_safe (~> 0.1) + unicode-display_width (1.3.0) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + jekyll-feed (~> 0.6) + minima (~> 2.0) + +BUNDLED WITH + 1.15.3 diff --git a/demos/homepage/_config.yml b/demos/homepage/_config.yml new file mode 100644 index 0000000000..a7bcf13a7c --- /dev/null +++ b/demos/homepage/_config.yml @@ -0,0 +1,32 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. + +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: deeplearn.js +baseurl: "" # the subpath of your site, e.g. /blog + +# Build settings +markdown: kramdown +theme: minima +gems: + - jekyll-feed + - jekyll-redirect-from +exclude: + - Gemfile + - Gemfile.lock + - node_modules/ + - bower_components/ + +# deployment +host: 0.0.0.0 diff --git a/demos/homepage/_includes/footer.html b/demos/homepage/_includes/footer.html new file mode 100644 index 0000000000..eca84845e6 --- /dev/null +++ b/demos/homepage/_includes/footer.html @@ -0,0 +1,32 @@ + + + + diff --git a/demos/homepage/_includes/header.html b/demos/homepage/_includes/header.html new file mode 100644 index 0000000000..2c25c16548 --- /dev/null +++ b/demos/homepage/_includes/header.html @@ -0,0 +1,31 @@ + +
+ +
+ + diff --git a/demos/homepage/_layouts/default.html b/demos/homepage/_layouts/default.html new file mode 100644 index 0000000000..953754325a --- /dev/null +++ b/demos/homepage/_layouts/default.html @@ -0,0 +1,36 @@ + + + + + + deeplearn.js + + + + + + + + +
+ {% include header.html %} +
+ {{ content }} + {% include footer.html %} +
+
+ + diff --git a/demos/homepage/_layouts/page.html b/demos/homepage/_layouts/page.html new file mode 100644 index 0000000000..6b83422475 --- /dev/null +++ b/demos/homepage/_layouts/page.html @@ -0,0 +1,30 @@ +--- +layout: default +--- +
+
+
+
+ {{ content }} +
+
+
+
+ {% assign default_paths = site.pages | map: "path" %} + {% assign page_paths = site.header_pages | default: default_paths %} + + {% if page_paths %} + + {% endif %} +
+
diff --git a/demos/homepage/assets/style.css b/demos/homepage/assets/style.css new file mode 100644 index 0000000000..21575f4f00 --- /dev/null +++ b/demos/homepage/assets/style.css @@ -0,0 +1,280 @@ +html { + width: 100%; + height: 100%; +} +body { + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + margin: 0; + width: 100%; + height: 100%; +} +head { + height: 0; + width: 0; +} +/* Typography */ +p { + font-family: 'Roboto', sans-serif; + line-height: 1.67em; + font-size: 16px; +} +.mdl-list__item a { + text-decoration: none; + color: rgba(0,0,0,0.64); + font-weight: 400; + letter-spacing: 0; +} +.mdl-list__item a:hover { + text-decoration: underline; + color: rgba(0,0,0,0.64); + font-weight: 400; + letter-spacing: 0; +} +.mdl-navigation__link{ + font-family: 'Roboto', sans-serif; +} +.mdl-layout__drawer { + font-family: 'Roboto', sans-serif; +} + +/* CPPN Demo */ +.banner { + background-size: cover; + background-position: center; + min-height: 250px; + padding-top: 64px; + padding-bottom: 32px; + position: relative; +} +.banner-cover { + position: relative; + background: #466368; + background: -webkit-linear-gradient(#c4c4c4, #293f50); + background: -moz-linear-gradient(#c4c4c4, #293f50); + background: linear-gradient(#c4c4c4, #293f50); +} +.banner-text { + color:#fff; +} +.cppn-controls { + min-width: 300px; + max-width: 300px; + padding:24px 24px 60px 24px; + background-color: rgba(255,255,255,0.99); + position: relative; +} +.cppn-demo { + color:#fff; +} +#disabled-demo-overlay { + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(62, 82, 90, .9); + top: 0; + left: 0; +} +#disabled-demo { + margin: auto; + position: absolute; + top: 20%; + left: 0; + right: 0; + padding: 32px; + bottom: 0; + font-size: 18px; + color: white; + font-weight: 400; + text-align: left; + line-height: 1.3325em; + z-index: 2; +} +#inference { + width: 100%; + height: calc(100% + 1px); + position: absolute; +} +.getmdl-select .mdl-icon-toggle__label{ + float:right; + margin-top:-30px; + color:rgba(0,0,0,0.4); +} +/* Introduction */ +.intro-text { + margin: 32px auto 32px auto; +} +.mdl-grid { + max-width: 1200px; +} +.mdl-mini-footer { + margin-top: 72px; +} +.mdl-card__actions { + padding-left: 12px; +} +.intro-headline { + line-height: 1.67em; + font-weight: 300; + color: #888; +} +/* Responsive behavior */ +.intro-text > .mdl-cell--8-col-tablet { + margin-top: 72px; +} +.banner >.mdl-cell--8-col-tablet { + margin: 36px; +} +.resource-tabs { + font-family: 'Roboto', sans-serif; +} + +a { + color: #346f91; + font-size: 16px; +} +a:visited { + color: #346f91; +} +a:hover { + color: #346f91; +} +.mdl-card__actions a:hover { + text-decoration: none; +} + +a.main-title { + color: white; + text-decoration: none; +} + +blockquote { + color: black; + padding: 8px 8px 8px 20px; + border-left-width: 2px; + font-style: normal; + border-left-style: solid; + border-color: #9e9e9e; + background-color: #eee; + margin-bottom: 32px; +} +blockquote:before { + content: none; +} +blockquote:after { + content: none; +} +ul { + margin-left: 16px; + list-style-type: disc; +} +ul ul { + list-style-type: disc; +} +ul ul ul { + list-style-type: disc; +} +ul ul ul ul { + list-style-type: disc; +} +.highlight .err { + color: inherit; + background-color: inherit; +} +table { + border-spacing: 20px; +} +.site-nav { + position: fixed; + float: inherit; +} +.site-nav ul { + margin-top: 32px; + border-left: solid 3px #C0EbF1; + padding-left: 20px; + line-height: 28px; + list-style-type: none; + line-height: 2.7em; + font-weight: normal; +} +.site-nav ul a { + color: #50797f; + font-weight: normal; +} +ul.index { + border-left: 2px solid #346f91; + margin-left: 16px; + margin-top: 16px; + padding-left: 16px; + list-style-type: none; +} +.deeplearn-shine { + color: #777; + font-weight: 500; +} +.demo-card .mdl-card__title { + color: #fff; + height: 176px; +} + +.demo-card .mdl-card__title:before { + content: ''; + height: 176px; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(53, 71, 80, 0.65); +} + +#model-builder { + background: url('/demos/images/model-builder.png') center / cover; +} + +#webcam { + background: url('/demos/images/imagenet.png') center / cover; +} + +#nnart { + background: url('/demos/images/nn-art.png') center / cover; +} + +#benchmarks { + background: url('/demos/images/benchmark.png') center / cover; +} + +.demo-card .mdl-card__title-text { + color: white; + z-index: 1; + font-weight: 400; +} + +.mdl-typography--display-2, h1, h2, h3, h4 { + color: #414141; + font-weight: 300; +} +code { + background: none; +} +.highlighter-rouge .highlight { + background-color: #f5f5f5; + padding-bottom: 16px; + margin-bottom: 32px; +} + +h2 { + font-size: 40px; +} + +h3 { + font-size: 30px; +} +p.intro-body { + font-weight: 300; +} +.mdl-mini-footer__link-list a { + color: rgb(158, 158, 158); +} +.mdl-mini-footer__link-list a:visited { + color: rgb(158, 158, 158); +} diff --git a/demos/homepage/bundle.js b/demos/homepage/bundle.js new file mode 100644 index 0000000000..fb014c9d2e --- /dev/null +++ b/demos/homepage/bundle.js @@ -0,0 +1,8216 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o " + inputNumDimensions + ".0) {\n gl_FragColor = vec4(z[1], 0, 0, 0);\n } else {\n gl_FragColor = texture2D(source, resultUV);\n }\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getAddLatentVariablesShader = getAddLatentVariablesShader; +function addLatentVariables(gpgpu, addZShader, sourceTex, resultTex, shapeRowCol, z1, z2) { + gpgpu.setOutputMatrixTexture(resultTex, shapeRowCol[0], shapeRowCol[1]); + gpgpu.setProgram(addZShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + var zLoc = gpgpu.getUniformLocation('z'); + gpgpu.gl.uniform2f(zLoc, z1, z2); + gpgpu.executeProgram(); +} +exports.addLatentVariables = addLatentVariables; +function getRenderShader(gpgpu, imageSize) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n uniform int colorMode;\n uniform float outputNumDimensions;\n\n const float destinationSize = " + imageSize + ".0;\n\n const mat3 yuv2rgb = mat3(\n 1, 1, 1,\n 0, -.34413, 1.772,\n 1.402, -.71414, 0);\n\n vec3 hsv2rgb(vec3 c) {\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n }\n\n void main() {\n vec2 outputCR = floor(gl_FragCoord.xy);\n float inputC = outputCR.y * destinationSize + outputCR.x;\n float u = (inputC + 0.5) / " + imageSize * imageSize + ".0;\n\n vec4 inputR = vec4(0.0, 1.0, 2.0, 3.0);\n vec4 v = (inputR + 0.5) / outputNumDimensions;\n\n vec4 values = vec4(\n texture2D(source, vec2(u, v[0])).r,\n texture2D(source, vec2(u, v[1])).r,\n texture2D(source, vec2(u, v[2])).r,\n texture2D(source, vec2(u, v[3])).r);\n\n if (colorMode == 0) {\n // RGB\n gl_FragColor = vec4(values.rgb, 1.0);\n } else if (colorMode == 1) {\n // RGBA\n gl_FragColor = values;\n } else if (colorMode == 2) {\n // HSV\n vec3 rgb = hsv2rgb(values.rgb);\n gl_FragColor = vec4(rgb, 1.0);\n } else if (colorMode == 3) {\n // HSVA\n vec3 rgb = hsv2rgb(values.rgb);\n gl_FragColor = vec4(rgb, values[3]);\n } else if (colorMode == 4 || colorMode == 5) {\n // YUV\n values[0] = clamp(values[0], 0.2, 0.8);\n values[1] = values[1] - 0.5;\n values[2] = values[2] - 0.5;\n vec3 rgb = yuv2rgb * values.rgb;\n if (colorMode == 4) {\n // YUV\n gl_FragColor = vec4(rgb, 1.0);\n } else if (colorMode == 5) {\n // YUVA\n gl_FragColor = vec4(rgb, values.a);\n }\n } else if (colorMode == 6) {\n gl_FragColor = vec4(values[0], values[0], values[0], 1.0);\n }\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderShader = getRenderShader; +function render(gpgpu, renderShader, sourceTex, outputNumDimensions, colorMode) { + learnjs_1.webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + var colorModeLoc = gpgpu.getUniformLocation('colorMode'); + gpgpu.gl.uniform1i(colorModeLoc, colorMode); + var outputNumDimensionsLoc = gpgpu.getUniformLocation('outputNumDimensions'); + gpgpu.gl.uniform1f(outputNumDimensionsLoc, outputNumDimensions); + gpgpu.executeProgram(); +} +exports.render = render; +function imagePixelToNormalizedCoord(x, y, imageWidth, imageHeight, zSize) { + var halfWidth = imageWidth * 0.5; + var halfHeight = imageHeight * 0.5; + var normX = (x - halfWidth) / imageWidth; + var normY = (y - halfHeight) / imageHeight; + var r = Math.sqrt(normX * normX + normY * normY); + var result = [normX, normY, r]; + for (var i = 0; i < zSize; i++) { + result.push(0); + } + return result; +} +exports.imagePixelToNormalizedCoord = imagePixelToNormalizedCoord; + +},{"../learnjs":2}],5:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var MANIFEST_FILE = 'manifest.json'; +var CheckpointLoader = (function () { + function CheckpointLoader(urlPath) { + this.urlPath = urlPath; + if (this.urlPath.charAt(this.urlPath.length - 1) !== '/') { + this.urlPath += '/'; + } + } + CheckpointLoader.prototype.loadManifest = function () { + var _this = this; + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', _this.urlPath + MANIFEST_FILE); + xhr.onload = function () { + _this.checkpointManifest = JSON.parse(xhr.responseText); + resolve(); + }; + xhr.onerror = function (error) { + throw new Error(MANIFEST_FILE + " not found at " + _this.urlPath + ". " + error); + }; + xhr.send(); + }); + }; + CheckpointLoader.prototype.getCheckpointManifest = function () { + var _this = this; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + resolve(_this.checkpointManifest); + }); + }); + } + return new Promise(function (resolve, reject) { + resolve(_this.checkpointManifest); + }); + }; + CheckpointLoader.prototype.getAllVariables = function () { + var _this = this; + if (this.variables != null) { + return new Promise(function (resolve, reject) { + resolve(_this.variables); + }); + } + return new Promise(function (resolve, reject) { + _this.getCheckpointManifest().then(function (checkpointDefinition) { + var variableNames = Object.keys(_this.checkpointManifest); + var variablePromises = []; + for (var i = 0; i < variableNames.length; i++) { + variablePromises.push(_this.getVariable(variableNames[i])); + } + Promise.all(variablePromises).then(function (variables) { + _this.variables = {}; + for (var i = 0; i < variables.length; i++) { + _this.variables[variableNames[i]] = variables[i]; + } + resolve(_this.variables); + }); + }); + }); + }; + CheckpointLoader.prototype.getVariable = function (varName) { + var _this = this; + if (!(varName in this.checkpointManifest)) { + throw new Error('Cannot load non-existant variable ' + varName); + } + var variableRequestPromiseMethod = function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + var fname = _this.checkpointManifest[varName].filename; + xhr.open('GET', _this.urlPath + fname); + xhr.onload = function () { + var values = new Float32Array(xhr.response); + var ndarray = ndarray_1.NDArray.make(_this.checkpointManifest[varName].shape, { values: values }); + resolve(ndarray); + }; + xhr.onerror = function (error) { + throw new Error('Could not fetch variable ' + varName + ': ' + error); + }; + xhr.send(); + }; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + new Promise(variableRequestPromiseMethod).then(resolve); + }); + }); + } + return new Promise(variableRequestPromiseMethod); + }; + return CheckpointLoader; +}()); +exports.CheckpointLoader = CheckpointLoader; + +},{"./math/ndarray":22}],6:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var STATS_SAMPLE_PERCENTAGE = 0.1; +var InMemoryDataset = (function () { + function InMemoryDataset(dataShapes) { + this.dataShapes = dataShapes; + this.normalizationInfo = {}; + } + InMemoryDataset.prototype.getDataShape = function (dataIndex) { + return this.dataShapes[dataIndex]; + }; + InMemoryDataset.prototype.getData = function () { + return this.dataset; + }; + InMemoryDataset.prototype.getStats = function () { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + return this.dataset.map(function (d) { return _this.getStatsForData(d); }); + }; + InMemoryDataset.prototype.getStatsForData = function (data) { + var inputMin = Number.POSITIVE_INFINITY; + var inputMax = Number.NEGATIVE_INFINITY; + var exampleIndices = data.map(function (example, i) { return i; }); + util.shuffle(exampleIndices); + exampleIndices = + exampleIndices.slice(exampleIndices.length * STATS_SAMPLE_PERCENTAGE); + for (var i = 0; i < exampleIndices.length; i++) { + var inputValues = data[exampleIndices[i]].getValues(); + for (var j = 0; j < inputValues.length; j++) { + inputMin = Math.min(inputMin, inputValues[j]); + inputMax = Math.max(inputMax, inputValues[j]); + } + } + return { + inputMin: inputMin, + inputMax: inputMax, + exampleCount: data.length, + shape: data[0].shape, + }; + }; + InMemoryDataset.prototype.normalizeExamplesToRange = function (examples, curLowerBounds, curUpperBounds, newLowerBounds, newUpperBounds) { + var curBoundsIsPerDimension = (curUpperBounds instanceof Float32Array && + curLowerBounds instanceof Float32Array); + var newBoundsIsPerDimension = (newLowerBounds instanceof Float32Array && + newUpperBounds instanceof Float32Array); + var inputSize = util.sizeFromShape(examples[0].shape); + var newExamples = []; + examples.forEach(function (example) { + var inputValues = example.getValues(); + var normalizedValues = new Float32Array(inputSize); + for (var j = 0; j < inputSize; j++) { + var curLowerBound = curBoundsIsPerDimension ? + curLowerBounds[j] : + curLowerBounds; + var curUpperBound = curBoundsIsPerDimension ? + curUpperBounds[j] : + curUpperBounds; + var curRange = curUpperBound - curLowerBound; + var newLowerBound = newBoundsIsPerDimension ? + newLowerBounds[j] : + newLowerBounds; + var newUpperBound = newBoundsIsPerDimension ? + newUpperBounds[j] : + newUpperBounds; + var newRange = newUpperBound - newLowerBound; + if (curRange === 0) { + normalizedValues[j] = newLowerBound; + } + else { + normalizedValues[j] = newLowerBound + + newRange * (inputValues[j] - curLowerBound) / curRange; + } + } + newExamples.push(ndarray_1.NDArray.make(example.shape, { values: normalizedValues })); + }); + return newExamples; + }; + InMemoryDataset.prototype.computeBounds = function (dataIndex) { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + var size = util.sizeFromShape(this.dataset[dataIndex][0].shape); + this.normalizationInfo[dataIndex] = { + isNormalized: false, + minValues: new Float32Array(size), + maxValues: new Float32Array(size) + }; + for (var i = 0; i < size; i++) { + this.normalizationInfo[dataIndex].minValues[i] = Number.POSITIVE_INFINITY; + this.normalizationInfo[dataIndex].maxValues[i] = Number.NEGATIVE_INFINITY; + } + this.dataset[dataIndex].forEach(function (example) { + var inputValues = example.getValues(); + for (var k = 0; k < size; k++) { + _this.normalizationInfo[dataIndex].minValues[k] = Math.min(_this.normalizationInfo[dataIndex].minValues[k], inputValues[k]); + _this.normalizationInfo[dataIndex].maxValues[k] = Math.max(_this.normalizationInfo[dataIndex].maxValues[k], inputValues[k]); + } + }); + }; + InMemoryDataset.prototype.normalizeWithinBounds = function (dataIndex, lowerBound, upperBound) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + if (dataIndex >= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":22,"./util":86}],7:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":8,"./math/concat3d_util":15,"./math/conv_util":16,"./math/ndarray":22,"./util":86}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":12}],9:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":22,"./session":82}],10:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":7,"./priority_queue":81}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":5,"./dataset":6,"./graph":7,"./graph_runner":9,"./initializers":12,"./input_provider":13,"./math/conv_util":16,"./math/math":19,"./math/math_cpu":20,"./math/math_gpu":21,"./math/ndarray":22,"./math/webgl/gpgpu_context":35,"./math/webgl/gpgpu_util":36,"./math/webgl/render_ndarray_gpu_util":48,"./math/webgl/webgl_util":58,"./optimizer":80,"./session":82,"./sgd_optimizer":84,"./util":86}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":22}],13:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":22,"./util":86}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":22}],15:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":86}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":86}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":22}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":86,"./concat3d_util":15,"./copy2d_util":17,"./ndarray":22}],20:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":16,"../util":86,"./concat3d_util":15,"./copy2d_util":17,"./math":19,"./ndarray":22}],21:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":86,"./concat3d_util":15,"./conv_util":16,"./math":19,"./ndarray":22,"./webgl/addscaledmat_gpu":23,"./webgl/addsubmuldiv_gpu":24,"./webgl/argmaxequals_gpu":25,"./webgl/argminmax_gpu":26,"./webgl/avg_pool_gpu":27,"./webgl/batchnorm_gpu":28,"./webgl/concat3d_gpu":30,"./webgl/conv_backprop_gpu":31,"./webgl/conv_gpu":32,"./webgl/copy_gpu":33,"./webgl/exp_gpu":34,"./webgl/gpgpu_context":35,"./webgl/gpgpu_util":36,"./webgl/log_gpu":37,"./webgl/logsumexp_gpu":38,"./webgl/max_pool_backprop_gpu":39,"./webgl/max_pool_gpu":40,"./webgl/min_pool_gpu":41,"./webgl/minmax_gpu":42,"./webgl/mulmat_gpu":43,"./webgl/neg_gpu":44,"./webgl/pool_gpu":45,"./webgl/reducesum_gpu":46,"./webgl/relu_gpu":47,"./webgl/reshape_gpu":49,"./webgl/resize_bilinear_gpu":50,"./webgl/shader_compiler":51,"./webgl/sigmoid_gpu":52,"./webgl/step_gpu":53,"./webgl/texture_manager":55,"./webgl/trig_gpu":56,"./webgl/webgl_util":58}],22:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":86,"./webgl/webgl_util":58}],23:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":35}],24:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":19,"./binaryop_gpu":29}],25:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":26}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":58}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":45}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":35}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":16}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":16,"./conv_gpu":32}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":16}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":57}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":36,"./tex_util":54,"./webgl_util":58}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":54,"./webgl_util":58}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":57}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":35}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":16}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":45}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":45}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":58}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":19,"./shader_compiler":51}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":57}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":16,"./webgl_util":58}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":35}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":57}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":58}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":86}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":16}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":86}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":57}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":57}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":57}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":35}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":86}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":7,"./graph_util":10,"./ops/add":60,"./ops/argmax":61,"./ops/argmaxequals":62,"./ops/concat3d":63,"./ops/convolution":64,"./ops/divide":65,"./ops/element_wise_activation":66,"./ops/element_wise_cost":67,"./ops/exp":68,"./ops/linear_combination":69,"./ops/log":70,"./ops/matmul":71,"./ops/max_pool":72,"./ops/multiply":73,"./ops/reduce_sum":75,"./ops/reshape":76,"./ops/softmax":77,"./ops/split":78,"./ops/subtract":79}],60:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":10,"../math/ndarray":22,"../util":86,"./op":74}],61:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":74}],62:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":74}],63:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":15,"./op":74}],64:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":16,"../util":86,"./op":74}],65:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":10,"../util":86,"./op":74}],66:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":14,"./op":74}],67:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":10,"../math/cost_functions":18,"../math/ndarray":22,"../util":86,"./op":74}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":10,"./op":74}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":10,"./op":74}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":10,"./op":74}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":10,"../math/math":19,"./op":74}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":16,"../util":86,"./op":74}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":10,"../util":86,"./op":74}],74:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],75:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":10,"../math/ndarray":22,"../util":86,"./op":74}],76:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":86,"./op":74}],77:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":7,"../math/ndarray":22,"../util":86,"./op":74}],78:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":10,"../util":86,"./op":74}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":10,"../math/ndarray":22,"../util":86,"./op":74}],80:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],81:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],82:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":22,"./operation_emitter":59,"./session_util":83,"./tensor_array_map":85,"./util":86}],83:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":7,"./graph_util":10,"./math/ndarray":22,"./util":86}],84:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":22,"./optimizer":80,"./session_util":83,"./tensor_array_map":85}],85:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],86:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[1]); diff --git a/demos/homepage/index.md b/demos/homepage/index.md new file mode 100644 index 0000000000..fd0dcb89f7 --- /dev/null +++ b/demos/homepage/index.md @@ -0,0 +1,256 @@ +--- +layout: default +--- + + + + +
+
+
+

deeplearn.js is an open-source library that brings performant machine learning building blocks to the web, allowing you to train neural networks in a browser or run pre-trained models in inference mode.

+
+
+
+

We provide two APIs, an + immediate execution model (think NumPy) + and a deferred execution model + mirroring the TensorFlow API.

deeplearn.js + was originally developed by the Google Brain PAIR team to build powerful + interactive machine learning tools for the browser. You can use the library + for everything from education, to model understanding, to art projects.

+
+
+
+ +
+
+
+

Examples

+
+
+ + + + +
+ + +
+
+
+

Ready to Get Started?

+ {% assign default_paths = site.pages | map: "path" %} + {% assign page_paths = site.header_pages | default: default_paths %} +
    + {% for path in default_paths %} + {% assign my_page = site.pages | where: "path", path | first %} + {% assign title = my_page.title | trim %} + {% if title %} +
  • + + {{my_page.title | escape }} + +
  • + {% endif %} + {% endfor %} +
+
+
+
+ + +
+
+
+

Acknowledgements

+
+
+
+ +
+
+
+

+ deeplearn.js was originally developed by Nikhil Thorat, Daniel Smilkov and Charles Nicholson. +

+
+
+
+

+ We would like to acknowledge Chi Zeng, David Farhi, Mahima Pushkarna, + Minsuk (Brian) Kahng, James Wexler, the entire Big Picture group and + Google Brain PAIR for providing support for the project. +

+
+
+
+ diff --git a/demos/homepage/index.ts b/demos/homepage/index.ts new file mode 100644 index 0000000000..78c81d45d6 --- /dev/null +++ b/demos/homepage/index.ts @@ -0,0 +1,150 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMathGPU} from '../deeplearnjs'; +import {ActivationFunction, ColorMode, CPPN} from '../nn-art/cppn'; + +try { + const math = new NDArrayMathGPU(); + startCPPN(); +} catch (e) { + document.getElementById('disabled-demo-overlay').style.display = ''; +} + +function startCPPN() { + const MAX_Z_SCALE = 400; + const DEFAULT_Z_SCALE = 1; + const NUM_NEURONS = 30; + const DEFAULT_NUM_LAYERS = 2; + const WEIGHTS_STDEV = 0.6; + + const inferenceCanvas = + document.querySelector('#inference') as HTMLCanvasElement; + const cppn = new CPPN(inferenceCanvas); + + cppn.setActivationFunction('tanh'); + cppn.setColorMode('rgb'); + cppn.setNumLayers(DEFAULT_NUM_LAYERS); + cppn.setZ1Scale(convertZScale(DEFAULT_Z_SCALE)); + cppn.setZ2Scale(convertZScale(DEFAULT_Z_SCALE)); + cppn.generateWeights(NUM_NEURONS, WEIGHTS_STDEV); + cppn.start(); + + const currentColorElement = + document.querySelector('#colormode') as HTMLInputElement; + + document.querySelector('#color-selector')!.addEventListener( + // tslint:disable-next-line:no-any + 'click', (event: any) => { + const colorMode = + (event.target as HTMLElement).getAttribute('data-val') as ColorMode; + currentColorElement.value = colorMode; + cppn.setColorMode(colorMode); + }); + + const currentActivationFnElement = + document.querySelector('#activation-fn') as HTMLInputElement; + document.querySelector('#activation-selector')!.addEventListener( + // tslint:disable-next-line:no-any + 'click', (event: any) => { + const activationFn = + (event.target as HTMLElement).getAttribute('data-val') as + ActivationFunction; + currentActivationFnElement.value = activationFn; + cppn.setActivationFunction(activationFn); + }); + + const layersSlider = + document.querySelector('#layers-slider') as HTMLInputElement; + const layersCountElement = + document.querySelector('#layers-count') as HTMLDivElement; + layersSlider!.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + const numLayers = (event as any).target.value; + layersCountElement.innerText = '' + numLayers; + cppn.setNumLayers(numLayers); + }); + layersCountElement.innerText = '' + DEFAULT_NUM_LAYERS; + + const z1Slider = document.querySelector('#z1-slider') as HTMLInputElement; + z1Slider.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + const z1Scale = (event as any).target.value; + cppn.setZ1Scale(convertZScale(z1Scale)); + }); + + const z2Slider = document.querySelector('#z2-slider') as HTMLInputElement; + z2Slider.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + const z2Scale = (event as any).target.value; + cppn.setZ2Scale(convertZScale(z2Scale)); + }); + + const randomizeButton = + document.querySelector('#random') as HTMLButtonElement; + randomizeButton.addEventListener('click', () => { + cppn.generateWeights(NUM_NEURONS, WEIGHTS_STDEV); + if (!playing) { + cppn.start(); + requestAnimationFrame(() => { + cppn.stopInferenceLoop(); + }); + } + }); + + let playing = true; + const toggleButton = document.querySelector('#toggle') as HTMLButtonElement; + toggleButton.addEventListener('click', () => { + playing = !playing; + if (playing) { + toggleButton.innerHTML = 'STOP'; + cppn.start(); + } else { + toggleButton.innerHTML = 'START'; + cppn.stopInferenceLoop(); + } + }); + + let canvasOnScreenLast = true; + let scrollEventScheduled = false; + const mainElement = document.querySelector('main') as HTMLElement; + mainElement.addEventListener('scroll', () => { + if (!scrollEventScheduled) { + window.requestAnimationFrame(() => { + const canvasOnScreen = isCanvasOnScreen(); + if (canvasOnScreen !== canvasOnScreenLast) { + if (canvasOnScreen) { + if (playing) { + cppn.start(); + } + } else { + cppn.stopInferenceLoop(); + } + canvasOnScreenLast = canvasOnScreen; + } + scrollEventScheduled = false; + }); + } + scrollEventScheduled = true; + }); + + function isCanvasOnScreen() { + return mainElement.scrollTop < inferenceCanvas.offsetHeight; + } + + function convertZScale(z: number): number { + return (103 - z); + } +} diff --git a/demos/imagenet/bundle.js b/demos/imagenet/bundle.js new file mode 100644 index 0000000000..44332e7744 --- /dev/null +++ b/demos/imagenet/bundle.js @@ -0,0 +1,9296 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":26,"./util":90}],11:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":12,"./math/concat3d_util":19,"./math/conv_util":20,"./math/ndarray":26,"./util":90}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":16}],13:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":26,"./session":86}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":11,"./priority_queue":85}],15:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":9,"./dataset":10,"./graph":11,"./graph_runner":13,"./initializers":16,"./input_provider":17,"./math/conv_util":20,"./math/math":23,"./math/math_cpu":24,"./math/math_gpu":25,"./math/ndarray":26,"./math/webgl/gpgpu_context":39,"./math/webgl/gpgpu_util":40,"./math/webgl/render_ndarray_gpu_util":52,"./math/webgl/webgl_util":62,"./optimizer":84,"./session":86,"./sgd_optimizer":88,"./util":90}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":26}],17:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":26,"./util":90}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":26}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":90}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":90}],21:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],22:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":26}],23:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":90,"./concat3d_util":19,"./copy2d_util":21,"./ndarray":26}],24:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":20,"../util":90,"./concat3d_util":19,"./copy2d_util":21,"./math":23,"./ndarray":26}],25:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":90,"./concat3d_util":19,"./conv_util":20,"./math":23,"./ndarray":26,"./webgl/addscaledmat_gpu":27,"./webgl/addsubmuldiv_gpu":28,"./webgl/argmaxequals_gpu":29,"./webgl/argminmax_gpu":30,"./webgl/avg_pool_gpu":31,"./webgl/batchnorm_gpu":32,"./webgl/concat3d_gpu":34,"./webgl/conv_backprop_gpu":35,"./webgl/conv_gpu":36,"./webgl/copy_gpu":37,"./webgl/exp_gpu":38,"./webgl/gpgpu_context":39,"./webgl/gpgpu_util":40,"./webgl/log_gpu":41,"./webgl/logsumexp_gpu":42,"./webgl/max_pool_backprop_gpu":43,"./webgl/max_pool_gpu":44,"./webgl/min_pool_gpu":45,"./webgl/minmax_gpu":46,"./webgl/mulmat_gpu":47,"./webgl/neg_gpu":48,"./webgl/pool_gpu":49,"./webgl/reducesum_gpu":50,"./webgl/relu_gpu":51,"./webgl/reshape_gpu":53,"./webgl/resize_bilinear_gpu":54,"./webgl/shader_compiler":55,"./webgl/sigmoid_gpu":56,"./webgl/step_gpu":57,"./webgl/texture_manager":59,"./webgl/trig_gpu":60,"./webgl/webgl_util":62}],26:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":90,"./webgl/webgl_util":62}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":39}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":23,"./binaryop_gpu":33}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":30}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":62}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":49}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":39}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":20}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":20,"./conv_gpu":36}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":20}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":61}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":40,"./tex_util":58,"./webgl_util":62}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":58,"./webgl_util":62}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":61}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":39}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":20}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":49}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":49}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":62}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":23,"./shader_compiler":55}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":61}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":20,"./webgl_util":62}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":39}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":61}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":62}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":90}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":20}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":90}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":61}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":61}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":61}],61:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":39}],62:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":90}],63:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":11,"./graph_util":14,"./ops/add":64,"./ops/argmax":65,"./ops/argmaxequals":66,"./ops/concat3d":67,"./ops/convolution":68,"./ops/divide":69,"./ops/element_wise_activation":70,"./ops/element_wise_cost":71,"./ops/exp":72,"./ops/linear_combination":73,"./ops/log":74,"./ops/matmul":75,"./ops/max_pool":76,"./ops/multiply":77,"./ops/reduce_sum":79,"./ops/reshape":80,"./ops/softmax":81,"./ops/split":82,"./ops/subtract":83}],64:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":14,"../math/ndarray":26,"../util":90,"./op":78}],65:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":78}],66:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":78}],67:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":19,"./op":78}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":20,"../util":90,"./op":78}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":14,"../util":90,"./op":78}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":18,"./op":78}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":14,"../math/cost_functions":22,"../math/ndarray":26,"../util":90,"./op":78}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":14,"./op":78}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":14,"./op":78}],74:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":14,"./op":78}],75:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":14,"../math/math":23,"./op":78}],76:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":20,"../util":90,"./op":78}],77:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":14,"../util":90,"./op":78}],78:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":14,"../math/ndarray":26,"../util":90,"./op":78}],80:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":90,"./op":78}],81:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":11,"../math/ndarray":26,"../util":90,"./op":78}],82:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":14,"../util":90,"./op":78}],83:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":14,"../math/ndarray":26,"../util":90,"./op":78}],84:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],85:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],86:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":26,"./operation_emitter":63,"./session_util":87,"./tensor_array_map":89,"./util":90}],87:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":11,"./graph_util":14,"./math/ndarray":26,"./util":90}],88:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":26,"./optimizer":84,"./session_util":87,"./tensor_array_map":89}],89:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],90:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[3]) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJkZW1vcy9kZW1vLWZvb3Rlci50cyIsImRlbW9zL2RlbW8taGVhZGVyLnRzIiwiZGVtb3MvaW1hZ2VuZXQvaW1hZ2VuZXQtZGVtby50cyIsImRlbW9zL2xlYXJuanMudHMiLCJkZW1vcy9tb2RlbHMvaW1hZ2VuZXRfY2xhc3Nlcy50cyIsImRlbW9zL21vZGVscy9pbWFnZW5ldF91dGlsLnRzIiwiZGVtb3MvbW9kZWxzL3NxdWVlemVuZXQudHMiLCJkZW1vcy9wb2x5bWVyLXNwZWMudHMiLCJzcmMvY2hlY2twb2ludF9sb2FkZXIudHMiLCJzcmMvZGF0YXNldC50cyIsInNyYy9ncmFwaC50cyIsInNyYy9ncmFwaF9sYXllcnMudHMiLCJzcmMvZ3JhcGhfcnVubmVyLnRzIiwic3JjL2dyYXBoX3V0aWwudHMiLCJzcmMvaW5kZXgudHMiLCJzcmMvaW5pdGlhbGl6ZXJzLnRzIiwic3JjL2lucHV0X3Byb3ZpZGVyLnRzIiwic3JjL21hdGgvYWN0aXZhdGlvbl9mdW5jdGlvbnMudHMiLCJzcmMvbWF0aC9jb25jYXQzZF91dGlsLnRzIiwic3JjL21hdGgvY29udl91dGlsLnRzIiwic3JjL21hdGgvY29weTJkX3V0aWwudHMiLCJzcmMvbWF0aC9jb3N0X2Z1bmN0aW9ucy50cyIsInNyYy9tYXRoL21hdGgudHMiLCJzcmMvbWF0aC9tYXRoX2NwdS50cyIsInNyYy9tYXRoL21hdGhfZ3B1LnRzIiwic3JjL21hdGgvbmRhcnJheS50cyIsInNyYy9tYXRoL3dlYmdsL2FkZHNjYWxlZG1hdF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9hZGRzdWJtdWxkaXZfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYXJnbWF4ZXF1YWxzX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2FyZ21pbm1heF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9hdmdfcG9vbF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9iYXRjaG5vcm1fZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYmluYXJ5b3BfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvY29uY2F0M2RfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvY29udl9iYWNrcHJvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb252X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2NvcHlfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvZXhwX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQudHMiLCJzcmMvbWF0aC93ZWJnbC9ncGdwdV91dGlsLnRzIiwic3JjL21hdGgvd2ViZ2wvbG9nX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2xvZ3N1bWV4cF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9tYXhfcG9vbF9iYWNrcHJvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9tYXhfcG9vbF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9taW5fcG9vbF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9taW5tYXhfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbXVsbWF0X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL25lZ19ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3JlZHVjZXN1bV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9yZWx1X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3JlbmRlcl9uZGFycmF5X2dwdV91dGlsLnRzIiwic3JjL21hdGgvd2ViZ2wvcmVzaGFwZV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9yZXNpemVfYmlsaW5lYXJfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvc2hhZGVyX2NvbXBpbGVyLnRzIiwic3JjL21hdGgvd2ViZ2wvc2lnbW9pZF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9zdGVwX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3RleF91dGlsLnRzIiwic3JjL21hdGgvd2ViZ2wvdGV4dHVyZV9tYW5hZ2VyLnRzIiwic3JjL21hdGgvd2ViZ2wvdHJpZ19ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC91bmFyeW9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3dlYmdsX3V0aWwudHMiLCJzcmMvb3BlcmF0aW9uX2VtaXR0ZXIudHMiLCJzcmMvb3BzL2FkZC50cyIsInNyYy9vcHMvYXJnbWF4LnRzIiwic3JjL29wcy9hcmdtYXhlcXVhbHMudHMiLCJzcmMvb3BzL2NvbmNhdDNkLnRzIiwic3JjL29wcy9jb252b2x1dGlvbi50cyIsInNyYy9vcHMvZGl2aWRlLnRzIiwic3JjL29wcy9lbGVtZW50X3dpc2VfYWN0aXZhdGlvbi50cyIsInNyYy9vcHMvZWxlbWVudF93aXNlX2Nvc3QudHMiLCJzcmMvb3BzL2V4cC50cyIsInNyYy9vcHMvbGluZWFyX2NvbWJpbmF0aW9uLnRzIiwic3JjL29wcy9sb2cudHMiLCJzcmMvb3BzL21hdG11bC50cyIsInNyYy9vcHMvbWF4X3Bvb2wudHMiLCJzcmMvb3BzL211bHRpcGx5LnRzIiwic3JjL29wcy9vcC50cyIsInNyYy9vcHMvcmVkdWNlX3N1bS50cyIsInNyYy9vcHMvcmVzaGFwZS50cyIsInNyYy9vcHMvc29mdG1heC50cyIsInNyYy9vcHMvc3BsaXQudHMiLCJzcmMvb3BzL3N1YnRyYWN0LnRzIiwic3JjL29wdGltaXplci50cyIsInNyYy9wcmlvcml0eV9xdWV1ZS50cyIsInNyYy9zZXNzaW9uLnRzIiwic3JjL3Nlc3Npb25fdXRpbC50cyIsInNyYy9zZ2Rfb3B0aW1pemVyLnRzIiwic3JjL3RlbnNvcl9hcnJheV9tYXAudHMiLCJzcmMvdXRpbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ2NBLE9BQU8sQ0FBQyxFQUFDLEVBQUUsRUFBRSxhQUFhLEVBQUMsQ0FBQyxDQUFDOzs7QUNBN0IsT0FBTyxDQUFDLEVBQUMsRUFBRSxFQUFFLGFBQWEsRUFBQyxDQUFDLENBQUM7Ozs7Ozs7Ozs7Ozs7OztBQ0M3QiwwQkFBd0I7QUFDeEIsMEJBQXdCO0FBQ3hCLHNDQUFzRztBQUV0Ryx1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RSxDQUFDLEVBQUUsQ0FBQztRQUMzQixlQUFlLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFDRCxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDekIsTUFBTSxDQUFDLGVBQWUsQ0FBQztBQUN6QixDQUFDO0FBUEQsc0RBT0MiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuUG9seW1lcih7aXM6ICdkZW1vLWZvb3Rlcid9KTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblBvbHltZXIoe2lzOiAnZGVtby1oZWFkZXInfSk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAnLi4vZGVtby1oZWFkZXInO1xuaW1wb3J0ICcuLi9kZW1vLWZvb3Rlcic7XG5pbXBvcnQge0FycmF5M0QsIGdwZ3B1X3V0aWwsIEdQR1BVQ29udGV4dCwgTkRBcnJheSwgTkRBcnJheU1hdGhDUFUsIE5EQXJyYXlNYXRoR1BVfSBmcm9tICcuLi9sZWFybmpzJztcbmltcG9ydCAqIGFzIGltYWdlbmV0X2NsYXNzZXMgZnJvbSAnLi4vbW9kZWxzL2ltYWdlbmV0X2NsYXNzZXMnO1xuaW1wb3J0ICogYXMgaW1hZ2VuZXRfdXRpbCBmcm9tICcuLi9tb2RlbHMvaW1hZ2VuZXRfdXRpbCc7XG5pbXBvcnQge1NxdWVlemVOZXR9IGZyb20gJy4uL21vZGVscy9zcXVlZXplbmV0Jztcbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby11bnVzZWQtdmFyaWFibGVcbmltcG9ydCB7UG9seW1lckVsZW1lbnQsIFBvbHltZXJIVE1MRWxlbWVudH0gZnJvbSAnLi4vcG9seW1lci1zcGVjJztcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOnZhcmlhYmxlLW5hbWVcbmV4cG9ydCBjb25zdCBJbWFnZW5ldERlbW9Qb2x5bWVyID0gUG9seW1lckVsZW1lbnQoe1xuICBpczogJ2ltYWdlbmV0LWRlbW8nLFxuICBwcm9wZXJ0aWVzOiB7XG4gICAgbGF5ZXJOYW1lczogQXJyYXksXG4gICAgc2VsZWN0ZWRMYXllck5hbWU6IFN0cmluZyxcbiAgICBpbnB1dE5hbWVzOiBBcnJheSxcbiAgICBzZWxlY3RlZElucHV0TmFtZTogU3RyaW5nXG4gIH1cbn0pO1xuXG4vKipcbiAqIE5PVEU6IFRvIHVzZSB0aGUgd2ViY2FtIHdpdGhvdXQgU1NMLCB1c2UgdGhlIGNocm9tZSBmbGFnOlxuICogLS11bnNhZmVseS10cmVhdC1pbnNlY3VyZS1vcmlnaW4tYXMtc2VjdXJlPVxcXG4gKiAgICAgaHR0cDovL2xvY2FsaG9zdDo1NDMyXG4gKi9cblxuY29uc3QgTlVNX0NMQVNTRVMgPSAxMDAwO1xuY29uc3QgSU1BR0VfU0laRSA9IDIyNztcbmNvbnN0IFRPUF9LX0NMQVNTRVMgPSA1O1xuXG5jb25zdCBJTlBVVF9OQU1FUyA9IFsnY2F0JywgJ2RvZzEnLCAnZG9nMicsICdiZWVyYm90dGxlJywgJ3BpYW5vJywgJ3NheG9waG9uZSddO1xuZXhwb3J0IGNsYXNzIEltYWdlbmV0RGVtbyBleHRlbmRzIEltYWdlbmV0RGVtb1BvbHltZXIge1xuICBwcml2YXRlIHZhcmlhYmxlczoge1t2YXJOYW1lOiBzdHJpbmddOiBOREFycmF5fTtcblxuICBwcml2YXRlIG1hdGg6IE5EQXJyYXlNYXRoR1BVO1xuICBwcml2YXRlIG1hdGhDUFU6IE5EQXJyYXlNYXRoQ1BVO1xuICBwcml2YXRlIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIHByaXZhdGUgZ3BncHU6IEdQR1BVQ29udGV4dDtcbiAgcHJpdmF0ZSByZW5kZXJHcmF5c2NhbGVDaGFubmVsc0NvbGxhZ2VTaGFkZXI6IFdlYkdMU2hhZGVyO1xuXG4gIHByaXZhdGUgc3F1ZWV6ZU5ldDogU3F1ZWV6ZU5ldDtcblxuICBwcml2YXRlIHdlYmNhbVZpZGVvRWxlbWVudDogSFRNTFZpZGVvRWxlbWVudDtcbiAgcHJpdmF0ZSBzdGF0aWNJbWdFbGVtZW50OiBIVE1MSW1hZ2VFbGVtZW50O1xuXG4gIHByaXZhdGUgbGF5ZXJOYW1lczogc3RyaW5nW107XG4gIHByaXZhdGUgc2VsZWN0ZWRMYXllck5hbWU6IHN0cmluZztcbiAgcHJpdmF0ZSBpbnB1dE5hbWVzOiBzdHJpbmdbXTtcbiAgcHJpdmF0ZSBzZWxlY3RlZElucHV0TmFtZTogc3RyaW5nO1xuXG4gIHByaXZhdGUgaW5mZXJlbmNlQ2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudDtcblxuICByZWFkeSgpIHtcbiAgICB0aGlzLmluZmVyZW5jZUNhbnZhcyA9XG4gICAgICAgIHRoaXMucXVlcnlTZWxlY3RvcignI2luZmVyZW5jZS1jYW52YXMnKSBhcyBIVE1MQ2FudmFzRWxlbWVudDtcbiAgICB0aGlzLnN0YXRpY0ltZ0VsZW1lbnQgPVxuICAgICAgICB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNzdGF0aWNJbWcnKSBhcyBIVE1MSW1hZ2VFbGVtZW50O1xuICAgIHRoaXMud2ViY2FtVmlkZW9FbGVtZW50ID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjd2ViY2FtVmlkZW8nKSBhcyBIVE1MVmlkZW9FbGVtZW50O1xuXG4gICAgdGhpcy5sYXllck5hbWVzID0gW107XG4gICAgdGhpcy5zZWxlY3RlZExheWVyTmFtZSA9ICdjb252XzEnO1xuXG4gICAgY29uc3QgaW5wdXREcm9wZG93biA9IHRoaXMucXVlcnlTZWxlY3RvcignI2lucHV0LWRyb3Bkb3duJykhO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICBpbnB1dERyb3Bkb3duLmFkZEV2ZW50TGlzdGVuZXIoJ2lyb24tYWN0aXZhdGUnLCAoZXZlbnQ6IGFueSkgPT4ge1xuICAgICAgY29uc3Qgc2VsZWN0ZWRJbnB1dE5hbWUgPSBldmVudC5kZXRhaWwuc2VsZWN0ZWQ7XG4gICAgICBpZiAoc2VsZWN0ZWRJbnB1dE5hbWUgPT09ICd3ZWJjYW0nKSB7XG4gICAgICAgIHRoaXMud2ViY2FtVmlkZW9FbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnJztcbiAgICAgICAgdGhpcy5zdGF0aWNJbWdFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLndlYmNhbVZpZGVvRWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICB0aGlzLnN0YXRpY0ltZ0VsZW1lbnQuc3R5bGUuZGlzcGxheSA9ICcnO1xuICAgICAgfVxuICAgICAgdGhpcy5zdGF0aWNJbWdFbGVtZW50LnNyYyA9ICdpbWFnZXMvJyArIGV2ZW50LmRldGFpbC5zZWxlY3RlZCArICcuanBnJztcbiAgICB9KTtcblxuICAgIGlmIChuYXZpZ2F0b3IuZ2V0VXNlck1lZGlhKSB7XG4gICAgICBuYXZpZ2F0b3IuZ2V0VXNlck1lZGlhKFxuICAgICAgICAgIHt2aWRlbzogdHJ1ZX0sXG4gICAgICAgICAgKHN0cmVhbSkgPT4ge1xuICAgICAgICAgICAgdGhpcy53ZWJjYW1WaWRlb0VsZW1lbnQuc3JjID0gd2luZG93LlVSTC5jcmVhdGVPYmplY3RVUkwoc3RyZWFtKTtcbiAgICAgICAgICAgIHRoaXMuaW5pdFdpdGhXZWJjYW0oKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIChlcnJvcikgPT4ge1xuICAgICAgICAgICAgY29uc29sZS5sb2coZXJyb3IpO1xuICAgICAgICAgICAgdGhpcy5pbml0V2l0aG91dFdlYmNhbSgpO1xuICAgICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmluaXRXaXRob3V0V2ViY2FtKCk7XG4gICAgfVxuXG4gICAgdGhpcy5nbCA9IGdwZ3B1X3V0aWwuY3JlYXRlV2ViR0xDb250ZXh0KHRoaXMuaW5mZXJlbmNlQ2FudmFzKTtcbiAgICB0aGlzLmdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCh0aGlzLmdsKTtcbiAgICB0aGlzLm1hdGggPSBuZXcgTkRBcnJheU1hdGhHUFUodGhpcy5ncGdwdSk7XG4gICAgdGhpcy5tYXRoQ1BVID0gbmV3IE5EQXJyYXlNYXRoQ1BVKCk7XG5cbiAgICB0aGlzLnNxdWVlemVOZXQgPSBuZXcgU3F1ZWV6ZU5ldCh0aGlzLmdwZ3B1LCB0aGlzLm1hdGgpO1xuICAgIHRoaXMuc3F1ZWV6ZU5ldC5sb2FkVmFyaWFibGVzKCkudGhlbigoKSA9PiB7XG4gICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5hbmltYXRlKCkpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5yZW5kZXJHcmF5c2NhbGVDaGFubmVsc0NvbGxhZ2VTaGFkZXIgPVxuICAgICAgICBpbWFnZW5ldF91dGlsLmdldFJlbmRlckdyYXlzY2FsZUNoYW5uZWxzQ29sbGFnZVNoYWRlcih0aGlzLmdwZ3B1KTtcbiAgfVxuXG4gIHByaXZhdGUgaW5pdFdpdGhvdXRXZWJjYW0oKSB7XG4gICAgdGhpcy5pbnB1dE5hbWVzID0gSU5QVVRfTkFNRVM7XG4gICAgdGhpcy5zZWxlY3RlZElucHV0TmFtZSA9ICdjYXQnO1xuICAgIHRoaXMuc3RhdGljSW1nRWxlbWVudC5zcmMgPSAnaW1hZ2VzL2NhdC5qcGcnO1xuICAgIHRoaXMud2ViY2FtVmlkZW9FbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgdGhpcy5zdGF0aWNJbWdFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnJztcblxuICAgIGlmIChsb2NhdGlvbi5wcm90b2NvbCAhPT0gJ2h0dHBzOicpIHtcbiAgICAgICh0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNzc2wtbWVzc2FnZScpIGFzIEhUTUxFbGVtZW50KS5zdHlsZS5kaXNwbGF5ID1cbiAgICAgICAgICAnYmxvY2snO1xuICAgIH1cblxuICAgICh0aGlzLnF1ZXJ5U2VsZWN0b3IoJyN3ZWJjYW0tbWVzc2FnZScpIGFzIEhUTUxFbGVtZW50KS5zdHlsZS5kaXNwbGF5ID1cbiAgICAgICAgJ2Jsb2NrJztcbiAgfVxuXG4gIHByaXZhdGUgaW5pdFdpdGhXZWJjYW0oKSB7XG4gICAgY29uc3QgaW5wdXROYW1lcyA9IElOUFVUX05BTUVTLnNsaWNlKCk7XG4gICAgaW5wdXROYW1lcy51bnNoaWZ0KCd3ZWJjYW0nKTtcbiAgICB0aGlzLmlucHV0TmFtZXMgPSBpbnB1dE5hbWVzO1xuICAgIHRoaXMuc2VsZWN0ZWRJbnB1dE5hbWUgPSAnd2ViY2FtJztcbiAgfVxuXG4gIHByaXZhdGUgYW5pbWF0ZSgpIHtcbiAgICBjb25zdCBzdGFydFRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuICAgIGNvbnN0IGlzV2ViY2FtID0gdGhpcy5zZWxlY3RlZElucHV0TmFtZSA9PT0gJ3dlYmNhbSc7XG5cbiAgICBjb25zdCBjYW52YXNUZXh0dXJlU2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbSU1BR0VfU0laRSwgSU1BR0VfU0laRV07XG4gICAgY29uc3QgY2FudmFzVGV4dHVyZSA9XG4gICAgICAgIHRoaXMubWF0aC5nZXRUZXh0dXJlTWFuYWdlcigpLmFjcXVpcmVUZXh0dXJlKGNhbnZhc1RleHR1cmVTaGFwZSk7XG5cbiAgICBjb25zdCBlbGVtZW50ID0gaXNXZWJjYW0gPyB0aGlzLndlYmNhbVZpZGVvRWxlbWVudCA6IHRoaXMuc3RhdGljSW1nRWxlbWVudDtcbiAgICB0aGlzLmdwZ3B1LnVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZShjYW52YXNUZXh0dXJlLCBlbGVtZW50KTtcblxuICAgIHRoaXMubWF0aC5zY29wZSgoa2VlcCwgdHJhY2spID0+IHtcbiAgICAgIGNvbnN0IHByZXByb2Nlc3NlZElucHV0ID1cbiAgICAgICAgICB0cmFjayh0aGlzLnNxdWVlemVOZXQucHJlcHJvY2Vzc0NvbG9yVGV4dHVyZVRvQXJyYXkzRChcbiAgICAgICAgICAgICAgY2FudmFzVGV4dHVyZSwgY2FudmFzVGV4dHVyZVNoYXBlKSk7XG5cbiAgICAgIGNvbnN0IGluZmVyZW5jZVJlc3VsdCA9IHRoaXMuc3F1ZWV6ZU5ldC5pbmZlcihwcmVwcm9jZXNzZWRJbnB1dCk7XG4gICAgICBjb25zdCBuYW1lZEFjdGl2YXRpb25zID0gaW5mZXJlbmNlUmVzdWx0Lm5hbWVkQWN0aXZhdGlvbnM7XG5cbiAgICAgIHRoaXMubGF5ZXJOYW1lcyA9IE9iamVjdC5rZXlzKG5hbWVkQWN0aXZhdGlvbnMpO1xuICAgICAgdGhpcy5sYXllck5hbWVzLmZvckVhY2gobGF5ZXJOYW1lID0+IHRyYWNrKG5hbWVkQWN0aXZhdGlvbnNbbGF5ZXJOYW1lXSkpO1xuXG4gICAgICBjb25zdCB0b3BDbGFzc2VzVG9Qcm9iYWJpbGl0eSA9XG4gICAgICAgICAgdGhpcy5zcXVlZXplTmV0LmdldFRvcEtDbGFzc2VzKGluZmVyZW5jZVJlc3VsdC5sb2dpdHMsIFRPUF9LX0NMQVNTRVMpO1xuXG4gICAgICBsZXQgY291bnQgPSAwO1xuICAgICAgZm9yIChjb25zdCBjbGFzc05hbWUgaW4gdG9wQ2xhc3Nlc1RvUHJvYmFiaWxpdHkpIHtcbiAgICAgICAgaWYgKCEoY2xhc3NOYW1lIGluIHRvcENsYXNzZXNUb1Byb2JhYmlsaXR5KSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjbGFzcycgKyBjb3VudCkhLmlubmVySFRNTCA9IGNsYXNzTmFtZTtcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Byb2InICsgY291bnQpIS5pbm5lckhUTUwgPVxuICAgICAgICAgICAgJycgKyBNYXRoLmZsb29yKDEwMDAgKiB0b3BDbGFzc2VzVG9Qcm9iYWJpbGl0eVtjbGFzc05hbWVdKSAvIDEwMDA7XG4gICAgICAgIGNvdW50Kys7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVuZFRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKTtcblxuICAgICAgKHRoaXMucXVlcnlTZWxlY3RvcignI3RvdGFsVGltZScpIGFzIEhUTUxEaXZFbGVtZW50KS5pbm5lckhUTUwgPVxuICAgICAgICAgICdsYXN0IGluZmVyZW5jZSB0aW1lOiAnICtcbiAgICAgICAgICBNYXRoLmZsb29yKDEwMDAgKiAoZW5kVGltZSAtIHN0YXJ0VGltZSkpIC8gMTAwMCArICdtcyc7XG5cbiAgICAgIC8vIFJlbmRlciBhY3RpdmF0aW9ucy5cbiAgICAgIGNvbnN0IGFjdGl2YXRpb25OREFycmF5ID0gbmFtZWRBY3RpdmF0aW9uc1t0aGlzLnNlbGVjdGVkTGF5ZXJOYW1lXTtcblxuICAgICAgLy8gQ29tcHV0ZSBtYXggYW5kIG1pbiBwZXIgY2hhbm5lbCBmb3Igbm9ybWFsaXphdGlvbi5cbiAgICAgIGNvbnN0IG1heFZhbHVlcyA9IHRoaXMubWF0aC5tYXhQb29sKFxuICAgICAgICAgIGFjdGl2YXRpb25OREFycmF5LCBhY3RpdmF0aW9uTkRBcnJheS5zaGFwZVsxXSxcbiAgICAgICAgICBhY3RpdmF0aW9uTkRBcnJheS5zaGFwZVsxXSwgMCk7XG4gICAgICBjb25zdCBtaW5WYWx1ZXMgPSB0aGlzLm1hdGgubWluUG9vbChcbiAgICAgICAgICBhY3RpdmF0aW9uTkRBcnJheSwgYWN0aXZhdGlvbk5EQXJyYXkuc2hhcGVbMV0sXG4gICAgICAgICAgYWN0aXZhdGlvbk5EQXJyYXkuc2hhcGVbMV0sIDApO1xuXG4gICAgICAvLyBMb2dpY2FsbHkgcmVzaXplIHRoZSByZW5kZXJpbmcgY2FudmFzLiBUaGUgZGlzcGxheWVkIHdpZHRoIGlzIGZpeGVkLlxuICAgICAgY29uc3QgaW1hZ2VzUGVyUm93ID0gTWF0aC5jZWlsKE1hdGguc3FydChhY3RpdmF0aW9uTkRBcnJheS5zaGFwZVsyXSkpO1xuICAgICAgY29uc3QgbnVtUm93cyA9IE1hdGguY2VpbChhY3RpdmF0aW9uTkRBcnJheS5zaGFwZVsyXSAvIGltYWdlc1BlclJvdyk7XG4gICAgICB0aGlzLmluZmVyZW5jZUNhbnZhcy53aWR0aCA9IGltYWdlc1BlclJvdyAqIGFjdGl2YXRpb25OREFycmF5LnNoYXBlWzBdO1xuICAgICAgdGhpcy5pbmZlcmVuY2VDYW52YXMuaGVpZ2h0ID0gbnVtUm93cyAqIGFjdGl2YXRpb25OREFycmF5LnNoYXBlWzBdO1xuXG4gICAgICBpbWFnZW5ldF91dGlsLnJlbmRlckdyYXlzY2FsZUNoYW5uZWxzQ29sbGFnZShcbiAgICAgICAgICB0aGlzLmdwZ3B1LCB0aGlzLnJlbmRlckdyYXlzY2FsZUNoYW5uZWxzQ29sbGFnZVNoYWRlcixcbiAgICAgICAgICBhY3RpdmF0aW9uTkRBcnJheS5nZXRUZXh0dXJlKCksIG1pblZhbHVlcy5nZXRUZXh0dXJlKCksXG4gICAgICAgICAgbWF4VmFsdWVzLmdldFRleHR1cmUoKSwgYWN0aXZhdGlvbk5EQXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKSxcbiAgICAgICAgICBhY3RpdmF0aW9uTkRBcnJheS5zaGFwZVswXSwgYWN0aXZhdGlvbk5EQXJyYXkuc2hhcGVbMl0sXG4gICAgICAgICAgdGhpcy5pbmZlcmVuY2VDYW52YXMud2lkdGgsIG51bVJvd3MpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5tYXRoLmdldFRleHR1cmVNYW5hZ2VyKCkucmVsZWFzZVRleHR1cmUoXG4gICAgICAgIGNhbnZhc1RleHR1cmUsIGNhbnZhc1RleHR1cmVTaGFwZSk7XG5cbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5hbmltYXRlKCkpO1xuICB9XG59XG5cbmRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChJbWFnZW5ldERlbW8ucHJvdG90eXBlLmlzLCBJbWFnZW5ldERlbW8pO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyBUaGlzIGZpbGUgaXMganVzdCBhbiBhbGlhcyB0aGF0IHBvaW50cyB0byB0aGUgY3VycmVudCBsZWFybmpzIHZlcnNpb25cbi8vIGF0IHRoaXMgYnJhbmNoLCBzbyBkZW1vcyBjYW4gaW1wb3J0IHRoZSBsaWJyYXJ5IGFzICcuLi9sZWFybmpzJy5cbmV4cG9ydCAqIGZyb20gJy4uL3NyYy9pbmRleCc7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBjb25zdCBJTUFHRU5FVF9DTEFTU0VTOiB7W2tleTogbnVtYmVyXTogc3RyaW5nfSA9IHtcbiAgMDogJ3RlbmNoLCBUaW5jYSB0aW5jYScsXG4gIDE6ICdnb2xkZmlzaCwgQ2FyYXNzaXVzIGF1cmF0dXMnLFxuICAyOiAnZ3JlYXQgd2hpdGUgc2hhcmssIHdoaXRlIHNoYXJrLCBtYW4tZWF0ZXIsIG1hbi1lYXRpbmcgc2hhcmssIENhcmNoYXJvZG9uIGNhcmNoYXJpYXMnLFxuICAzOiAndGlnZXIgc2hhcmssIEdhbGVvY2VyZG8gY3V2aWVyaScsXG4gIDQ6ICdoYW1tZXJoZWFkLCBoYW1tZXJoZWFkIHNoYXJrJyxcbiAgNTogJ2VsZWN0cmljIHJheSwgY3JhbXBmaXNoLCBudW1iZmlzaCwgdG9ycGVkbycsXG4gIDY6ICdzdGluZ3JheScsXG4gIDc6ICdjb2NrJyxcbiAgODogJ2hlbicsXG4gIDk6ICdvc3RyaWNoLCBTdHJ1dGhpbyBjYW1lbHVzJyxcbiAgMTA6ICdicmFtYmxpbmcsIEZyaW5naWxsYSBtb250aWZyaW5naWxsYScsXG4gIDExOiAnZ29sZGZpbmNoLCBDYXJkdWVsaXMgY2FyZHVlbGlzJyxcbiAgMTI6ICdob3VzZSBmaW5jaCwgbGlubmV0LCBDYXJwb2RhY3VzIG1leGljYW51cycsXG4gIDEzOiAnanVuY28sIHNub3diaXJkJyxcbiAgMTQ6ICdpbmRpZ28gYnVudGluZywgaW5kaWdvIGZpbmNoLCBpbmRpZ28gYmlyZCwgUGFzc2VyaW5hIGN5YW5lYScsXG4gIDE1OiAncm9iaW4sIEFtZXJpY2FuIHJvYmluLCBUdXJkdXMgbWlncmF0b3JpdXMnLFxuICAxNjogJ2J1bGJ1bCcsXG4gIDE3OiAnamF5JyxcbiAgMTg6ICdtYWdwaWUnLFxuICAxOTogJ2NoaWNrYWRlZScsXG4gIDIwOiAnd2F0ZXIgb3V6ZWwsIGRpcHBlcicsXG4gIDIxOiAna2l0ZScsXG4gIDIyOiAnYmFsZCBlYWdsZSwgQW1lcmljYW4gZWFnbGUsIEhhbGlhZWV0dXMgbGV1Y29jZXBoYWx1cycsXG4gIDIzOiAndnVsdHVyZScsXG4gIDI0OiAnZ3JlYXQgZ3JleSBvd2wsIGdyZWF0IGdyYXkgb3dsLCBTdHJpeCBuZWJ1bG9zYScsXG4gIDI1OiAnRXVyb3BlYW4gZmlyZSBzYWxhbWFuZGVyLCBTYWxhbWFuZHJhIHNhbGFtYW5kcmEnLFxuICAyNjogJ2NvbW1vbiBuZXd0LCBUcml0dXJ1cyB2dWxnYXJpcycsXG4gIDI3OiAnZWZ0JyxcbiAgMjg6ICdzcG90dGVkIHNhbGFtYW5kZXIsIEFtYnlzdG9tYSBtYWN1bGF0dW0nLFxuICAyOTogJ2F4b2xvdGwsIG11ZCBwdXBweSwgQW1ieXN0b21hIG1leGljYW51bScsXG4gIDMwOiAnYnVsbGZyb2csIFJhbmEgY2F0ZXNiZWlhbmEnLFxuICAzMTogJ3RyZWUgZnJvZywgdHJlZS1mcm9nJyxcbiAgMzI6ICd0YWlsZWQgZnJvZywgYmVsbCB0b2FkLCByaWJiZWQgdG9hZCwgdGFpbGVkIHRvYWQsIEFzY2FwaHVzIHRydWknLFxuICAzMzogJ2xvZ2dlcmhlYWQsIGxvZ2dlcmhlYWQgdHVydGxlLCBDYXJldHRhIGNhcmV0dGEnLFxuICAzNDogJ2xlYXRoZXJiYWNrIHR1cnRsZSwgbGVhdGhlcmJhY2ssIGxlYXRoZXJ5IHR1cnRsZSwgRGVybW9jaGVseXMgY29yaWFjZWEnLFxuICAzNTogJ211ZCB0dXJ0bGUnLFxuICAzNjogJ3RlcnJhcGluJyxcbiAgMzc6ICdib3ggdHVydGxlLCBib3ggdG9ydG9pc2UnLFxuICAzODogJ2JhbmRlZCBnZWNrbycsXG4gIDM5OiAnY29tbW9uIGlndWFuYSwgaWd1YW5hLCBJZ3VhbmEgaWd1YW5hJyxcbiAgNDA6ICdBbWVyaWNhbiBjaGFtZWxlb24sIGFub2xlLCBBbm9saXMgY2Fyb2xpbmVuc2lzJyxcbiAgNDE6ICd3aGlwdGFpbCwgd2hpcHRhaWwgbGl6YXJkJyxcbiAgNDI6ICdhZ2FtYScsXG4gIDQzOiAnZnJpbGxlZCBsaXphcmQsIENobGFteWRvc2F1cnVzIGtpbmdpJyxcbiAgNDQ6ICdhbGxpZ2F0b3IgbGl6YXJkJyxcbiAgNDU6ICdHaWxhIG1vbnN0ZXIsIEhlbG9kZXJtYSBzdXNwZWN0dW0nLFxuICA0NjogJ2dyZWVuIGxpemFyZCwgTGFjZXJ0YSB2aXJpZGlzJyxcbiAgNDc6ICdBZnJpY2FuIGNoYW1lbGVvbiwgQ2hhbWFlbGVvIGNoYW1hZWxlb24nLFxuICA0ODogJ0tvbW9kbyBkcmFnb24sIEtvbW9kbyBsaXphcmQsIGRyYWdvbiBsaXphcmQsIGdpYW50IGxpemFyZCwgVmFyYW51cyBrb21vZG9lbnNpcycsXG4gIDQ5OiAnQWZyaWNhbiBjcm9jb2RpbGUsIE5pbGUgY3JvY29kaWxlLCBDcm9jb2R5bHVzIG5pbG90aWN1cycsXG4gIDUwOiAnQW1lcmljYW4gYWxsaWdhdG9yLCBBbGxpZ2F0b3IgbWlzc2lzc2lwaWVuc2lzJyxcbiAgNTE6ICd0cmljZXJhdG9wcycsXG4gIDUyOiAndGh1bmRlciBzbmFrZSwgd29ybSBzbmFrZSwgQ2FycGhvcGhpcyBhbW9lbnVzJyxcbiAgNTM6ICdyaW5nbmVjayBzbmFrZSwgcmluZy1uZWNrZWQgc25ha2UsIHJpbmcgc25ha2UnLFxuICA1NDogJ2hvZ25vc2Ugc25ha2UsIHB1ZmYgYWRkZXIsIHNhbmQgdmlwZXInLFxuICA1NTogJ2dyZWVuIHNuYWtlLCBncmFzcyBzbmFrZScsXG4gIDU2OiAna2luZyBzbmFrZSwga2luZ3NuYWtlJyxcbiAgNTc6ICdnYXJ0ZXIgc25ha2UsIGdyYXNzIHNuYWtlJyxcbiAgNTg6ICd3YXRlciBzbmFrZScsXG4gIDU5OiAndmluZSBzbmFrZScsXG4gIDYwOiAnbmlnaHQgc25ha2UsIEh5cHNpZ2xlbmEgdG9ycXVhdGEnLFxuICA2MTogJ2JvYSBjb25zdHJpY3RvciwgQ29uc3RyaWN0b3IgY29uc3RyaWN0b3InLFxuICA2MjogJ3JvY2sgcHl0aG9uLCByb2NrIHNuYWtlLCBQeXRob24gc2ViYWUnLFxuICA2MzogJ0luZGlhbiBjb2JyYSwgTmFqYSBuYWphJyxcbiAgNjQ6ICdncmVlbiBtYW1iYScsXG4gIDY1OiAnc2VhIHNuYWtlJyxcbiAgNjY6ICdob3JuZWQgdmlwZXIsIGNlcmFzdGVzLCBzYW5kIHZpcGVyLCBob3JuZWQgYXNwLCBDZXJhc3RlcyBjb3JudXR1cycsXG4gIDY3OiAnZGlhbW9uZGJhY2ssIGRpYW1vbmRiYWNrIHJhdHRsZXNuYWtlLCBDcm90YWx1cyBhZGFtYW50ZXVzJyxcbiAgNjg6ICdzaWRld2luZGVyLCBob3JuZWQgcmF0dGxlc25ha2UsIENyb3RhbHVzIGNlcmFzdGVzJyxcbiAgNjk6ICd0cmlsb2JpdGUnLFxuICA3MDogJ2hhcnZlc3RtYW4sIGRhZGR5IGxvbmdsZWdzLCBQaGFsYW5naXVtIG9waWxpbycsXG4gIDcxOiAnc2NvcnBpb24nLFxuICA3MjogJ2JsYWNrIGFuZCBnb2xkIGdhcmRlbiBzcGlkZXIsIEFyZ2lvcGUgYXVyYW50aWEnLFxuICA3MzogJ2Jhcm4gc3BpZGVyLCBBcmFuZXVzIGNhdmF0aWN1cycsXG4gIDc0OiAnZ2FyZGVuIHNwaWRlciwgQXJhbmVhIGRpYWRlbWF0YScsXG4gIDc1OiAnYmxhY2sgd2lkb3csIExhdHJvZGVjdHVzIG1hY3RhbnMnLFxuICA3NjogJ3RhcmFudHVsYScsXG4gIDc3OiAnd29sZiBzcGlkZXIsIGh1bnRpbmcgc3BpZGVyJyxcbiAgNzg6ICd0aWNrJyxcbiAgNzk6ICdjZW50aXBlZGUnLFxuICA4MDogJ2JsYWNrIGdyb3VzZScsXG4gIDgxOiAncHRhcm1pZ2FuJyxcbiAgODI6ICdydWZmZWQgZ3JvdXNlLCBwYXJ0cmlkZ2UsIEJvbmFzYSB1bWJlbGx1cycsXG4gIDgzOiAncHJhaXJpZSBjaGlja2VuLCBwcmFpcmllIGdyb3VzZSwgcHJhaXJpZSBmb3dsJyxcbiAgODQ6ICdwZWFjb2NrJyxcbiAgODU6ICdxdWFpbCcsXG4gIDg2OiAncGFydHJpZGdlJyxcbiAgODc6ICdBZnJpY2FuIGdyZXksIEFmcmljYW4gZ3JheSwgUHNpdHRhY3VzIGVyaXRoYWN1cycsXG4gIDg4OiAnbWFjYXcnLFxuICA4OTogJ3N1bHBodXItY3Jlc3RlZCBjb2NrYXRvbywgS2FrYXRvZSBnYWxlcml0YSwgQ2FjYXR1YSBnYWxlcml0YScsXG4gIDkwOiAnbG9yaWtlZXQnLFxuICA5MTogJ2NvdWNhbCcsXG4gIDkyOiAnYmVlIGVhdGVyJyxcbiAgOTM6ICdob3JuYmlsbCcsXG4gIDk0OiAnaHVtbWluZ2JpcmQnLFxuICA5NTogJ2phY2FtYXInLFxuICA5NjogJ3RvdWNhbicsXG4gIDk3OiAnZHJha2UnLFxuICA5ODogJ3JlZC1icmVhc3RlZCBtZXJnYW5zZXIsIE1lcmd1cyBzZXJyYXRvcicsXG4gIDk5OiAnZ29vc2UnLFxuICAxMDA6ICdibGFjayBzd2FuLCBDeWdudXMgYXRyYXR1cycsXG4gIDEwMTogJ3R1c2tlcicsXG4gIDEwMjogJ2VjaGlkbmEsIHNwaW55IGFudGVhdGVyLCBhbnRlYXRlcicsXG4gIDEwMzpcbiAgICAgICdwbGF0eXB1cywgZHVja2JpbGwsIGR1Y2tiaWxsZWQgcGxhdHlwdXMsIGR1Y2stYmlsbGVkIHBsYXR5cHVzLCBPcm5pdGhvcmh5bmNodXMgYW5hdGludXMnLFxuICAxMDQ6ICd3YWxsYWJ5LCBicnVzaCBrYW5nYXJvbycsXG4gIDEwNTogJ2tvYWxhLCBrb2FsYSBiZWFyLCBrYW5nYXJvbyBiZWFyLCBuYXRpdmUgYmVhciwgUGhhc2NvbGFyY3RvcyBjaW5lcmV1cycsXG4gIDEwNjogJ3dvbWJhdCcsXG4gIDEwNzogJ2plbGx5IGZpc2gnLFxuICAxMDg6ICdzZWEgYW5lbW9uZSwgYW5lbW9uZScsXG4gIDEwOTogJ2JyYWluIGNvcmFsJyxcbiAgMTEwOiAnZmxhdHdvcm0sIHBsYXR5aGVsbWludGgnLFxuICAxMTE6ICduZW1hdG9kZSwgbmVtYXRvZGUgd29ybSwgcm91bmR3b3JtJyxcbiAgMTEyOiAnY29uY2gnLFxuICAxMTM6ICdzbmFpbCcsXG4gIDExNDogJ3NsdWcnLFxuICAxMTU6ICdzZWEgc2x1ZywgbnVkaWJyYW5jaCcsXG4gIDExNjogJ2NoaXRvbiwgY29hdC1vZi1tYWlsIHNoZWxsLCBzZWEgY3JhZGxlLCBwb2x5cGxhY29waG9yZScsXG4gIDExNzogJ2NoYW1iZXJlZCBuYXV0aWx1cywgcGVhcmx5IG5hdXRpbHVzLCBuYXV0aWx1cycsXG4gIDExODogJ0R1bmdlbmVzcyBjcmFiLCBDYW5jZXIgbWFnaXN0ZXInLFxuICAxMTk6ICdyb2NrIGNyYWIsIENhbmNlciBpcnJvcmF0dXMnLFxuICAxMjA6ICdmaWRkbGVyIGNyYWInLFxuICAxMjE6XG4gICAgICAna2luZyBjcmFiLCBBbGFza2EgY3JhYiwgQWxhc2thbiBraW5nIGNyYWIsIEFsYXNrYSBraW5nIGNyYWIsIFBhcmFsaXRob2RlcyBjYW10c2NoYXRpY2EnLFxuICAxMjI6ICdBbWVyaWNhbiBsb2JzdGVyLCBOb3J0aGVybiBsb2JzdGVyLCBNYWluZSBsb2JzdGVyLCBIb21hcnVzIGFtZXJpY2FudXMnLFxuICAxMjM6XG4gICAgICAnc3BpbnkgbG9ic3RlciwgbGFuZ291c3RlLCByb2NrIGxvYnN0ZXIsIGNyYXdmaXNoLCBjcmF5ZmlzaCwgc2VhIGNyYXdmaXNoJyxcbiAgMTI0OiAnY3JheWZpc2gsIGNyYXdmaXNoLCBjcmF3ZGFkLCBjcmF3ZGFkZHknLFxuICAxMjU6ICdoZXJtaXQgY3JhYicsXG4gIDEyNjogJ2lzb3BvZCcsXG4gIDEyNzogJ3doaXRlIHN0b3JrLCBDaWNvbmlhIGNpY29uaWEnLFxuICAxMjg6ICdibGFjayBzdG9yaywgQ2ljb25pYSBuaWdyYScsXG4gIDEyOTogJ3Nwb29uYmlsbCcsXG4gIDEzMDogJ2ZsYW1pbmdvJyxcbiAgMTMxOiAnbGl0dGxlIGJsdWUgaGVyb24sIEVncmV0dGEgY2FlcnVsZWEnLFxuICAxMzI6ICdBbWVyaWNhbiBlZ3JldCwgZ3JlYXQgd2hpdGUgaGVyb24sIEVncmV0dGEgYWxidXMnLFxuICAxMzM6ICdiaXR0ZXJuJyxcbiAgMTM0OiAnY3JhbmUnLFxuICAxMzU6ICdsaW1wa2luLCBBcmFtdXMgcGljdHVzJyxcbiAgMTM2OiAnRXVyb3BlYW4gZ2FsbGludWxlLCBQb3JwaHlyaW8gcG9ycGh5cmlvJyxcbiAgMTM3OiAnQW1lcmljYW4gY29vdCwgbWFyc2ggaGVuLCBtdWQgaGVuLCB3YXRlciBoZW4sIEZ1bGljYSBhbWVyaWNhbmEnLFxuICAxMzg6ICdidXN0YXJkJyxcbiAgMTM5OiAncnVkZHkgdHVybnN0b25lLCBBcmVuYXJpYSBpbnRlcnByZXMnLFxuICAxNDA6ICdyZWQtYmFja2VkIHNhbmRwaXBlciwgZHVubGluLCBFcm9saWEgYWxwaW5hJyxcbiAgMTQxOiAncmVkc2hhbmssIFRyaW5nYSB0b3RhbnVzJyxcbiAgMTQyOiAnZG93aXRjaGVyJyxcbiAgMTQzOiAnb3lzdGVyY2F0Y2hlciwgb3lzdGVyIGNhdGNoZXInLFxuICAxNDQ6ICdwZWxpY2FuJyxcbiAgMTQ1OiAna2luZyBwZW5ndWluLCBBcHRlbm9keXRlcyBwYXRhZ29uaWNhJyxcbiAgMTQ2OiAnYWxiYXRyb3NzLCBtb2xseW1hd2snLFxuICAxNDc6XG4gICAgICAnZ3JleSB3aGFsZSwgZ3JheSB3aGFsZSwgZGV2aWxmaXNoLCBFc2NocmljaHRpdXMgZ2liYm9zdXMsIEVzY2hyaWNodGl1cyByb2J1c3R1cycsXG4gIDE0ODogJ2tpbGxlciB3aGFsZSwga2lsbGVyLCBvcmNhLCBncmFtcHVzLCBzZWEgd29sZiwgT3JjaW51cyBvcmNhJyxcbiAgMTQ5OiAnZHVnb25nLCBEdWdvbmcgZHVnb24nLFxuICAxNTA6ICdzZWEgbGlvbicsXG4gIDE1MTogJ0NoaWh1YWh1YScsXG4gIDE1MjogJ0phcGFuZXNlIHNwYW5pZWwnLFxuICAxNTM6ICdNYWx0ZXNlIGRvZywgTWFsdGVzZSB0ZXJyaWVyLCBNYWx0ZXNlJyxcbiAgMTU0OiAnUGVraW5lc2UsIFBla2luZ2VzZSwgUGVrZScsXG4gIDE1NTogJ1NoaWgtVHp1JyxcbiAgMTU2OiAnQmxlbmhlaW0gc3BhbmllbCcsXG4gIDE1NzogJ3BhcGlsbG9uJyxcbiAgMTU4OiAndG95IHRlcnJpZXInLFxuICAxNTk6ICdSaG9kZXNpYW4gcmlkZ2ViYWNrJyxcbiAgMTYwOiAnQWZnaGFuIGhvdW5kLCBBZmdoYW4nLFxuICAxNjE6ICdiYXNzZXQsIGJhc3NldCBob3VuZCcsXG4gIDE2MjogJ2JlYWdsZScsXG4gIDE2MzogJ2Jsb29kaG91bmQsIHNsZXV0aGhvdW5kJyxcbiAgMTY0OiAnYmx1ZXRpY2snLFxuICAxNjU6ICdibGFjay1hbmQtdGFuIGNvb25ob3VuZCcsXG4gIDE2NjogJ1dhbGtlciBob3VuZCwgV2Fsa2VyIGZveGhvdW5kJyxcbiAgMTY3OiAnRW5nbGlzaCBmb3hob3VuZCcsXG4gIDE2ODogJ3JlZGJvbmUnLFxuICAxNjk6ICdib3J6b2ksIFJ1c3NpYW4gd29sZmhvdW5kJyxcbiAgMTcwOiAnSXJpc2ggd29sZmhvdW5kJyxcbiAgMTcxOiAnSXRhbGlhbiBncmV5aG91bmQnLFxuICAxNzI6ICd3aGlwcGV0JyxcbiAgMTczOiAnSWJpemFuIGhvdW5kLCBJYml6YW4gUG9kZW5jbycsXG4gIDE3NDogJ05vcndlZ2lhbiBlbGtob3VuZCwgZWxraG91bmQnLFxuICAxNzU6ICdvdHRlcmhvdW5kLCBvdHRlciBob3VuZCcsXG4gIDE3NjogJ1NhbHVraSwgZ2F6ZWxsZSBob3VuZCcsXG4gIDE3NzogJ1Njb3R0aXNoIGRlZXJob3VuZCwgZGVlcmhvdW5kJyxcbiAgMTc4OiAnV2VpbWFyYW5lcicsXG4gIDE3OTogJ1N0YWZmb3Jkc2hpcmUgYnVsbHRlcnJpZXIsIFN0YWZmb3Jkc2hpcmUgYnVsbCB0ZXJyaWVyJyxcbiAgMTgwOlxuICAgICAgJ0FtZXJpY2FuIFN0YWZmb3Jkc2hpcmUgdGVycmllciwgU3RhZmZvcmRzaGlyZSB0ZXJyaWVyLCBBbWVyaWNhbiBwaXQgYnVsbCB0ZXJyaWVyLCBwaXQgYnVsbCB0ZXJyaWVyJyxcbiAgMTgxOiAnQmVkbGluZ3RvbiB0ZXJyaWVyJyxcbiAgMTgyOiAnQm9yZGVyIHRlcnJpZXInLFxuICAxODM6ICdLZXJyeSBibHVlIHRlcnJpZXInLFxuICAxODQ6ICdJcmlzaCB0ZXJyaWVyJyxcbiAgMTg1OiAnTm9yZm9sayB0ZXJyaWVyJyxcbiAgMTg2OiAnTm9yd2ljaCB0ZXJyaWVyJyxcbiAgMTg3OiAnWW9ya3NoaXJlIHRlcnJpZXInLFxuICAxODg6ICd3aXJlLWhhaXJlZCBmb3ggdGVycmllcicsXG4gIDE4OTogJ0xha2VsYW5kIHRlcnJpZXInLFxuICAxOTA6ICdTZWFseWhhbSB0ZXJyaWVyLCBTZWFseWhhbScsXG4gIDE5MTogJ0FpcmVkYWxlLCBBaXJlZGFsZSB0ZXJyaWVyJyxcbiAgMTkyOiAnY2Fpcm4sIGNhaXJuIHRlcnJpZXInLFxuICAxOTM6ICdBdXN0cmFsaWFuIHRlcnJpZXInLFxuICAxOTQ6ICdEYW5kaWUgRGlubW9udCwgRGFuZGllIERpbm1vbnQgdGVycmllcicsXG4gIDE5NTogJ0Jvc3RvbiBidWxsLCBCb3N0b24gdGVycmllcicsXG4gIDE5NjogJ21pbmlhdHVyZSBzY2huYXV6ZXInLFxuICAxOTc6ICdnaWFudCBzY2huYXV6ZXInLFxuICAxOTg6ICdzdGFuZGFyZCBzY2huYXV6ZXInLFxuICAxOTk6ICdTY290Y2ggdGVycmllciwgU2NvdHRpc2ggdGVycmllciwgU2NvdHRpZScsXG4gIDIwMDogJ1RpYmV0YW4gdGVycmllciwgY2hyeXNhbnRoZW11bSBkb2cnLFxuICAyMDE6ICdzaWxreSB0ZXJyaWVyLCBTeWRuZXkgc2lsa3knLFxuICAyMDI6ICdzb2Z0LWNvYXRlZCB3aGVhdGVuIHRlcnJpZXInLFxuICAyMDM6ICdXZXN0IEhpZ2hsYW5kIHdoaXRlIHRlcnJpZXInLFxuICAyMDQ6ICdMaGFzYSwgTGhhc2EgYXBzbycsXG4gIDIwNTogJ2ZsYXQtY29hdGVkIHJldHJpZXZlcicsXG4gIDIwNjogJ2N1cmx5LWNvYXRlZCByZXRyaWV2ZXInLFxuICAyMDc6ICdnb2xkZW4gcmV0cmlldmVyJyxcbiAgMjA4OiAnTGFicmFkb3IgcmV0cmlldmVyJyxcbiAgMjA5OiAnQ2hlc2FwZWFrZSBCYXkgcmV0cmlldmVyJyxcbiAgMjEwOiAnR2VybWFuIHNob3J0LWhhaXJlZCBwb2ludGVyJyxcbiAgMjExOiAndml6c2xhLCBIdW5nYXJpYW4gcG9pbnRlcicsXG4gIDIxMjogJ0VuZ2xpc2ggc2V0dGVyJyxcbiAgMjEzOiAnSXJpc2ggc2V0dGVyLCByZWQgc2V0dGVyJyxcbiAgMjE0OiAnR29yZG9uIHNldHRlcicsXG4gIDIxNTogJ0JyaXR0YW55IHNwYW5pZWwnLFxuICAyMTY6ICdjbHVtYmVyLCBjbHVtYmVyIHNwYW5pZWwnLFxuICAyMTc6ICdFbmdsaXNoIHNwcmluZ2VyLCBFbmdsaXNoIHNwcmluZ2VyIHNwYW5pZWwnLFxuICAyMTg6ICdXZWxzaCBzcHJpbmdlciBzcGFuaWVsJyxcbiAgMjE5OiAnY29ja2VyIHNwYW5pZWwsIEVuZ2xpc2ggY29ja2VyIHNwYW5pZWwsIGNvY2tlcicsXG4gIDIyMDogJ1N1c3NleCBzcGFuaWVsJyxcbiAgMjIxOiAnSXJpc2ggd2F0ZXIgc3BhbmllbCcsXG4gIDIyMjogJ2t1dmFzeicsXG4gIDIyMzogJ3NjaGlwcGVya2UnLFxuICAyMjQ6ICdncm9lbmVuZGFlbCcsXG4gIDIyNTogJ21hbGlub2lzJyxcbiAgMjI2OiAnYnJpYXJkJyxcbiAgMjI3OiAna2VscGllJyxcbiAgMjI4OiAna29tb25kb3InLFxuICAyMjk6ICdPbGQgRW5nbGlzaCBzaGVlcGRvZywgYm9idGFpbCcsXG4gIDIzMDogJ1NoZXRsYW5kIHNoZWVwZG9nLCBTaGV0bGFuZCBzaGVlcCBkb2csIFNoZXRsYW5kJyxcbiAgMjMxOiAnY29sbGllJyxcbiAgMjMyOiAnQm9yZGVyIGNvbGxpZScsXG4gIDIzMzogJ0JvdXZpZXIgZGVzIEZsYW5kcmVzLCBCb3V2aWVycyBkZXMgRmxhbmRyZXMnLFxuICAyMzQ6ICdSb3R0d2VpbGVyJyxcbiAgMjM1OiAnR2VybWFuIHNoZXBoZXJkLCBHZXJtYW4gc2hlcGhlcmQgZG9nLCBHZXJtYW4gcG9saWNlIGRvZywgYWxzYXRpYW4nLFxuICAyMzY6ICdEb2Jlcm1hbiwgRG9iZXJtYW4gcGluc2NoZXInLFxuICAyMzc6ICdtaW5pYXR1cmUgcGluc2NoZXInLFxuICAyMzg6ICdHcmVhdGVyIFN3aXNzIE1vdW50YWluIGRvZycsXG4gIDIzOTogJ0Jlcm5lc2UgbW91bnRhaW4gZG9nJyxcbiAgMjQwOiAnQXBwZW56ZWxsZXInLFxuICAyNDE6ICdFbnRsZUJ1Y2hlcicsXG4gIDI0MjogJ2JveGVyJyxcbiAgMjQzOiAnYnVsbCBtYXN0aWZmJyxcbiAgMjQ0OiAnVGliZXRhbiBtYXN0aWZmJyxcbiAgMjQ1OiAnRnJlbmNoIGJ1bGxkb2cnLFxuICAyNDY6ICdHcmVhdCBEYW5lJyxcbiAgMjQ3OiAnU2FpbnQgQmVybmFyZCwgU3QgQmVybmFyZCcsXG4gIDI0ODogJ0Vza2ltbyBkb2csIGh1c2t5JyxcbiAgMjQ5OiAnbWFsYW11dGUsIG1hbGVtdXRlLCBBbGFza2FuIG1hbGFtdXRlJyxcbiAgMjUwOiAnU2liZXJpYW4gaHVza3knLFxuICAyNTE6ICdkYWxtYXRpYW4sIGNvYWNoIGRvZywgY2FycmlhZ2UgZG9nJyxcbiAgMjUyOiAnYWZmZW5waW5zY2hlciwgbW9ua2V5IHBpbnNjaGVyLCBtb25rZXkgZG9nJyxcbiAgMjUzOiAnYmFzZW5qaScsXG4gIDI1NDogJ3B1ZywgcHVnLWRvZycsXG4gIDI1NTogJ0xlb25iZXJnJyxcbiAgMjU2OiAnTmV3Zm91bmRsYW5kLCBOZXdmb3VuZGxhbmQgZG9nJyxcbiAgMjU3OiAnR3JlYXQgUHlyZW5lZXMnLFxuICAyNTg6ICdTYW1veWVkLCBTYW1veWVkZScsXG4gIDI1OTogJ1BvbWVyYW5pYW4nLFxuICAyNjA6ICdjaG93LCBjaG93IGNob3cnLFxuICAyNjE6ICdrZWVzaG9uZCcsXG4gIDI2MjogJ0JyYWJhbmNvbiBncmlmZm9uJyxcbiAgMjYzOiAnUGVtYnJva2UsIFBlbWJyb2tlIFdlbHNoIGNvcmdpJyxcbiAgMjY0OiAnQ2FyZGlnYW4sIENhcmRpZ2FuIFdlbHNoIGNvcmdpJyxcbiAgMjY1OiAndG95IHBvb2RsZScsXG4gIDI2NjogJ21pbmlhdHVyZSBwb29kbGUnLFxuICAyNjc6ICdzdGFuZGFyZCBwb29kbGUnLFxuICAyNjg6ICdNZXhpY2FuIGhhaXJsZXNzJyxcbiAgMjY5OiAndGltYmVyIHdvbGYsIGdyZXkgd29sZiwgZ3JheSB3b2xmLCBDYW5pcyBsdXB1cycsXG4gIDI3MDogJ3doaXRlIHdvbGYsIEFyY3RpYyB3b2xmLCBDYW5pcyBsdXB1cyB0dW5kcmFydW0nLFxuICAyNzE6ICdyZWQgd29sZiwgbWFuZWQgd29sZiwgQ2FuaXMgcnVmdXMsIENhbmlzIG5pZ2VyJyxcbiAgMjcyOiAnY295b3RlLCBwcmFpcmllIHdvbGYsIGJydXNoIHdvbGYsIENhbmlzIGxhdHJhbnMnLFxuICAyNzM6ICdkaW5nbywgd2FycmlnYWwsIHdhcnJhZ2FsLCBDYW5pcyBkaW5nbycsXG4gIDI3NDogJ2Rob2xlLCBDdW9uIGFscGludXMnLFxuICAyNzU6ICdBZnJpY2FuIGh1bnRpbmcgZG9nLCBoeWVuYSBkb2csIENhcGUgaHVudGluZyBkb2csIEx5Y2FvbiBwaWN0dXMnLFxuICAyNzY6ICdoeWVuYSwgaHlhZW5hJyxcbiAgMjc3OiAncmVkIGZveCwgVnVscGVzIHZ1bHBlcycsXG4gIDI3ODogJ2tpdCBmb3gsIFZ1bHBlcyBtYWNyb3RpcycsXG4gIDI3OTogJ0FyY3RpYyBmb3gsIHdoaXRlIGZveCwgQWxvcGV4IGxhZ29wdXMnLFxuICAyODA6ICdncmV5IGZveCwgZ3JheSBmb3gsIFVyb2N5b24gY2luZXJlb2FyZ2VudGV1cycsXG4gIDI4MTogJ3RhYmJ5LCB0YWJieSBjYXQnLFxuICAyODI6ICd0aWdlciBjYXQnLFxuICAyODM6ICdQZXJzaWFuIGNhdCcsXG4gIDI4NDogJ1NpYW1lc2UgY2F0LCBTaWFtZXNlJyxcbiAgMjg1OiAnRWd5cHRpYW4gY2F0JyxcbiAgMjg2OlxuICAgICAgJ2NvdWdhciwgcHVtYSwgY2F0YW1vdW50LCBtb3VudGFpbiBsaW9uLCBwYWludGVyLCBwYW50aGVyLCBGZWxpcyBjb25jb2xvcicsXG4gIDI4NzogJ2x5bngsIGNhdGFtb3VudCcsXG4gIDI4ODogJ2xlb3BhcmQsIFBhbnRoZXJhIHBhcmR1cycsXG4gIDI4OTogJ3Nub3cgbGVvcGFyZCwgb3VuY2UsIFBhbnRoZXJhIHVuY2lhJyxcbiAgMjkwOiAnamFndWFyLCBwYW50aGVyLCBQYW50aGVyYSBvbmNhLCBGZWxpcyBvbmNhJyxcbiAgMjkxOiAnbGlvbiwga2luZyBvZiBiZWFzdHMsIFBhbnRoZXJhIGxlbycsXG4gIDI5MjogJ3RpZ2VyLCBQYW50aGVyYSB0aWdyaXMnLFxuICAyOTM6ICdjaGVldGFoLCBjaGV0YWgsIEFjaW5vbnl4IGp1YmF0dXMnLFxuICAyOTQ6ICdicm93biBiZWFyLCBicnVpbiwgVXJzdXMgYXJjdG9zJyxcbiAgMjk1OiAnQW1lcmljYW4gYmxhY2sgYmVhciwgYmxhY2sgYmVhciwgVXJzdXMgYW1lcmljYW51cywgRXVhcmN0b3MgYW1lcmljYW51cycsXG4gIDI5NjogJ2ljZSBiZWFyLCBwb2xhciBiZWFyLCBVcnN1cyBNYXJpdGltdXMsIFRoYWxhcmN0b3MgbWFyaXRpbXVzJyxcbiAgMjk3OiAnc2xvdGggYmVhciwgTWVsdXJzdXMgdXJzaW51cywgVXJzdXMgdXJzaW51cycsXG4gIDI5ODogJ21vbmdvb3NlJyxcbiAgMjk5OiAnbWVlcmthdCwgbWllcmthdCcsXG4gIDMwMDogJ3RpZ2VyIGJlZXRsZScsXG4gIDMwMTogJ2xhZHlidWcsIGxhZHliZWV0bGUsIGxhZHkgYmVldGxlLCBsYWR5YmlyZCwgbGFkeWJpcmQgYmVldGxlJyxcbiAgMzAyOiAnZ3JvdW5kIGJlZXRsZSwgY2FyYWJpZCBiZWV0bGUnLFxuICAzMDM6ICdsb25nLWhvcm5lZCBiZWV0bGUsIGxvbmdpY29ybiwgbG9uZ2ljb3JuIGJlZXRsZScsXG4gIDMwNDogJ2xlYWYgYmVldGxlLCBjaHJ5c29tZWxpZCcsXG4gIDMwNTogJ2R1bmcgYmVldGxlJyxcbiAgMzA2OiAncmhpbm9jZXJvcyBiZWV0bGUnLFxuICAzMDc6ICd3ZWV2aWwnLFxuICAzMDg6ICdmbHknLFxuICAzMDk6ICdiZWUnLFxuICAzMTA6ICdhbnQsIGVtbWV0LCBwaXNtaXJlJyxcbiAgMzExOiAnZ3Jhc3Nob3BwZXIsIGhvcHBlcicsXG4gIDMxMjogJ2NyaWNrZXQnLFxuICAzMTM6ICd3YWxraW5nIHN0aWNrLCB3YWxraW5nc3RpY2ssIHN0aWNrIGluc2VjdCcsXG4gIDMxNDogJ2NvY2tyb2FjaCwgcm9hY2gnLFxuICAzMTU6ICdtYW50aXMsIG1hbnRpZCcsXG4gIDMxNjogJ2NpY2FkYSwgY2ljYWxhJyxcbiAgMzE3OiAnbGVhZmhvcHBlcicsXG4gIDMxODogJ2xhY2V3aW5nLCBsYWNld2luZyBmbHknLFxuICAzMTk6XG4gICAgICAnZHJhZ29uZmx5LCBkYXJuaW5nIG5lZWRsZSwgZGV2aWxcXCdzIGRhcm5pbmcgbmVlZGxlLCBzZXdpbmcgbmVlZGxlLCBzbmFrZSBmZWVkZXIsIHNuYWtlIGRvY3RvciwgbW9zcXVpdG8gaGF3aywgc2tlZXRlciBoYXdrJyxcbiAgMzIwOiAnZGFtc2VsZmx5JyxcbiAgMzIxOiAnYWRtaXJhbCcsXG4gIDMyMjogJ3JpbmdsZXQsIHJpbmdsZXQgYnV0dGVyZmx5JyxcbiAgMzIzOiAnbW9uYXJjaCwgbW9uYXJjaCBidXR0ZXJmbHksIG1pbGt3ZWVkIGJ1dHRlcmZseSwgRGFuYXVzIHBsZXhpcHB1cycsXG4gIDMyNDogJ2NhYmJhZ2UgYnV0dGVyZmx5JyxcbiAgMzI1OiAnc3VscGh1ciBidXR0ZXJmbHksIHN1bGZ1ciBidXR0ZXJmbHknLFxuICAzMjY6ICdseWNhZW5pZCwgbHljYWVuaWQgYnV0dGVyZmx5JyxcbiAgMzI3OiAnc3RhcmZpc2gsIHNlYSBzdGFyJyxcbiAgMzI4OiAnc2VhIHVyY2hpbicsXG4gIDMyOTogJ3NlYSBjdWN1bWJlciwgaG9sb3RodXJpYW4nLFxuICAzMzA6ICd3b29kIHJhYmJpdCwgY290dG9udGFpbCwgY290dG9udGFpbCByYWJiaXQnLFxuICAzMzE6ICdoYXJlJyxcbiAgMzMyOiAnQW5nb3JhLCBBbmdvcmEgcmFiYml0JyxcbiAgMzMzOiAnaGFtc3RlcicsXG4gIDMzNDogJ3BvcmN1cGluZSwgaGVkZ2Vob2cnLFxuICAzMzU6ICdmb3ggc3F1aXJyZWwsIGVhc3Rlcm4gZm94IHNxdWlycmVsLCBTY2l1cnVzIG5pZ2VyJyxcbiAgMzM2OiAnbWFybW90JyxcbiAgMzM3OiAnYmVhdmVyJyxcbiAgMzM4OiAnZ3VpbmVhIHBpZywgQ2F2aWEgY29iYXlhJyxcbiAgMzM5OiAnc29ycmVsJyxcbiAgMzQwOiAnemVicmEnLFxuICAzNDE6ICdob2csIHBpZywgZ3J1bnRlciwgc3F1ZWFsZXIsIFN1cyBzY3JvZmEnLFxuICAzNDI6ICd3aWxkIGJvYXIsIGJvYXIsIFN1cyBzY3JvZmEnLFxuICAzNDM6ICd3YXJ0aG9nJyxcbiAgMzQ0OiAnaGlwcG9wb3RhbXVzLCBoaXBwbywgcml2ZXIgaG9yc2UsIEhpcHBvcG90YW11cyBhbXBoaWJpdXMnLFxuICAzNDU6ICdveCcsXG4gIDM0NjogJ3dhdGVyIGJ1ZmZhbG8sIHdhdGVyIG94LCBBc2lhdGljIGJ1ZmZhbG8sIEJ1YmFsdXMgYnViYWxpcycsXG4gIDM0NzogJ2Jpc29uJyxcbiAgMzQ4OiAncmFtLCB0dXAnLFxuICAzNDk6XG4gICAgICAnYmlnaG9ybiwgYmlnaG9ybiBzaGVlcCwgY2ltYXJyb24sIFJvY2t5IE1vdW50YWluIGJpZ2hvcm4sIFJvY2t5IE1vdW50YWluIHNoZWVwLCBPdmlzIGNhbmFkZW5zaXMnLFxuICAzNTA6ICdpYmV4LCBDYXByYSBpYmV4JyxcbiAgMzUxOiAnaGFydGViZWVzdCcsXG4gIDM1MjogJ2ltcGFsYSwgQWVweWNlcm9zIG1lbGFtcHVzJyxcbiAgMzUzOiAnZ2F6ZWxsZScsXG4gIDM1NDogJ0FyYWJpYW4gY2FtZWwsIGRyb21lZGFyeSwgQ2FtZWx1cyBkcm9tZWRhcml1cycsXG4gIDM1NTogJ2xsYW1hJyxcbiAgMzU2OiAnd2Vhc2VsJyxcbiAgMzU3OiAnbWluaycsXG4gIDM1ODogJ3BvbGVjYXQsIGZpdGNoLCBmb3VsbWFydCwgZm91bWFydCwgTXVzdGVsYSBwdXRvcml1cycsXG4gIDM1OTogJ2JsYWNrLWZvb3RlZCBmZXJyZXQsIGZlcnJldCwgTXVzdGVsYSBuaWdyaXBlcycsXG4gIDM2MDogJ290dGVyJyxcbiAgMzYxOiAnc2t1bmssIHBvbGVjYXQsIHdvb2QgcHVzc3knLFxuICAzNjI6ICdiYWRnZXInLFxuICAzNjM6ICdhcm1hZGlsbG8nLFxuICAzNjQ6ICd0aHJlZS10b2VkIHNsb3RoLCBhaSwgQnJhZHlwdXMgdHJpZGFjdHlsdXMnLFxuICAzNjU6ICdvcmFuZ3V0YW4sIG9yYW5nLCBvcmFuZ3V0YW5nLCBQb25nbyBweWdtYWV1cycsXG4gIDM2NjogJ2dvcmlsbGEsIEdvcmlsbGEgZ29yaWxsYScsXG4gIDM2NzogJ2NoaW1wYW56ZWUsIGNoaW1wLCBQYW4gdHJvZ2xvZHl0ZXMnLFxuICAzNjg6ICdnaWJib24sIEh5bG9iYXRlcyBsYXInLFxuICAzNjk6ICdzaWFtYW5nLCBIeWxvYmF0ZXMgc3luZGFjdHlsdXMsIFN5bXBoYWxhbmd1cyBzeW5kYWN0eWx1cycsXG4gIDM3MDogJ2d1ZW5vbiwgZ3Vlbm9uIG1vbmtleScsXG4gIDM3MTogJ3BhdGFzLCBodXNzYXIgbW9ua2V5LCBFcnl0aHJvY2VidXMgcGF0YXMnLFxuICAzNzI6ICdiYWJvb24nLFxuICAzNzM6ICdtYWNhcXVlJyxcbiAgMzc0OiAnbGFuZ3VyJyxcbiAgMzc1OiAnY29sb2J1cywgY29sb2J1cyBtb25rZXknLFxuICAzNzY6ICdwcm9ib3NjaXMgbW9ua2V5LCBOYXNhbGlzIGxhcnZhdHVzJyxcbiAgMzc3OiAnbWFybW9zZXQnLFxuICAzNzg6ICdjYXB1Y2hpbiwgcmluZ3RhaWwsIENlYnVzIGNhcHVjaW51cycsXG4gIDM3OTogJ2hvd2xlciBtb25rZXksIGhvd2xlcicsXG4gIDM4MDogJ3RpdGksIHRpdGkgbW9ua2V5JyxcbiAgMzgxOiAnc3BpZGVyIG1vbmtleSwgQXRlbGVzIGdlb2Zmcm95aScsXG4gIDM4MjogJ3NxdWlycmVsIG1vbmtleSwgU2FpbWlyaSBzY2l1cmV1cycsXG4gIDM4MzogJ01hZGFnYXNjYXIgY2F0LCByaW5nLXRhaWxlZCBsZW11ciwgTGVtdXIgY2F0dGEnLFxuICAzODQ6ICdpbmRyaSwgaW5kcmlzLCBJbmRyaSBpbmRyaSwgSW5kcmkgYnJldmljYXVkYXR1cycsXG4gIDM4NTogJ0luZGlhbiBlbGVwaGFudCwgRWxlcGhhcyBtYXhpbXVzJyxcbiAgMzg2OiAnQWZyaWNhbiBlbGVwaGFudCwgTG94b2RvbnRhIGFmcmljYW5hJyxcbiAgMzg3OiAnbGVzc2VyIHBhbmRhLCByZWQgcGFuZGEsIHBhbmRhLCBiZWFyIGNhdCwgY2F0IGJlYXIsIEFpbHVydXMgZnVsZ2VucycsXG4gIDM4ODogJ2dpYW50IHBhbmRhLCBwYW5kYSwgcGFuZGEgYmVhciwgY29vbiBiZWFyLCBBaWx1cm9wb2RhIG1lbGFub2xldWNhJyxcbiAgMzg5OiAnYmFycmFjb3V0YSwgc25vZWsnLFxuICAzOTA6ICdlZWwnLFxuICAzOTE6XG4gICAgICAnY29obywgY29ob2UsIGNvaG8gc2FsbW9uLCBibHVlIGphY2ssIHNpbHZlciBzYWxtb24sIE9uY29yaHluY2h1cyBraXN1dGNoJyxcbiAgMzkyOiAncm9jayBiZWF1dHksIEhvbG9jYW50aHVzIHRyaWNvbG9yJyxcbiAgMzkzOiAnYW5lbW9uZSBmaXNoJyxcbiAgMzk0OiAnc3R1cmdlb24nLFxuICAzOTU6ICdnYXIsIGdhcmZpc2gsIGdhcnBpa2UsIGJpbGxmaXNoLCBMZXBpc29zdGV1cyBvc3NldXMnLFxuICAzOTY6ICdsaW9uZmlzaCcsXG4gIDM5NzogJ3B1ZmZlciwgcHVmZmVyZmlzaCwgYmxvd2Zpc2gsIGdsb2JlZmlzaCcsXG4gIDM5ODogJ2FiYWN1cycsXG4gIDM5OTogJ2FiYXlhJyxcbiAgNDAwOiAnYWNhZGVtaWMgZ293biwgYWNhZGVtaWMgcm9iZSwganVkZ2VcXCdzIHJvYmUnLFxuICA0MDE6ICdhY2NvcmRpb24sIHBpYW5vIGFjY29yZGlvbiwgc3F1ZWV6ZSBib3gnLFxuICA0MDI6ICdhY291c3RpYyBndWl0YXInLFxuICA0MDM6ICdhaXJjcmFmdCBjYXJyaWVyLCBjYXJyaWVyLCBmbGF0dG9wLCBhdHRhY2sgYWlyY3JhZnQgY2FycmllcicsXG4gIDQwNDogJ2FpcmxpbmVyJyxcbiAgNDA1OiAnYWlyc2hpcCwgZGlyaWdpYmxlJyxcbiAgNDA2OiAnYWx0YXInLFxuICA0MDc6ICdhbWJ1bGFuY2UnLFxuICA0MDg6ICdhbXBoaWJpYW4sIGFtcGhpYmlvdXMgdmVoaWNsZScsXG4gIDQwOTogJ2FuYWxvZyBjbG9jaycsXG4gIDQxMDogJ2FwaWFyeSwgYmVlIGhvdXNlJyxcbiAgNDExOiAnYXByb24nLFxuICA0MTI6XG4gICAgICAnYXNoY2FuLCB0cmFzaCBjYW4sIGdhcmJhZ2UgY2FuLCB3YXN0ZWJpbiwgYXNoIGJpbiwgYXNoLWJpbiwgYXNoYmluLCBkdXN0YmluLCB0cmFzaCBiYXJyZWwsIHRyYXNoIGJpbicsXG4gIDQxMzogJ2Fzc2F1bHQgcmlmbGUsIGFzc2F1bHQgZ3VuJyxcbiAgNDE0OiAnYmFja3BhY2ssIGJhY2sgcGFjaywga25hcHNhY2ssIHBhY2tzYWNrLCBydWNrc2FjaywgaGF2ZXJzYWNrJyxcbiAgNDE1OiAnYmFrZXJ5LCBiYWtlc2hvcCwgYmFrZWhvdXNlJyxcbiAgNDE2OiAnYmFsYW5jZSBiZWFtLCBiZWFtJyxcbiAgNDE3OiAnYmFsbG9vbicsXG4gIDQxODogJ2JhbGxwb2ludCwgYmFsbHBvaW50IHBlbiwgYmFsbHBlbiwgQmlybycsXG4gIDQxOTogJ0JhbmQgQWlkJyxcbiAgNDIwOiAnYmFuam8nLFxuICA0MjE6ICdiYW5uaXN0ZXIsIGJhbmlzdGVyLCBiYWx1c3RyYWRlLCBiYWx1c3RlcnMsIGhhbmRyYWlsJyxcbiAgNDIyOiAnYmFyYmVsbCcsXG4gIDQyMzogJ2JhcmJlciBjaGFpcicsXG4gIDQyNDogJ2JhcmJlcnNob3AnLFxuICA0MjU6ICdiYXJuJyxcbiAgNDI2OiAnYmFyb21ldGVyJyxcbiAgNDI3OiAnYmFycmVsLCBjYXNrJyxcbiAgNDI4OiAnYmFycm93LCBnYXJkZW4gY2FydCwgbGF3biBjYXJ0LCB3aGVlbGJhcnJvdycsXG4gIDQyOTogJ2Jhc2ViYWxsJyxcbiAgNDMwOiAnYmFza2V0YmFsbCcsXG4gIDQzMTogJ2Jhc3NpbmV0JyxcbiAgNDMyOiAnYmFzc29vbicsXG4gIDQzMzogJ2JhdGhpbmcgY2FwLCBzd2ltbWluZyBjYXAnLFxuICA0MzQ6ICdiYXRoIHRvd2VsJyxcbiAgNDM1OiAnYmF0aHR1YiwgYmF0aGluZyB0dWIsIGJhdGgsIHR1YicsXG4gIDQzNjpcbiAgICAgICdiZWFjaCB3YWdvbiwgc3RhdGlvbiB3YWdvbiwgd2Fnb24sIGVzdGF0ZSBjYXIsIGJlYWNoIHdhZ2dvbiwgc3RhdGlvbiB3YWdnb24sIHdhZ2dvbicsXG4gIDQzNzogJ2JlYWNvbiwgbGlnaHRob3VzZSwgYmVhY29uIGxpZ2h0LCBwaGFyb3MnLFxuICA0Mzg6ICdiZWFrZXInLFxuICA0Mzk6ICdiZWFyc2tpbiwgYnVzYnksIHNoYWtvJyxcbiAgNDQwOiAnYmVlciBib3R0bGUnLFxuICA0NDE6ICdiZWVyIGdsYXNzJyxcbiAgNDQyOiAnYmVsbCBjb3RlLCBiZWxsIGNvdCcsXG4gIDQ0MzogJ2JpYicsXG4gIDQ0NDogJ2JpY3ljbGUtYnVpbHQtZm9yLXR3bywgdGFuZGVtIGJpY3ljbGUsIHRhbmRlbScsXG4gIDQ0NTogJ2Jpa2luaSwgdHdvLXBpZWNlJyxcbiAgNDQ2OiAnYmluZGVyLCByaW5nLWJpbmRlcicsXG4gIDQ0NzogJ2Jpbm9jdWxhcnMsIGZpZWxkIGdsYXNzZXMsIG9wZXJhIGdsYXNzZXMnLFxuICA0NDg6ICdiaXJkaG91c2UnLFxuICA0NDk6ICdib2F0aG91c2UnLFxuICA0NTA6ICdib2JzbGVkLCBib2JzbGVpZ2gsIGJvYicsXG4gIDQ1MTogJ2JvbG8gdGllLCBib2xvLCBib2xhIHRpZSwgYm9sYScsXG4gIDQ1MjogJ2Jvbm5ldCwgcG9rZSBib25uZXQnLFxuICA0NTM6ICdib29rY2FzZScsXG4gIDQ1NDogJ2Jvb2tzaG9wLCBib29rc3RvcmUsIGJvb2tzdGFsbCcsXG4gIDQ1NTogJ2JvdHRsZWNhcCcsXG4gIDQ1NjogJ2JvdycsXG4gIDQ1NzogJ2JvdyB0aWUsIGJvdy10aWUsIGJvd3RpZScsXG4gIDQ1ODogJ2JyYXNzLCBtZW1vcmlhbCB0YWJsZXQsIHBsYXF1ZScsXG4gIDQ1OTogJ2JyYXNzaWVyZSwgYnJhLCBiYW5kZWF1JyxcbiAgNDYwOiAnYnJlYWt3YXRlciwgZ3JvaW4sIGdyb3luZSwgbW9sZSwgYnVsd2Fyaywgc2Vhd2FsbCwgamV0dHknLFxuICA0NjE6ICdicmVhc3RwbGF0ZSwgYWVnaXMsIGVnaXMnLFxuICA0NjI6ICdicm9vbScsXG4gIDQ2MzogJ2J1Y2tldCwgcGFpbCcsXG4gIDQ2NDogJ2J1Y2tsZScsXG4gIDQ2NTogJ2J1bGxldHByb29mIHZlc3QnLFxuICA0NjY6ICdidWxsZXQgdHJhaW4sIGJ1bGxldCcsXG4gIDQ2NzogJ2J1dGNoZXIgc2hvcCwgbWVhdCBtYXJrZXQnLFxuICA0Njg6ICdjYWIsIGhhY2ssIHRheGksIHRheGljYWInLFxuICA0Njk6ICdjYWxkcm9uLCBjYXVsZHJvbicsXG4gIDQ3MDogJ2NhbmRsZSwgdGFwZXIsIHdheCBsaWdodCcsXG4gIDQ3MTogJ2Nhbm5vbicsXG4gIDQ3MjogJ2Nhbm9lJyxcbiAgNDczOiAnY2FuIG9wZW5lciwgdGluIG9wZW5lcicsXG4gIDQ3NDogJ2NhcmRpZ2FuJyxcbiAgNDc1OiAnY2FyIG1pcnJvcicsXG4gIDQ3NjogJ2Nhcm91c2VsLCBjYXJyb3VzZWwsIG1lcnJ5LWdvLXJvdW5kLCByb3VuZGFib3V0LCB3aGlybGlnaWcnLFxuICA0Nzc6ICdjYXJwZW50ZXJcXCdzIGtpdCwgdG9vbCBraXQnLFxuICA0Nzg6ICdjYXJ0b24nLFxuICA0Nzk6ICdjYXIgd2hlZWwnLFxuICA0ODA6XG4gICAgICAnY2FzaCBtYWNoaW5lLCBjYXNoIGRpc3BlbnNlciwgYXV0b21hdGVkIHRlbGxlciBtYWNoaW5lLCBhdXRvbWF0aWMgdGVsbGVyIG1hY2hpbmUsIGF1dG9tYXRlZCB0ZWxsZXIsIGF1dG9tYXRpYyB0ZWxsZXIsIEFUTScsXG4gIDQ4MTogJ2Nhc3NldHRlJyxcbiAgNDgyOiAnY2Fzc2V0dGUgcGxheWVyJyxcbiAgNDgzOiAnY2FzdGxlJyxcbiAgNDg0OiAnY2F0YW1hcmFuJyxcbiAgNDg1OiAnQ0QgcGxheWVyJyxcbiAgNDg2OiAnY2VsbG8sIHZpb2xvbmNlbGxvJyxcbiAgNDg3OiAnY2VsbHVsYXIgdGVsZXBob25lLCBjZWxsdWxhciBwaG9uZSwgY2VsbHBob25lLCBjZWxsLCBtb2JpbGUgcGhvbmUnLFxuICA0ODg6ICdjaGFpbicsXG4gIDQ4OTogJ2NoYWlubGluayBmZW5jZScsXG4gIDQ5MDpcbiAgICAgICdjaGFpbiBtYWlsLCByaW5nIG1haWwsIG1haWwsIGNoYWluIGFybW9yLCBjaGFpbiBhcm1vdXIsIHJpbmcgYXJtb3IsIHJpbmcgYXJtb3VyJyxcbiAgNDkxOiAnY2hhaW4gc2F3LCBjaGFpbnNhdycsXG4gIDQ5MjogJ2NoZXN0JyxcbiAgNDkzOiAnY2hpZmZvbmllciwgY29tbW9kZScsXG4gIDQ5NDogJ2NoaW1lLCBiZWxsLCBnb25nJyxcbiAgNDk1OiAnY2hpbmEgY2FiaW5ldCwgY2hpbmEgY2xvc2V0JyxcbiAgNDk2OiAnQ2hyaXN0bWFzIHN0b2NraW5nJyxcbiAgNDk3OiAnY2h1cmNoLCBjaHVyY2ggYnVpbGRpbmcnLFxuICA0OTg6ICdjaW5lbWEsIG1vdmllIHRoZWF0ZXIsIG1vdmllIHRoZWF0cmUsIG1vdmllIGhvdXNlLCBwaWN0dXJlIHBhbGFjZScsXG4gIDQ5OTogJ2NsZWF2ZXIsIG1lYXQgY2xlYXZlciwgY2hvcHBlcicsXG4gIDUwMDogJ2NsaWZmIGR3ZWxsaW5nJyxcbiAgNTAxOiAnY2xvYWsnLFxuICA1MDI6ICdjbG9nLCBnZXRhLCBwYXR0ZW4sIHNhYm90JyxcbiAgNTAzOiAnY29ja3RhaWwgc2hha2VyJyxcbiAgNTA0OiAnY29mZmVlIG11ZycsXG4gIDUwNTogJ2NvZmZlZXBvdCcsXG4gIDUwNjogJ2NvaWwsIHNwaXJhbCwgdm9sdXRlLCB3aG9ybCwgaGVsaXgnLFxuICA1MDc6ICdjb21iaW5hdGlvbiBsb2NrJyxcbiAgNTA4OiAnY29tcHV0ZXIga2V5Ym9hcmQsIGtleXBhZCcsXG4gIDUwOTogJ2NvbmZlY3Rpb25lcnksIGNvbmZlY3Rpb25hcnksIGNhbmR5IHN0b3JlJyxcbiAgNTEwOiAnY29udGFpbmVyIHNoaXAsIGNvbnRhaW5lcnNoaXAsIGNvbnRhaW5lciB2ZXNzZWwnLFxuICA1MTE6ICdjb252ZXJ0aWJsZScsXG4gIDUxMjogJ2NvcmtzY3JldywgYm90dGxlIHNjcmV3JyxcbiAgNTEzOiAnY29ybmV0LCBob3JuLCB0cnVtcGV0LCB0cnVtcCcsXG4gIDUxNDogJ2Nvd2JveSBib290JyxcbiAgNTE1OiAnY293Ym95IGhhdCwgdGVuLWdhbGxvbiBoYXQnLFxuICA1MTY6ICdjcmFkbGUnLFxuICA1MTc6ICdjcmFuZScsXG4gIDUxODogJ2NyYXNoIGhlbG1ldCcsXG4gIDUxOTogJ2NyYXRlJyxcbiAgNTIwOiAnY3JpYiwgY290JyxcbiAgNTIxOiAnQ3JvY2sgUG90JyxcbiAgNTIyOiAnY3JvcXVldCBiYWxsJyxcbiAgNTIzOiAnY3J1dGNoJyxcbiAgNTI0OiAnY3VpcmFzcycsXG4gIDUyNTogJ2RhbSwgZGlrZSwgZHlrZScsXG4gIDUyNjogJ2Rlc2snLFxuICA1Mjc6ICdkZXNrdG9wIGNvbXB1dGVyJyxcbiAgNTI4OiAnZGlhbCB0ZWxlcGhvbmUsIGRpYWwgcGhvbmUnLFxuICA1Mjk6ICdkaWFwZXIsIG5hcHB5LCBuYXBraW4nLFxuICA1MzA6ICdkaWdpdGFsIGNsb2NrJyxcbiAgNTMxOiAnZGlnaXRhbCB3YXRjaCcsXG4gIDUzMjogJ2RpbmluZyB0YWJsZSwgYm9hcmQnLFxuICA1MzM6ICdkaXNocmFnLCBkaXNoY2xvdGgnLFxuICA1MzQ6ICdkaXNod2FzaGVyLCBkaXNoIHdhc2hlciwgZGlzaHdhc2hpbmcgbWFjaGluZScsXG4gIDUzNTogJ2Rpc2sgYnJha2UsIGRpc2MgYnJha2UnLFxuICA1MzY6ICdkb2NrLCBkb2NrYWdlLCBkb2NraW5nIGZhY2lsaXR5JyxcbiAgNTM3OiAnZG9nc2xlZCwgZG9nIHNsZWQsIGRvZyBzbGVpZ2gnLFxuICA1Mzg6ICdkb21lJyxcbiAgNTM5OiAnZG9vcm1hdCwgd2VsY29tZSBtYXQnLFxuICA1NDA6ICdkcmlsbGluZyBwbGF0Zm9ybSwgb2Zmc2hvcmUgcmlnJyxcbiAgNTQxOiAnZHJ1bSwgbWVtYnJhbm9waG9uZSwgdHltcGFuJyxcbiAgNTQyOiAnZHJ1bXN0aWNrJyxcbiAgNTQzOiAnZHVtYmJlbGwnLFxuICA1NDQ6ICdEdXRjaCBvdmVuJyxcbiAgNTQ1OiAnZWxlY3RyaWMgZmFuLCBibG93ZXInLFxuICA1NDY6ICdlbGVjdHJpYyBndWl0YXInLFxuICA1NDc6ICdlbGVjdHJpYyBsb2NvbW90aXZlJyxcbiAgNTQ4OiAnZW50ZXJ0YWlubWVudCBjZW50ZXInLFxuICA1NDk6ICdlbnZlbG9wZScsXG4gIDU1MDogJ2VzcHJlc3NvIG1ha2VyJyxcbiAgNTUxOiAnZmFjZSBwb3dkZXInLFxuICA1NTI6ICdmZWF0aGVyIGJvYSwgYm9hJyxcbiAgNTUzOiAnZmlsZSwgZmlsZSBjYWJpbmV0LCBmaWxpbmcgY2FiaW5ldCcsXG4gIDU1NDogJ2ZpcmVib2F0JyxcbiAgNTU1OiAnZmlyZSBlbmdpbmUsIGZpcmUgdHJ1Y2snLFxuICA1NTY6ICdmaXJlIHNjcmVlbiwgZmlyZWd1YXJkJyxcbiAgNTU3OiAnZmxhZ3BvbGUsIGZsYWdzdGFmZicsXG4gIDU1ODogJ2ZsdXRlLCB0cmFuc3ZlcnNlIGZsdXRlJyxcbiAgNTU5OiAnZm9sZGluZyBjaGFpcicsXG4gIDU2MDogJ2Zvb3RiYWxsIGhlbG1ldCcsXG4gIDU2MTogJ2ZvcmtsaWZ0JyxcbiAgNTYyOiAnZm91bnRhaW4nLFxuICA1NjM6ICdmb3VudGFpbiBwZW4nLFxuICA1NjQ6ICdmb3VyLXBvc3RlcicsXG4gIDU2NTogJ2ZyZWlnaHQgY2FyJyxcbiAgNTY2OiAnRnJlbmNoIGhvcm4sIGhvcm4nLFxuICA1Njc6ICdmcnlpbmcgcGFuLCBmcnlwYW4sIHNraWxsZXQnLFxuICA1Njg6ICdmdXIgY29hdCcsXG4gIDU2OTogJ2dhcmJhZ2UgdHJ1Y2ssIGR1c3RjYXJ0JyxcbiAgNTcwOiAnZ2FzbWFzaywgcmVzcGlyYXRvciwgZ2FzIGhlbG1ldCcsXG4gIDU3MTogJ2dhcyBwdW1wLCBnYXNvbGluZSBwdW1wLCBwZXRyb2wgcHVtcCwgaXNsYW5kIGRpc3BlbnNlcicsXG4gIDU3MjogJ2dvYmxldCcsXG4gIDU3MzogJ2dvLWthcnQnLFxuICA1NzQ6ICdnb2xmIGJhbGwnLFxuICA1NzU6ICdnb2xmY2FydCwgZ29sZiBjYXJ0JyxcbiAgNTc2OiAnZ29uZG9sYScsXG4gIDU3NzogJ2dvbmcsIHRhbS10YW0nLFxuICA1Nzg6ICdnb3duJyxcbiAgNTc5OiAnZ3JhbmQgcGlhbm8sIGdyYW5kJyxcbiAgNTgwOiAnZ3JlZW5ob3VzZSwgbnVyc2VyeSwgZ2xhc3Nob3VzZScsXG4gIDU4MTogJ2dyaWxsZSwgcmFkaWF0b3IgZ3JpbGxlJyxcbiAgNTgyOiAnZ3JvY2VyeSBzdG9yZSwgZ3JvY2VyeSwgZm9vZCBtYXJrZXQsIG1hcmtldCcsXG4gIDU4MzogJ2d1aWxsb3RpbmUnLFxuICA1ODQ6ICdoYWlyIHNsaWRlJyxcbiAgNTg1OiAnaGFpciBzcHJheScsXG4gIDU4NjogJ2hhbGYgdHJhY2snLFxuICA1ODc6ICdoYW1tZXInLFxuICA1ODg6ICdoYW1wZXInLFxuICA1ODk6ICdoYW5kIGJsb3dlciwgYmxvdyBkcnllciwgYmxvdyBkcmllciwgaGFpciBkcnllciwgaGFpciBkcmllcicsXG4gIDU5MDogJ2hhbmQtaGVsZCBjb21wdXRlciwgaGFuZC1oZWxkIG1pY3JvY29tcHV0ZXInLFxuICA1OTE6ICdoYW5ka2VyY2hpZWYsIGhhbmtpZSwgaGFua3ksIGhhbmtleScsXG4gIDU5MjogJ2hhcmQgZGlzYywgaGFyZCBkaXNrLCBmaXhlZCBkaXNrJyxcbiAgNTkzOiAnaGFybW9uaWNhLCBtb3V0aCBvcmdhbiwgaGFycCwgbW91dGggaGFycCcsXG4gIDU5NDogJ2hhcnAnLFxuICA1OTU6ICdoYXJ2ZXN0ZXIsIHJlYXBlcicsXG4gIDU5NjogJ2hhdGNoZXQnLFxuICA1OTc6ICdob2xzdGVyJyxcbiAgNTk4OiAnaG9tZSB0aGVhdGVyLCBob21lIHRoZWF0cmUnLFxuICA1OTk6ICdob25leWNvbWInLFxuICA2MDA6ICdob29rLCBjbGF3JyxcbiAgNjAxOiAnaG9vcHNraXJ0LCBjcmlub2xpbmUnLFxuICA2MDI6ICdob3Jpem9udGFsIGJhciwgaGlnaCBiYXInLFxuICA2MDM6ICdob3JzZSBjYXJ0LCBob3JzZS1jYXJ0JyxcbiAgNjA0OiAnaG91cmdsYXNzJyxcbiAgNjA1OiAnaVBvZCcsXG4gIDYwNjogJ2lyb24sIHNtb290aGluZyBpcm9uJyxcbiAgNjA3OiAnamFjay1vXFwnLWxhbnRlcm4nLFxuICA2MDg6ICdqZWFuLCBibHVlIGplYW4sIGRlbmltJyxcbiAgNjA5OiAnamVlcCwgbGFuZHJvdmVyJyxcbiAgNjEwOiAnamVyc2V5LCBULXNoaXJ0LCB0ZWUgc2hpcnQnLFxuICA2MTE6ICdqaWdzYXcgcHV6emxlJyxcbiAgNjEyOiAnamlucmlraXNoYSwgcmlja3NoYSwgcmlja3NoYXcnLFxuICA2MTM6ICdqb3lzdGljaycsXG4gIDYxNDogJ2tpbW9ubycsXG4gIDYxNTogJ2tuZWUgcGFkJyxcbiAgNjE2OiAna25vdCcsXG4gIDYxNzogJ2xhYiBjb2F0LCBsYWJvcmF0b3J5IGNvYXQnLFxuICA2MTg6ICdsYWRsZScsXG4gIDYxOTogJ2xhbXBzaGFkZSwgbGFtcCBzaGFkZScsXG4gIDYyMDogJ2xhcHRvcCwgbGFwdG9wIGNvbXB1dGVyJyxcbiAgNjIxOiAnbGF3biBtb3dlciwgbW93ZXInLFxuICA2MjI6ICdsZW5zIGNhcCwgbGVucyBjb3ZlcicsXG4gIDYyMzogJ2xldHRlciBvcGVuZXIsIHBhcGVyIGtuaWZlLCBwYXBlcmtuaWZlJyxcbiAgNjI0OiAnbGlicmFyeScsXG4gIDYyNTogJ2xpZmVib2F0JyxcbiAgNjI2OiAnbGlnaHRlciwgbGlnaHQsIGlnbml0ZXIsIGlnbml0b3InLFxuICA2Mjc6ICdsaW1vdXNpbmUsIGxpbW8nLFxuICA2Mjg6ICdsaW5lciwgb2NlYW4gbGluZXInLFxuICA2Mjk6ICdsaXBzdGljaywgbGlwIHJvdWdlJyxcbiAgNjMwOiAnTG9hZmVyJyxcbiAgNjMxOiAnbG90aW9uJyxcbiAgNjMyOiAnbG91ZHNwZWFrZXIsIHNwZWFrZXIsIHNwZWFrZXIgdW5pdCwgbG91ZHNwZWFrZXIgc3lzdGVtLCBzcGVha2VyIHN5c3RlbScsXG4gIDYzMzogJ2xvdXBlLCBqZXdlbGVyXFwncyBsb3VwZScsXG4gIDYzNDogJ2x1bWJlcm1pbGwsIHNhd21pbGwnLFxuICA2MzU6ICdtYWduZXRpYyBjb21wYXNzJyxcbiAgNjM2OiAnbWFpbGJhZywgcG9zdGJhZycsXG4gIDYzNzogJ21haWxib3gsIGxldHRlciBib3gnLFxuICA2Mzg6ICdtYWlsbG90JyxcbiAgNjM5OiAnbWFpbGxvdCwgdGFuayBzdWl0JyxcbiAgNjQwOiAnbWFuaG9sZSBjb3ZlcicsXG4gIDY0MTogJ21hcmFjYScsXG4gIDY0MjogJ21hcmltYmEsIHh5bG9waG9uZScsXG4gIDY0MzogJ21hc2snLFxuICA2NDQ6ICdtYXRjaHN0aWNrJyxcbiAgNjQ1OiAnbWF5cG9sZScsXG4gIDY0NjogJ21hemUsIGxhYnlyaW50aCcsXG4gIDY0NzogJ21lYXN1cmluZyBjdXAnLFxuICA2NDg6ICdtZWRpY2luZSBjaGVzdCwgbWVkaWNpbmUgY2FiaW5ldCcsXG4gIDY0OTogJ21lZ2FsaXRoLCBtZWdhbGl0aGljIHN0cnVjdHVyZScsXG4gIDY1MDogJ21pY3JvcGhvbmUsIG1pa2UnLFxuICA2NTE6ICdtaWNyb3dhdmUsIG1pY3Jvd2F2ZSBvdmVuJyxcbiAgNjUyOiAnbWlsaXRhcnkgdW5pZm9ybScsXG4gIDY1MzogJ21pbGsgY2FuJyxcbiAgNjU0OiAnbWluaWJ1cycsXG4gIDY1NTogJ21pbmlza2lydCwgbWluaScsXG4gIDY1NjogJ21pbml2YW4nLFxuICA2NTc6ICdtaXNzaWxlJyxcbiAgNjU4OiAnbWl0dGVuJyxcbiAgNjU5OiAnbWl4aW5nIGJvd2wnLFxuICA2NjA6ICdtb2JpbGUgaG9tZSwgbWFudWZhY3R1cmVkIGhvbWUnLFxuICA2NjE6ICdNb2RlbCBUJyxcbiAgNjYyOiAnbW9kZW0nLFxuICA2NjM6ICdtb25hc3RlcnknLFxuICA2NjQ6ICdtb25pdG9yJyxcbiAgNjY1OiAnbW9wZWQnLFxuICA2NjY6ICdtb3J0YXInLFxuICA2Njc6ICdtb3J0YXJib2FyZCcsXG4gIDY2ODogJ21vc3F1ZScsXG4gIDY2OTogJ21vc3F1aXRvIG5ldCcsXG4gIDY3MDogJ21vdG9yIHNjb290ZXIsIHNjb290ZXInLFxuICA2NzE6ICdtb3VudGFpbiBiaWtlLCBhbGwtdGVycmFpbiBiaWtlLCBvZmYtcm9hZGVyJyxcbiAgNjcyOiAnbW91bnRhaW4gdGVudCcsXG4gIDY3MzogJ21vdXNlLCBjb21wdXRlciBtb3VzZScsXG4gIDY3NDogJ21vdXNldHJhcCcsXG4gIDY3NTogJ21vdmluZyB2YW4nLFxuICA2NzY6ICdtdXp6bGUnLFxuICA2Nzc6ICduYWlsJyxcbiAgNjc4OiAnbmVjayBicmFjZScsXG4gIDY3OTogJ25lY2tsYWNlJyxcbiAgNjgwOiAnbmlwcGxlJyxcbiAgNjgxOiAnbm90ZWJvb2ssIG5vdGVib29rIGNvbXB1dGVyJyxcbiAgNjgyOiAnb2JlbGlzaycsXG4gIDY4MzogJ29ib2UsIGhhdXRib3ksIGhhdXRib2lzJyxcbiAgNjg0OiAnb2NhcmluYSwgc3dlZXQgcG90YXRvJyxcbiAgNjg1OiAnb2RvbWV0ZXIsIGhvZG9tZXRlciwgbWlsZW9tZXRlciwgbWlsb21ldGVyJyxcbiAgNjg2OiAnb2lsIGZpbHRlcicsXG4gIDY4NzogJ29yZ2FuLCBwaXBlIG9yZ2FuJyxcbiAgNjg4OiAnb3NjaWxsb3Njb3BlLCBzY29wZSwgY2F0aG9kZS1yYXkgb3NjaWxsb3Njb3BlLCBDUk8nLFxuICA2ODk6ICdvdmVyc2tpcnQnLFxuICA2OTA6ICdveGNhcnQnLFxuICA2OTE6ICdveHlnZW4gbWFzaycsXG4gIDY5MjogJ3BhY2tldCcsXG4gIDY5MzogJ3BhZGRsZSwgYm9hdCBwYWRkbGUnLFxuICA2OTQ6ICdwYWRkbGV3aGVlbCwgcGFkZGxlIHdoZWVsJyxcbiAgNjk1OiAncGFkbG9jaycsXG4gIDY5NjogJ3BhaW50YnJ1c2gnLFxuICA2OTc6ICdwYWphbWEsIHB5amFtYSwgcGpcXCdzLCBqYW1taWVzJyxcbiAgNjk4OiAncGFsYWNlJyxcbiAgNjk5OiAncGFucGlwZSwgcGFuZGVhbiBwaXBlLCBzeXJpbngnLFxuICA3MDA6ICdwYXBlciB0b3dlbCcsXG4gIDcwMTogJ3BhcmFjaHV0ZSwgY2h1dGUnLFxuICA3MDI6ICdwYXJhbGxlbCBiYXJzLCBiYXJzJyxcbiAgNzAzOiAncGFyayBiZW5jaCcsXG4gIDcwNDogJ3BhcmtpbmcgbWV0ZXInLFxuICA3MDU6ICdwYXNzZW5nZXIgY2FyLCBjb2FjaCwgY2FycmlhZ2UnLFxuICA3MDY6ICdwYXRpbywgdGVycmFjZScsXG4gIDcwNzogJ3BheS1waG9uZSwgcGF5LXN0YXRpb24nLFxuICA3MDg6ICdwZWRlc3RhbCwgcGxpbnRoLCBmb290c3RhbGwnLFxuICA3MDk6ICdwZW5jaWwgYm94LCBwZW5jaWwgY2FzZScsXG4gIDcxMDogJ3BlbmNpbCBzaGFycGVuZXInLFxuICA3MTE6ICdwZXJmdW1lLCBlc3NlbmNlJyxcbiAgNzEyOiAnUGV0cmkgZGlzaCcsXG4gIDcxMzogJ3Bob3RvY29waWVyJyxcbiAgNzE0OiAncGljaywgcGxlY3RydW0sIHBsZWN0cm9uJyxcbiAgNzE1OiAncGlja2VsaGF1YmUnLFxuICA3MTY6ICdwaWNrZXQgZmVuY2UsIHBhbGluZycsXG4gIDcxNzogJ3BpY2t1cCwgcGlja3VwIHRydWNrJyxcbiAgNzE4OiAncGllcicsXG4gIDcxOTogJ3BpZ2d5IGJhbmssIHBlbm55IGJhbmsnLFxuICA3MjA6ICdwaWxsIGJvdHRsZScsXG4gIDcyMTogJ3BpbGxvdycsXG4gIDcyMjogJ3BpbmctcG9uZyBiYWxsJyxcbiAgNzIzOiAncGlud2hlZWwnLFxuICA3MjQ6ICdwaXJhdGUsIHBpcmF0ZSBzaGlwJyxcbiAgNzI1OiAncGl0Y2hlciwgZXdlcicsXG4gIDcyNjogJ3BsYW5lLCBjYXJwZW50ZXJcXCdzIHBsYW5lLCB3b29kd29ya2luZyBwbGFuZScsXG4gIDcyNzogJ3BsYW5ldGFyaXVtJyxcbiAgNzI4OiAncGxhc3RpYyBiYWcnLFxuICA3Mjk6ICdwbGF0ZSByYWNrJyxcbiAgNzMwOiAncGxvdywgcGxvdWdoJyxcbiAgNzMxOiAncGx1bmdlciwgcGx1bWJlclxcJ3MgaGVscGVyJyxcbiAgNzMyOiAnUG9sYXJvaWQgY2FtZXJhLCBQb2xhcm9pZCBMYW5kIGNhbWVyYScsXG4gIDczMzogJ3BvbGUnLFxuICA3MzQ6XG4gICAgICAncG9saWNlIHZhbiwgcG9saWNlIHdhZ29uLCBwYWRkeSB3YWdvbiwgcGF0cm9sIHdhZ29uLCB3YWdvbiwgYmxhY2sgTWFyaWEnLFxuICA3MzU6ICdwb25jaG8nLFxuICA3MzY6ICdwb29sIHRhYmxlLCBiaWxsaWFyZCB0YWJsZSwgc25vb2tlciB0YWJsZScsXG4gIDczNzogJ3BvcCBib3R0bGUsIHNvZGEgYm90dGxlJyxcbiAgNzM4OiAncG90LCBmbG93ZXJwb3QnLFxuICA3Mzk6ICdwb3R0ZXJcXCdzIHdoZWVsJyxcbiAgNzQwOiAncG93ZXIgZHJpbGwnLFxuICA3NDE6ICdwcmF5ZXIgcnVnLCBwcmF5ZXIgbWF0JyxcbiAgNzQyOiAncHJpbnRlcicsXG4gIDc0MzogJ3ByaXNvbiwgcHJpc29uIGhvdXNlJyxcbiAgNzQ0OiAncHJvamVjdGlsZSwgbWlzc2lsZScsXG4gIDc0NTogJ3Byb2plY3RvcicsXG4gIDc0NjogJ3B1Y2ssIGhvY2tleSBwdWNrJyxcbiAgNzQ3OiAncHVuY2hpbmcgYmFnLCBwdW5jaCBiYWcsIHB1bmNoaW5nIGJhbGwsIHB1bmNoYmFsbCcsXG4gIDc0ODogJ3B1cnNlJyxcbiAgNzQ5OiAncXVpbGwsIHF1aWxsIHBlbicsXG4gIDc1MDogJ3F1aWx0LCBjb21mb3J0ZXIsIGNvbWZvcnQsIHB1ZmYnLFxuICA3NTE6ICdyYWNlciwgcmFjZSBjYXIsIHJhY2luZyBjYXInLFxuICA3NTI6ICdyYWNrZXQsIHJhY3F1ZXQnLFxuICA3NTM6ICdyYWRpYXRvcicsXG4gIDc1NDogJ3JhZGlvLCB3aXJlbGVzcycsXG4gIDc1NTogJ3JhZGlvIHRlbGVzY29wZSwgcmFkaW8gcmVmbGVjdG9yJyxcbiAgNzU2OiAncmFpbiBiYXJyZWwnLFxuICA3NTc6ICdyZWNyZWF0aW9uYWwgdmVoaWNsZSwgUlYsIFIuVi4nLFxuICA3NTg6ICdyZWVsJyxcbiAgNzU5OiAncmVmbGV4IGNhbWVyYScsXG4gIDc2MDogJ3JlZnJpZ2VyYXRvciwgaWNlYm94JyxcbiAgNzYxOiAncmVtb3RlIGNvbnRyb2wsIHJlbW90ZScsXG4gIDc2MjogJ3Jlc3RhdXJhbnQsIGVhdGluZyBob3VzZSwgZWF0aW5nIHBsYWNlLCBlYXRlcnknLFxuICA3NjM6ICdyZXZvbHZlciwgc2l4LWd1biwgc2l4LXNob290ZXInLFxuICA3NjQ6ICdyaWZsZScsXG4gIDc2NTogJ3JvY2tpbmcgY2hhaXIsIHJvY2tlcicsXG4gIDc2NjogJ3JvdGlzc2VyaWUnLFxuICA3Njc6ICdydWJiZXIgZXJhc2VyLCBydWJiZXIsIHBlbmNpbCBlcmFzZXInLFxuICA3Njg6ICdydWdieSBiYWxsJyxcbiAgNzY5OiAncnVsZSwgcnVsZXInLFxuICA3NzA6ICdydW5uaW5nIHNob2UnLFxuICA3NzE6ICdzYWZlJyxcbiAgNzcyOiAnc2FmZXR5IHBpbicsXG4gIDc3MzogJ3NhbHRzaGFrZXIsIHNhbHQgc2hha2VyJyxcbiAgNzc0OiAnc2FuZGFsJyxcbiAgNzc1OiAnc2Fyb25nJyxcbiAgNzc2OiAnc2F4LCBzYXhvcGhvbmUnLFxuICA3Nzc6ICdzY2FiYmFyZCcsXG4gIDc3ODogJ3NjYWxlLCB3ZWlnaGluZyBtYWNoaW5lJyxcbiAgNzc5OiAnc2Nob29sIGJ1cycsXG4gIDc4MDogJ3NjaG9vbmVyJyxcbiAgNzgxOiAnc2NvcmVib2FyZCcsXG4gIDc4MjogJ3NjcmVlbiwgQ1JUIHNjcmVlbicsXG4gIDc4MzogJ3NjcmV3JyxcbiAgNzg0OiAnc2NyZXdkcml2ZXInLFxuICA3ODU6ICdzZWF0IGJlbHQsIHNlYXRiZWx0JyxcbiAgNzg2OiAnc2V3aW5nIG1hY2hpbmUnLFxuICA3ODc6ICdzaGllbGQsIGJ1Y2tsZXInLFxuICA3ODg6ICdzaG9lIHNob3AsIHNob2Utc2hvcCwgc2hvZSBzdG9yZScsXG4gIDc4OTogJ3Nob2ppJyxcbiAgNzkwOiAnc2hvcHBpbmcgYmFza2V0JyxcbiAgNzkxOiAnc2hvcHBpbmcgY2FydCcsXG4gIDc5MjogJ3Nob3ZlbCcsXG4gIDc5MzogJ3Nob3dlciBjYXAnLFxuICA3OTQ6ICdzaG93ZXIgY3VydGFpbicsXG4gIDc5NTogJ3NraScsXG4gIDc5NjogJ3NraSBtYXNrJyxcbiAgNzk3OiAnc2xlZXBpbmcgYmFnJyxcbiAgNzk4OiAnc2xpZGUgcnVsZSwgc2xpcHN0aWNrJyxcbiAgNzk5OiAnc2xpZGluZyBkb29yJyxcbiAgODAwOiAnc2xvdCwgb25lLWFybWVkIGJhbmRpdCcsXG4gIDgwMTogJ3Nub3JrZWwnLFxuICA4MDI6ICdzbm93bW9iaWxlJyxcbiAgODAzOiAnc25vd3Bsb3csIHNub3dwbG91Z2gnLFxuICA4MDQ6ICdzb2FwIGRpc3BlbnNlcicsXG4gIDgwNTogJ3NvY2NlciBiYWxsJyxcbiAgODA2OiAnc29jaycsXG4gIDgwNzogJ3NvbGFyIGRpc2gsIHNvbGFyIGNvbGxlY3Rvciwgc29sYXIgZnVybmFjZScsXG4gIDgwODogJ3NvbWJyZXJvJyxcbiAgODA5OiAnc291cCBib3dsJyxcbiAgODEwOiAnc3BhY2UgYmFyJyxcbiAgODExOiAnc3BhY2UgaGVhdGVyJyxcbiAgODEyOiAnc3BhY2Ugc2h1dHRsZScsXG4gIDgxMzogJ3NwYXR1bGEnLFxuICA4MTQ6ICdzcGVlZGJvYXQnLFxuICA4MTU6ICdzcGlkZXIgd2ViLCBzcGlkZXJcXCdzIHdlYicsXG4gIDgxNjogJ3NwaW5kbGUnLFxuICA4MTc6ICdzcG9ydHMgY2FyLCBzcG9ydCBjYXInLFxuICA4MTg6ICdzcG90bGlnaHQsIHNwb3QnLFxuICA4MTk6ICdzdGFnZScsXG4gIDgyMDogJ3N0ZWFtIGxvY29tb3RpdmUnLFxuICA4MjE6ICdzdGVlbCBhcmNoIGJyaWRnZScsXG4gIDgyMjogJ3N0ZWVsIGRydW0nLFxuICA4MjM6ICdzdGV0aG9zY29wZScsXG4gIDgyNDogJ3N0b2xlJyxcbiAgODI1OiAnc3RvbmUgd2FsbCcsXG4gIDgyNjogJ3N0b3B3YXRjaCwgc3RvcCB3YXRjaCcsXG4gIDgyNzogJ3N0b3ZlJyxcbiAgODI4OiAnc3RyYWluZXInLFxuICA4Mjk6ICdzdHJlZXRjYXIsIHRyYW0sIHRyYW1jYXIsIHRyb2xsZXksIHRyb2xsZXkgY2FyJyxcbiAgODMwOiAnc3RyZXRjaGVyJyxcbiAgODMxOiAnc3R1ZGlvIGNvdWNoLCBkYXkgYmVkJyxcbiAgODMyOiAnc3R1cGEsIHRvcGUnLFxuICA4MzM6ICdzdWJtYXJpbmUsIHBpZ2JvYXQsIHN1YiwgVS1ib2F0JyxcbiAgODM0OiAnc3VpdCwgc3VpdCBvZiBjbG90aGVzJyxcbiAgODM1OiAnc3VuZGlhbCcsXG4gIDgzNjogJ3N1bmdsYXNzJyxcbiAgODM3OiAnc3VuZ2xhc3NlcywgZGFyayBnbGFzc2VzLCBzaGFkZXMnLFxuICA4Mzg6ICdzdW5zY3JlZW4sIHN1bmJsb2NrLCBzdW4gYmxvY2tlcicsXG4gIDgzOTogJ3N1c3BlbnNpb24gYnJpZGdlJyxcbiAgODQwOiAnc3dhYiwgc3dvYiwgbW9wJyxcbiAgODQxOiAnc3dlYXRzaGlydCcsXG4gIDg0MjogJ3N3aW1taW5nIHRydW5rcywgYmF0aGluZyB0cnVua3MnLFxuICA4NDM6ICdzd2luZycsXG4gIDg0NDogJ3N3aXRjaCwgZWxlY3RyaWMgc3dpdGNoLCBlbGVjdHJpY2FsIHN3aXRjaCcsXG4gIDg0NTogJ3N5cmluZ2UnLFxuICA4NDY6ICd0YWJsZSBsYW1wJyxcbiAgODQ3OiAndGFuaywgYXJteSB0YW5rLCBhcm1vcmVkIGNvbWJhdCB2ZWhpY2xlLCBhcm1vdXJlZCBjb21iYXQgdmVoaWNsZScsXG4gIDg0ODogJ3RhcGUgcGxheWVyJyxcbiAgODQ5OiAndGVhcG90JyxcbiAgODUwOiAndGVkZHksIHRlZGR5IGJlYXInLFxuICA4NTE6ICd0ZWxldmlzaW9uLCB0ZWxldmlzaW9uIHN5c3RlbScsXG4gIDg1MjogJ3Rlbm5pcyBiYWxsJyxcbiAgODUzOiAndGhhdGNoLCB0aGF0Y2hlZCByb29mJyxcbiAgODU0OiAndGhlYXRlciBjdXJ0YWluLCB0aGVhdHJlIGN1cnRhaW4nLFxuICA4NTU6ICd0aGltYmxlJyxcbiAgODU2OiAndGhyZXNoZXIsIHRocmFzaGVyLCB0aHJlc2hpbmcgbWFjaGluZScsXG4gIDg1NzogJ3Rocm9uZScsXG4gIDg1ODogJ3RpbGUgcm9vZicsXG4gIDg1OTogJ3RvYXN0ZXInLFxuICA4NjA6ICd0b2JhY2NvIHNob3AsIHRvYmFjY29uaXN0IHNob3AsIHRvYmFjY29uaXN0JyxcbiAgODYxOiAndG9pbGV0IHNlYXQnLFxuICA4NjI6ICd0b3JjaCcsXG4gIDg2MzogJ3RvdGVtIHBvbGUnLFxuICA4NjQ6ICd0b3cgdHJ1Y2ssIHRvdyBjYXIsIHdyZWNrZXInLFxuICA4NjU6ICd0b3lzaG9wJyxcbiAgODY2OiAndHJhY3RvcicsXG4gIDg2NzpcbiAgICAgICd0cmFpbGVyIHRydWNrLCB0cmFjdG9yIHRyYWlsZXIsIHRydWNraW5nIHJpZywgcmlnLCBhcnRpY3VsYXRlZCBsb3JyeSwgc2VtaScsXG4gIDg2ODogJ3RyYXknLFxuICA4Njk6ICd0cmVuY2ggY29hdCcsXG4gIDg3MDogJ3RyaWN5Y2xlLCB0cmlrZSwgdmVsb2NpcGVkZScsXG4gIDg3MTogJ3RyaW1hcmFuJyxcbiAgODcyOiAndHJpcG9kJyxcbiAgODczOiAndHJpdW1waGFsIGFyY2gnLFxuICA4NzQ6ICd0cm9sbGV5YnVzLCB0cm9sbGV5IGNvYWNoLCB0cmFja2xlc3MgdHJvbGxleScsXG4gIDg3NTogJ3Ryb21ib25lJyxcbiAgODc2OiAndHViLCB2YXQnLFxuICA4Nzc6ICd0dXJuc3RpbGUnLFxuICA4Nzg6ICd0eXBld3JpdGVyIGtleWJvYXJkJyxcbiAgODc5OiAndW1icmVsbGEnLFxuICA4ODA6ICd1bmljeWNsZSwgbW9ub2N5Y2xlJyxcbiAgODgxOiAndXByaWdodCwgdXByaWdodCBwaWFubycsXG4gIDg4MjogJ3ZhY3V1bSwgdmFjdXVtIGNsZWFuZXInLFxuICA4ODM6ICd2YXNlJyxcbiAgODg0OiAndmF1bHQnLFxuICA4ODU6ICd2ZWx2ZXQnLFxuICA4ODY6ICd2ZW5kaW5nIG1hY2hpbmUnLFxuICA4ODc6ICd2ZXN0bWVudCcsXG4gIDg4ODogJ3ZpYWR1Y3QnLFxuICA4ODk6ICd2aW9saW4sIGZpZGRsZScsXG4gIDg5MDogJ3ZvbGxleWJhbGwnLFxuICA4OTE6ICd3YWZmbGUgaXJvbicsXG4gIDg5MjogJ3dhbGwgY2xvY2snLFxuICA4OTM6ICd3YWxsZXQsIGJpbGxmb2xkLCBub3RlY2FzZSwgcG9ja2V0Ym9vaycsXG4gIDg5NDogJ3dhcmRyb2JlLCBjbG9zZXQsIHByZXNzJyxcbiAgODk1OiAnd2FycGxhbmUsIG1pbGl0YXJ5IHBsYW5lJyxcbiAgODk2OiAnd2FzaGJhc2luLCBoYW5kYmFzaW4sIHdhc2hib3dsLCBsYXZhYm8sIHdhc2gtaGFuZCBiYXNpbicsXG4gIDg5NzogJ3dhc2hlciwgYXV0b21hdGljIHdhc2hlciwgd2FzaGluZyBtYWNoaW5lJyxcbiAgODk4OiAnd2F0ZXIgYm90dGxlJyxcbiAgODk5OiAnd2F0ZXIganVnJyxcbiAgOTAwOiAnd2F0ZXIgdG93ZXInLFxuICA5MDE6ICd3aGlza2V5IGp1ZycsXG4gIDkwMjogJ3doaXN0bGUnLFxuICA5MDM6ICd3aWcnLFxuICA5MDQ6ICd3aW5kb3cgc2NyZWVuJyxcbiAgOTA1OiAnd2luZG93IHNoYWRlJyxcbiAgOTA2OiAnV2luZHNvciB0aWUnLFxuICA5MDc6ICd3aW5lIGJvdHRsZScsXG4gIDkwODogJ3dpbmcnLFxuICA5MDk6ICd3b2snLFxuICA5MTA6ICd3b29kZW4gc3Bvb24nLFxuICA5MTE6ICd3b29sLCB3b29sZW4sIHdvb2xsZW4nLFxuICA5MTI6ICd3b3JtIGZlbmNlLCBzbmFrZSBmZW5jZSwgc25ha2UtcmFpbCBmZW5jZSwgVmlyZ2luaWEgZmVuY2UnLFxuICA5MTM6ICd3cmVjaycsXG4gIDkxNDogJ3lhd2wnLFxuICA5MTU6ICd5dXJ0JyxcbiAgOTE2OiAnd2ViIHNpdGUsIHdlYnNpdGUsIGludGVybmV0IHNpdGUsIHNpdGUnLFxuICA5MTc6ICdjb21pYyBib29rJyxcbiAgOTE4OiAnY3Jvc3N3b3JkIHB1enpsZSwgY3Jvc3N3b3JkJyxcbiAgOTE5OiAnc3RyZWV0IHNpZ24nLFxuICA5MjA6ICd0cmFmZmljIGxpZ2h0LCB0cmFmZmljIHNpZ25hbCwgc3RvcGxpZ2h0JyxcbiAgOTIxOiAnYm9vayBqYWNrZXQsIGR1c3QgY292ZXIsIGR1c3QgamFja2V0LCBkdXN0IHdyYXBwZXInLFxuICA5MjI6ICdtZW51JyxcbiAgOTIzOiAncGxhdGUnLFxuICA5MjQ6ICdndWFjYW1vbGUnLFxuICA5MjU6ICdjb25zb21tZScsXG4gIDkyNjogJ2hvdCBwb3QsIGhvdHBvdCcsXG4gIDkyNzogJ3RyaWZsZScsXG4gIDkyODogJ2ljZSBjcmVhbSwgaWNlY3JlYW0nLFxuICA5Mjk6ICdpY2UgbG9sbHksIGxvbGx5LCBsb2xsaXBvcCwgcG9wc2ljbGUnLFxuICA5MzA6ICdGcmVuY2ggbG9hZicsXG4gIDkzMTogJ2JhZ2VsLCBiZWlnZWwnLFxuICA5MzI6ICdwcmV0emVsJyxcbiAgOTMzOiAnY2hlZXNlYnVyZ2VyJyxcbiAgOTM0OiAnaG90ZG9nLCBob3QgZG9nLCByZWQgaG90JyxcbiAgOTM1OiAnbWFzaGVkIHBvdGF0bycsXG4gIDkzNjogJ2hlYWQgY2FiYmFnZScsXG4gIDkzNzogJ2Jyb2Njb2xpJyxcbiAgOTM4OiAnY2F1bGlmbG93ZXInLFxuICA5Mzk6ICd6dWNjaGluaSwgY291cmdldHRlJyxcbiAgOTQwOiAnc3BhZ2hldHRpIHNxdWFzaCcsXG4gIDk0MTogJ2Fjb3JuIHNxdWFzaCcsXG4gIDk0MjogJ2J1dHRlcm51dCBzcXVhc2gnLFxuICA5NDM6ICdjdWN1bWJlciwgY3VrZScsXG4gIDk0NDogJ2FydGljaG9rZSwgZ2xvYmUgYXJ0aWNob2tlJyxcbiAgOTQ1OiAnYmVsbCBwZXBwZXInLFxuICA5NDY6ICdjYXJkb29uJyxcbiAgOTQ3OiAnbXVzaHJvb20nLFxuICA5NDg6ICdHcmFubnkgU21pdGgnLFxuICA5NDk6ICdzdHJhd2JlcnJ5JyxcbiAgOTUwOiAnb3JhbmdlJyxcbiAgOTUxOiAnbGVtb24nLFxuICA5NTI6ICdmaWcnLFxuICA5NTM6ICdwaW5lYXBwbGUsIGFuYW5hcycsXG4gIDk1NDogJ2JhbmFuYScsXG4gIDk1NTogJ2phY2tmcnVpdCwgamFrLCBqYWNrJyxcbiAgOTU2OiAnY3VzdGFyZCBhcHBsZScsXG4gIDk1NzogJ3BvbWVncmFuYXRlJyxcbiAgOTU4OiAnaGF5JyxcbiAgOTU5OiAnY2FyYm9uYXJhJyxcbiAgOTYwOiAnY2hvY29sYXRlIHNhdWNlLCBjaG9jb2xhdGUgc3lydXAnLFxuICA5NjE6ICdkb3VnaCcsXG4gIDk2MjogJ21lYXQgbG9hZiwgbWVhdGxvYWYnLFxuICA5NjM6ICdwaXp6YSwgcGl6emEgcGllJyxcbiAgOTY0OiAncG90cGllJyxcbiAgOTY1OiAnYnVycml0bycsXG4gIDk2NjogJ3JlZCB3aW5lJyxcbiAgOTY3OiAnZXNwcmVzc28nLFxuICA5Njg6ICdjdXAnLFxuICA5Njk6ICdlZ2dub2cnLFxuICA5NzA6ICdhbHAnLFxuICA5NzE6ICdidWJibGUnLFxuICA5NzI6ICdjbGlmZiwgZHJvcCwgZHJvcC1vZmYnLFxuICA5NzM6ICdjb3JhbCByZWVmJyxcbiAgOTc0OiAnZ2V5c2VyJyxcbiAgOTc1OiAnbGFrZXNpZGUsIGxha2VzaG9yZScsXG4gIDk3NjogJ3Byb21vbnRvcnksIGhlYWRsYW5kLCBoZWFkLCBmb3JlbGFuZCcsXG4gIDk3NzogJ3NhbmRiYXIsIHNhbmQgYmFyJyxcbiAgOTc4OiAnc2Vhc2hvcmUsIGNvYXN0LCBzZWFjb2FzdCwgc2VhLWNvYXN0JyxcbiAgOTc5OiAndmFsbGV5LCB2YWxlJyxcbiAgOTgwOiAndm9sY2FubycsXG4gIDk4MTogJ2JhbGxwbGF5ZXIsIGJhc2ViYWxsIHBsYXllcicsXG4gIDk4MjogJ2dyb29tLCBicmlkZWdyb29tJyxcbiAgOTgzOiAnc2N1YmEgZGl2ZXInLFxuICA5ODQ6ICdyYXBlc2VlZCcsXG4gIDk4NTogJ2RhaXN5JyxcbiAgOTg2OlxuICAgICAgJ3llbGxvdyBsYWR5XFwncyBzbGlwcGVyLCB5ZWxsb3cgbGFkeS1zbGlwcGVyLCBDeXByaXBlZGl1bSBjYWxjZW9sdXMsIEN5cHJpcGVkaXVtIHBhcnZpZmxvcnVtJyxcbiAgOTg3OiAnY29ybicsXG4gIDk4ODogJ2Fjb3JuJyxcbiAgOTg5OiAnaGlwLCByb3NlIGhpcCwgcm9zZWhpcCcsXG4gIDk5MDogJ2J1Y2tleWUsIGhvcnNlIGNoZXN0bnV0LCBjb25rZXInLFxuICA5OTE6ICdjb3JhbCBmdW5ndXMnLFxuICA5OTI6ICdhZ2FyaWMnLFxuICA5OTM6ICdneXJvbWl0cmEnLFxuICA5OTQ6ICdzdGlua2hvcm4sIGNhcnJpb24gZnVuZ3VzJyxcbiAgOTk1OiAnZWFydGhzdGFyJyxcbiAgOTk2OlxuICAgICAgJ2hlbi1vZi10aGUtd29vZHMsIGhlbiBvZiB0aGUgd29vZHMsIFBvbHlwb3J1cyBmcm9uZG9zdXMsIEdyaWZvbGEgZnJvbmRvc2EnLFxuICA5OTc6ICdib2xldGUnLFxuICA5OTg6ICdlYXIsIHNwaWtlLCBjYXBpdHVsdW0nLFxuICA5OTk6ICd0b2lsZXQgdGlzc3VlLCB0b2lsZXQgcGFwZXIsIGJhdGhyb29tIHRpc3N1ZSdcbn07XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuLi8uLi9zcmMvbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi4vLi4vc3JjL21hdGgvd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbi8qKlxuICogVW5wYWNrcyBhbiBSR0IgcGFja2VkIGltYWdlIHRleHR1cmUgaW50byBhIDJEIHBoeXNpY2FsLCAzRCBsb2dpY2FsIHRleHR1cmVcbiAqIHdpdGggdGhlIGNvbnZlbnRpb25hbCBuZGFycmF5IGZvcm1hdCBhbmQgcGVyZm9ybXMgdGhlIHN0YW5kYXJkIGltYWdlbmV0IGltYWdlXG4gKiBwcmVwcm9jZXNzaW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrQW5kUHJlcHJvY2Vzc0lucHV0U2hhZGVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGlucHV0U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSk6IFdlYkdMUHJvZ3JhbSB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiBpbnB1dFNoYXBlQ1IgPSB2ZWMyKCR7aW5wdXRTaGFwZVJDWzFdfS4wLCAke2lucHV0U2hhcGVSQ1swXX0uMCk7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBvdXRwdXRDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIHZlYzIgc291cmNlQ1IgPSB2ZWMyKGZsb29yKG91dHB1dENSWzBdIC8gMy4wKSwgb3V0cHV0Q1JbMV0pO1xuICAgICAgdmVjMiBzb3VyY2VVViA9IChzb3VyY2VDUiArIGhhbGZDUikgLyBpbnB1dFNoYXBlQ1I7XG5cbiAgICAgIHZlYzQgc291cmNlVmFsdWUgPSB0ZXh0dXJlMkQoc291cmNlLCBzb3VyY2VVVikgKiAyNTUuMDtcblxuICAgICAgZmxvYXQgY2hhbm5lbFZhbHVlID0gMC4wO1xuICAgICAgaW50IGNoYW5uZWwgPSBpbnQobW9kKG91dHB1dENSWzBdLCAzLjApKTtcblxuICAgICAgaWYgKGNoYW5uZWwgPT0gMCkge1xuICAgICAgICBjaGFubmVsVmFsdWUgPSBzb3VyY2VWYWx1ZS5yIC0gMTAzLjkzOTtcbiAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbCA9PSAxKSB7XG4gICAgICAgIGNoYW5uZWxWYWx1ZSA9IHNvdXJjZVZhbHVlLmcgLSAxMTYuNzc5O1xuICAgICAgfSBlbHNlIGlmIChjaGFubmVsID09IDIpIHtcbiAgICAgICAgY2hhbm5lbFZhbHVlID0gc291cmNlVmFsdWUuYiAtIDEyMy42ODtcbiAgICAgIH1cblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChjaGFubmVsVmFsdWUsIDAsIDAsIDApO1xuICAgIH1gO1xuICByZXR1cm4gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwcmVwcm9jZXNzSW5wdXQoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJlcHJvY2Vzc0lucHV0U2hhZGVyOiBXZWJHTFByb2dyYW0sXG4gICAgc291cmNlVGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleDogV2ViR0xUZXh0dXJlLFxuICAgIHNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0VGV4LCBzaGFwZVJvd0NvbFswXSwgc2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByZXByb2Nlc3NJbnB1dFNoYWRlcik7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShzb3VyY2VUZXgsICdzb3VyY2UnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuLyoqXG4gKiBUcmFuc3Bvc2VzIHRoZSBkZXB0aCBhbmQgdGhlIGNvbHVtbiBkaW1lbnNpb25zIG9mIGEgM0QgbmRhcnJheSByZXByZXNlbnRlZCBhc1xuICogYSAyRCB0ZXh0dXJlIGludG8gYSBzcXVhcmUgY29sbGFnZSB3aXRoIGVhY2ggY2hhbm5lbCByZW5kZXJlZCBhcyBhIG5vcm1hbGl6ZWRcbiAqIGdyYXlzY2FsZSBpbWFnZS4gVGhlIG5vcm1hbGl6YXRpb24gYm91bmRzIGFyZSBnaXZlbiBhcyB0d28gc2FtcGxlMkRzLFxuICogbWluVmFsdWVzIGFuZCBtYXhWYWx1ZXMsIHdoaWNoIGdpdmUgbWluIGFuZCBtYXggdmFsdWVzIHBlciBjaGFubmVsLiBUaGVzZSBjYW5cbiAqIGJlIGNvbXB1dGVkIGZyb20gYSBtYXggYW5kIG1pbiBwb29saW5nIGxheWVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmVuZGVyR3JheXNjYWxlQ2hhbm5lbHNDb2xsYWdlU2hhZGVyKGdwZ3B1OiBHUEdQVUNvbnRleHQpOlxuICAgIFdlYkdMUHJvZ3JhbSB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWluVmFsdWVzO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1heFZhbHVlcztcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB1bmlmb3JtIGZsb2F0IGltYWdlU2l6ZTtcbiAgICB1bmlmb3JtIGZsb2F0IGNoYW5uZWxzO1xuICAgIHVuaWZvcm0gZmxvYXQgaW1hZ2VzUGVyUm93O1xuICAgIHVuaWZvcm0gdmVjMiBpbnB1dFNoYXBlQ1I7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBvdXRwdXRDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIGZsb2F0IGltYWdlUm93ID0gZmxvb3Iob3V0cHV0Q1JbMV0gLyBpbWFnZVNpemUpO1xuICAgICAgZmxvYXQgaW1hZ2VDb2wgPSBtb2Qob3V0cHV0Q1JbMF0sIGltYWdlU2l6ZSk7XG5cbiAgICAgIGZsb2F0IGN1cnJlbnRDaGFubmVsID0gZmxvb3Iob3V0cHV0Q1JbMF0gLyBpbWFnZVNpemUpICtcbiAgICAgICAgICBpbWFnZVJvdyAqIGltYWdlc1BlclJvdztcblxuICAgICAgLy8gV2hlbiB0aGUgbnVtYmVyIG9mIGNoYW5uZWxzIGlzIG5vdCBzcXVhcmUsIHdlIHJlbmRlciB3aGl0ZSB0byBmaWxsIGluXG4gICAgICAvLyB0aGUgb3V0cHV0IHRleHR1cmUuXG4gICAgICBpZiAoY3VycmVudENoYW5uZWwgPiBjaGFubmVscykge1xuICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KDEuMCwgMS4wLCAxLjAsIDEuMCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgZmxvYXQgc291cmNlQyA9IGNoYW5uZWxzICogaW1hZ2VDb2wgKyBjdXJyZW50Q2hhbm5lbDtcbiAgICAgIGZsb2F0IHNvdXJjZVIgPSBtb2Qob3V0cHV0Q1JbMV0sIGltYWdlU2l6ZSk7XG5cbiAgICAgIHZlYzIgc291cmNlVVYgPSAodmVjMihzb3VyY2VDLCBzb3VyY2VSKSArIGhhbGZDUikgLyBpbnB1dFNoYXBlQ1I7XG5cbiAgICAgIC8vIEZsaXAgdGhlIHZlcnRpY2FsIGF4aXMgb2YgdGhlIHRleHR1cmUgZm9yIGRpc3BsYXkgc2luY2Ugd2UgcmVwcmVzZW50XG4gICAgICAvLyBpbWFnZSB0ZXh0dXJlcyBhcyB2ZXJ0aWNhbGx5IGZsaXBwZWQuXG4gICAgICBmbG9hdCBzb3VyY2VWYWx1ZSA9IHRleHR1cmUyRChcbiAgICAgICAgICBzb3VyY2UsIHZlYzIoc291cmNlVVYucywgMS4wIC0gc291cmNlVVYudCkpLnI7XG5cbiAgICAgIC8vIE5vcm1hbGl6ZSB0aGUgdmFsdWUgYnkgc2FtcGxpbmcgdGhlIG1pblZhbHVlcyBhbmQgbWF4VmFsdWVzIHRleHR1cmVcbiAgICAgIC8vIHdoaWNoIGNvbnRhaW4gbWluIGFuZCBtYXggcGVyIGNoYW5uZWwuXG4gICAgICB2ZWMyIG1pbk1heFZhbHVlc1NoYXBlQ1IgPSB2ZWMyKGNoYW5uZWxzLCAxKTtcbiAgICAgIHZlYzIgbWluTWF4VmFsdWVzQ1IgPSB2ZWMyKGN1cnJlbnRDaGFubmVsLCAwKTtcbiAgICAgIHZlYzIgbWluTWF4VmFsdWVzVVYgPSAobWluTWF4VmFsdWVzQ1IgKyBoYWxmQ1IpIC8gbWluTWF4VmFsdWVzU2hhcGVDUjtcblxuICAgICAgZmxvYXQgbWluVmFsdWUgPSB0ZXh0dXJlMkQobWluVmFsdWVzLCBtaW5NYXhWYWx1ZXNVVikucjtcbiAgICAgIGZsb2F0IG1heFZhbHVlID0gdGV4dHVyZTJEKG1heFZhbHVlcywgbWluTWF4VmFsdWVzVVYpLnI7XG5cbiAgICAgIGZsb2F0IG5vcm1hbGl6ZWRWYWx1ZSA9IChzb3VyY2VWYWx1ZSAtIG1pblZhbHVlKSAvIChtYXhWYWx1ZSAtIG1pblZhbHVlKTtcblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChcbiAgICAgICAgICBub3JtYWxpemVkVmFsdWUsIG5vcm1hbGl6ZWRWYWx1ZSwgbm9ybWFsaXplZFZhbHVlLCAxKTtcbiAgICB9XG4gIGA7XG4gIHJldHVybiBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlckdyYXlzY2FsZUNoYW5uZWxzQ29sbGFnZShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCB1bnBhY2tDaGFubmVsc1NoYWRlcjogV2ViR0xQcm9ncmFtLFxuICAgIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlLCBtaW5WYWx1ZXNUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICBtYXhWYWx1ZXNUZXg6IFdlYkdMVGV4dHVyZSwgaW5wdXRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGltYWdlU2l6ZTogbnVtYmVyLCBjaGFubmVsczogbnVtYmVyLCB0ZXh0dXJlU2l6ZTogbnVtYmVyLCBudW1Sb3dzOiBudW1iZXIpIHtcbiAgd2ViZ2xfdXRpbC5iaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihncGdwdS5nbCk7XG4gIGdwZ3B1LnNldFByb2dyYW0odW5wYWNrQ2hhbm5lbHNTaGFkZXIpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc291cmNlVGV4LCAnc291cmNlJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShtaW5WYWx1ZXNUZXgsICdtaW5WYWx1ZXMnLCAxKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKG1heFZhbHVlc1RleCwgJ21heFZhbHVlcycsIDIpO1xuXG4gIGNvbnN0IGltYWdlU2l6ZUxvYyA9IGdwZ3B1LmdldFVuaWZvcm1Mb2NhdGlvbignaW1hZ2VTaXplJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0xZihpbWFnZVNpemVMb2MsIGltYWdlU2l6ZSk7XG5cbiAgY29uc3QgY2hhbm5lbHNMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2NoYW5uZWxzJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0xZihjaGFubmVsc0xvYywgY2hhbm5lbHMpO1xuXG4gIGNvbnN0IGltYWdlc1BlclJvd0xvYyA9IGdwZ3B1LmdldFVuaWZvcm1Mb2NhdGlvbignaW1hZ2VzUGVyUm93Jyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0xZihpbWFnZXNQZXJSb3dMb2MsIE1hdGguZmxvb3IodGV4dHVyZVNpemUgLyBpbWFnZVNpemUpKTtcblxuICBjb25zdCBpbnB1dFNoYXBlQ1JMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2lucHV0U2hhcGVDUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYoaW5wdXRTaGFwZUNSTG9jLCBpbnB1dFNoYXBlUkNbMV0sIGlucHV0U2hhcGVSQ1swXSk7XG5cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtDaGVja3BvaW50TG9hZGVyfSBmcm9tICcuLi8uLi9zcmMvY2hlY2twb2ludF9sb2FkZXInO1xuaW1wb3J0IHtOREFycmF5TWF0aENQVX0gZnJvbSAnLi4vLi4vc3JjL21hdGgvbWF0aF9jcHUnO1xuaW1wb3J0IHtOREFycmF5TWF0aEdQVX0gZnJvbSAnLi4vLi4vc3JjL21hdGgvbWF0aF9ncHUnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5fSBmcm9tICcuLi8uLi9zcmMvbWF0aC9uZGFycmF5JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuLi8uLi9zcmMvbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0JztcblxuaW1wb3J0ICogYXMgaW1hZ2VuZXRfY2xhc3NlcyBmcm9tICcuL2ltYWdlbmV0X2NsYXNzZXMnO1xuaW1wb3J0ICogYXMgaW1hZ2VuZXRfdXRpbCBmcm9tICcuL2ltYWdlbmV0X3V0aWwnO1xuXG5jb25zdCBJTUFHRV9TSVpFID0gMjI3O1xuY29uc3QgTlVNX0NMQVNTRVMgPSAxMDAwO1xuY29uc3QgR09PR0xFX0NMT1VEX1NUT1JBR0VfRElSID1cbiAgICAnaHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL2xlYXJuanMtZGF0YS9jaGVja3BvaW50X3pvby8nO1xuXG5leHBvcnQgY2xhc3MgU3F1ZWV6ZU5ldCB7XG4gIHByaXZhdGUgdmFyaWFibGVzOiB7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9O1xuXG4gIHByaXZhdGUgcHJlcHJvY2Vzc0lucHV0U2hhZGVyOiBXZWJHTFNoYWRlcjtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGhHUFUpIHtcbiAgICB0aGlzLnByZXByb2Nlc3NJbnB1dFNoYWRlciA9XG4gICAgICAgIGltYWdlbmV0X3V0aWwuZ2V0VW5wYWNrQW5kUHJlcHJvY2Vzc0lucHV0U2hhZGVyKFxuICAgICAgICAgICAgZ3BncHUsIFtJTUFHRV9TSVpFLCBJTUFHRV9TSVpFXSk7XG4gIH1cblxuICAvKipcbiAgICogTG9hZHMgbmVjZXNzYXJ5IHZhcmlhYmxlcyBmb3IgU3F1ZWV6ZU5ldC4gUmVzb2x2ZXMgdGhlIHByb21pc2Ugd2hlbiB0aGVcbiAgICogdmFyaWFibGVzIGhhdmUgYWxsIGJlZW4gbG9hZGVkLlxuICAgKi9cbiAgbG9hZFZhcmlhYmxlcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgY2hlY2twb2ludExvYWRlciA9XG4gICAgICAgICAgbmV3IENoZWNrcG9pbnRMb2FkZXIoR09PR0xFX0NMT1VEX1NUT1JBR0VfRElSICsgJ3NxdWVlemVuZXQxXzEvJyk7XG4gICAgICBjaGVja3BvaW50TG9hZGVyLmdldEFsbFZhcmlhYmxlcygpLnRoZW4odmFyaWFibGVzID0+IHtcbiAgICAgICAgdGhpcy52YXJpYWJsZXMgPSB2YXJpYWJsZXM7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFByZXByb2Nlc3MgYW4gUkdCIGNvbG9yIHRleHR1cmUgYmVmb3JlIGluZmVycmluZyB0aHJvdWdoIHNxdWVlemVuZXQuXG4gICAqIEBwYXJhbSByZ2JUZXh0dXJlIFRoZSBSR0IgY29sb3IgdGV4dHVyZSB0byBwcm9jZXNzIGludG8gYW4gQXJyYXkzRC5cbiAgICogQHBhcmFtIGltYWdlRGltZW5zaW9ucyBUaGUgMkQgZGltZW5zaW9ucyBvZiB0aGUgaW1hZ2UuXG4gICAqL1xuICBwcmVwcm9jZXNzQ29sb3JUZXh0dXJlVG9BcnJheTNEKHJnYlRleHR1cmU6IFdlYkdMVGV4dHVyZSwgaW1hZ2VEaW1lbnNpb25zOiBbXG4gICAgbnVtYmVyLCBudW1iZXJcbiAgXSk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHByZXByb2Nlc3NSZXN1bHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgW2ltYWdlRGltZW5zaW9uc1swXSwgaW1hZ2VEaW1lbnNpb25zWzBdICogM107XG5cbiAgICBjb25zdCBwcmVwcm9jZXNzUmVzdWx0VGV4dHVyZSA9XG4gICAgICAgIHRoaXMubWF0aC5nZXRUZXh0dXJlTWFuYWdlcigpLmFjcXVpcmVUZXh0dXJlKHByZXByb2Nlc3NSZXN1bHRTaGFwZVJDKTtcblxuICAgIGltYWdlbmV0X3V0aWwucHJlcHJvY2Vzc0lucHV0KFxuICAgICAgICB0aGlzLmdwZ3B1LCB0aGlzLnByZXByb2Nlc3NJbnB1dFNoYWRlciwgcmdiVGV4dHVyZSxcbiAgICAgICAgcHJlcHJvY2Vzc1Jlc3VsdFRleHR1cmUsIHByZXByb2Nlc3NSZXN1bHRTaGFwZVJDKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFtpbWFnZURpbWVuc2lvbnNbMF0sIGltYWdlRGltZW5zaW9uc1swXSwgM10sIHtcbiAgICAgIHRleHR1cmU6IHByZXByb2Nlc3NSZXN1bHRUZXh0dXJlLFxuICAgICAgdGV4dHVyZVNoYXBlUkM6IHByZXByb2Nlc3NSZXN1bHRTaGFwZVJDXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogSW5mZXIgdGhyb3VnaCBTcXVlZXplTmV0LCBhc3N1bWVzIHZhcmlhYmxlcyBoYXZlIGJlZW4gbG9hZGVkLiBUaGlzIGRvZXNcbiAgICogc3RhbmRhcmQgSW1hZ2VOZXQgcHJlLXByb2Nlc3NpbmcgYmVmb3JlIGluZmVycmluZyB0aHJvdWdoIHRoZSBtb2RlbC4gVGhpc1xuICAgKiBtZXRob2QgcmV0dXJucyBuYW1lZCBhY3RpdmF0aW9ucyBhcyB3ZWxsIGFzIHByZS1zb2Z0bWF4IGxvZ2l0cy4gVGhlIHVzZXJcbiAgICogbmVlZHMgdG8gY2xlYW4gdXAgbmFtZWRBY3RpdmF0aW9ucyBhZnRlciBpbmZlcnJpbmcuXG4gICAqXG4gICAqIEBwYXJhbSBwcmVwcm9jZXNzZWRJbnB1dCBwcmVwcm9jZXNzZWQgaW5wdXQgQXJyYXkuXG4gICAqIEByZXR1cm4gTmFtZWQgYWN0aXZhdGlvbnMgYW5kIHRoZSBwcmUtc29mdG1heCBsb2dpdHMuXG4gICAqL1xuICBpbmZlcihwcmVwcm9jZXNzZWRJbnB1dDogQXJyYXkzRCk6XG4gICAgICB7bmFtZWRBY3RpdmF0aW9uczoge1thY3RpdmF0aW9uTmFtZTogc3RyaW5nXTogQXJyYXkzRH0sIGxvZ2l0czogQXJyYXkxRH0ge1xuICAgIGNvbnN0IG5hbWVkQWN0aXZhdGlvbnM6IHtba2V5OiBzdHJpbmddOiBBcnJheTNEfSA9IHt9O1xuXG4gICAgY29uc3QgYXZncG9vbDEwID0gdGhpcy5tYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBjb252MSA9IHRoaXMubWF0aC5jb252MmQoXG4gICAgICAgICAgcHJlcHJvY2Vzc2VkSW5wdXQsIHRoaXMudmFyaWFibGVzWydjb252MV9XOjAnXSBhcyBBcnJheTRELFxuICAgICAgICAgIHRoaXMudmFyaWFibGVzWydjb252MV9iOjAnXSBhcyBBcnJheTFELCAyLCAwKTtcbiAgICAgIGNvbnN0IGNvbnYxcmVsdSA9IGtlZXAodGhpcy5tYXRoLnJlbHUoY29udjEpKTtcbiAgICAgIG5hbWVkQWN0aXZhdGlvbnNbJ2NvbnZfMSddID0gY29udjFyZWx1O1xuXG4gICAgICBjb25zdCBwb29sMSA9IGtlZXAodGhpcy5tYXRoLm1heFBvb2woY29udjFyZWx1LCAzLCAyLCAwKSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydtYXhwb29sXzEnXSA9IHBvb2wxO1xuXG4gICAgICBjb25zdCBmaXJlMiA9IGtlZXAodGhpcy5maXJlTW9kdWxlKHBvb2wxLCAyKSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydmaXJlMiddID0gZmlyZTI7XG5cbiAgICAgIGNvbnN0IGZpcmUzID0ga2VlcCh0aGlzLmZpcmVNb2R1bGUoZmlyZTIsIDMpKTtcbiAgICAgIG5hbWVkQWN0aXZhdGlvbnNbJ2ZpcmUzJ10gPSBmaXJlMztcblxuICAgICAgLy8gQmVjYXVzZSB3ZSBkb24ndCBoYXZlIHVuZXZlbiBwYWRkaW5nIHlldCwgbWFudWFsbHkgcGFkIHRoZSBuZGFycmF5IG9uXG4gICAgICAvLyB0aGUgcmlnaHQuXG4gICAgICBjb25zdCBmaXJlM1Jlc2hhcGUyZCA9XG4gICAgICAgICAgZmlyZTMuYXMyRChmaXJlMy5zaGFwZVswXSwgZmlyZTMuc2hhcGVbMV0gKiBmaXJlMy5zaGFwZVsyXSk7XG4gICAgICBjb25zdCBmaXJlM1NsaWNlZDJkID0gdGhpcy5tYXRoLnNsaWNlMkQoXG4gICAgICAgICAgZmlyZTNSZXNoYXBlMmQsIFswLCAwXSxcbiAgICAgICAgICBbZmlyZTMuc2hhcGVbMF0gLSAxLCAoZmlyZTMuc2hhcGVbMV0gLSAxKSAqIGZpcmUzLnNoYXBlWzJdXSk7XG4gICAgICBjb25zdCBmaXJlM1NsaWNlZCA9IGZpcmUzU2xpY2VkMmQuYXMzRChcbiAgICAgICAgICBmaXJlMy5zaGFwZVswXSAtIDEsIGZpcmUzLnNoYXBlWzFdIC0gMSwgZmlyZTMuc2hhcGVbMl0pO1xuICAgICAgY29uc3QgcG9vbDIgPSBrZWVwKHRoaXMubWF0aC5tYXhQb29sKGZpcmUzU2xpY2VkLCAzLCAyLCAwKSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydtYXhwb29sXzInXSA9IHBvb2wyO1xuXG4gICAgICBjb25zdCBmaXJlNCA9IGtlZXAodGhpcy5maXJlTW9kdWxlKHBvb2wyLCA0KSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydmaXJlNCddID0gZmlyZTQ7XG5cbiAgICAgIGNvbnN0IGZpcmU1ID0ga2VlcCh0aGlzLmZpcmVNb2R1bGUoZmlyZTQsIDUpKTtcbiAgICAgIG5hbWVkQWN0aXZhdGlvbnNbJ2ZpcmU1J10gPSBmaXJlNTtcblxuICAgICAgY29uc3QgcG9vbDMgPSBrZWVwKHRoaXMubWF0aC5tYXhQb29sKGZpcmU1LCAzLCAyLCAwKSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydtYXhwb29sXzMnXSA9IHBvb2wzO1xuXG4gICAgICBjb25zdCBmaXJlNiA9IGtlZXAodGhpcy5maXJlTW9kdWxlKHBvb2wzLCA2KSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydmaXJlNiddID0gZmlyZTY7XG5cbiAgICAgIGNvbnN0IGZpcmU3ID0ga2VlcCh0aGlzLmZpcmVNb2R1bGUoZmlyZTYsIDcpKTtcbiAgICAgIG5hbWVkQWN0aXZhdGlvbnNbJ2ZpcmU3J10gPSBmaXJlNztcblxuICAgICAgY29uc3QgZmlyZTggPSBrZWVwKHRoaXMuZmlyZU1vZHVsZShmaXJlNywgOCkpO1xuICAgICAgbmFtZWRBY3RpdmF0aW9uc1snZmlyZTgnXSA9IGZpcmU4O1xuXG4gICAgICBjb25zdCBmaXJlOSA9IGtlZXAodGhpcy5maXJlTW9kdWxlKGZpcmU4LCA5KSk7XG4gICAgICBuYW1lZEFjdGl2YXRpb25zWydmaXJlOSddID0gZmlyZTk7XG5cbiAgICAgIGNvbnN0IGNvbnYxMCA9IGtlZXAodGhpcy5tYXRoLmNvbnYyZChcbiAgICAgICAgICBmaXJlOSwgdGhpcy52YXJpYWJsZXNbJ2NvbnYxMF9XOjAnXSBhcyBBcnJheTRELFxuICAgICAgICAgIHRoaXMudmFyaWFibGVzWydjb252MTBfYjowJ10gYXMgQXJyYXkxRCwgMSwgMCkpO1xuICAgICAgbmFtZWRBY3RpdmF0aW9uc1snY29udjEwJ10gPSBjb252MTA7XG5cbiAgICAgIHJldHVybiB0aGlzLm1hdGguYXZnUG9vbChjb252MTAsIGNvbnYxMC5zaGFwZVswXSwgMSwgMCkuYXMxRCgpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHtuYW1lZEFjdGl2YXRpb25zLCBsb2dpdHM6IGF2Z3Bvb2wxMH07XG4gIH1cblxuICBwcml2YXRlIGZpcmVNb2R1bGUoaW5wdXQ6IEFycmF5M0QsIGZpcmVJZDogbnVtYmVyKSB7XG4gICAgY29uc3QgeTEgPSB0aGlzLm1hdGguY29udjJkKFxuICAgICAgICBpbnB1dCwgdGhpcy52YXJpYWJsZXNbJ2ZpcmUnICsgZmlyZUlkICsgJy9zcXVlZXplMXgxX1c6MCddIGFzIEFycmF5NEQsXG4gICAgICAgIHRoaXMudmFyaWFibGVzWydmaXJlJyArIGZpcmVJZCArICcvc3F1ZWV6ZTF4MV9iOjAnXSBhcyBBcnJheTFELCAxLCAwKTtcbiAgICBjb25zdCB5MiA9IHRoaXMubWF0aC5yZWx1KHkxKTtcbiAgICBjb25zdCBsZWZ0MSA9IHRoaXMubWF0aC5jb252MmQoXG4gICAgICAgIHkyLCB0aGlzLnZhcmlhYmxlc1snZmlyZScgKyBmaXJlSWQgKyAnL2V4cGFuZDF4MV9XOjAnXSBhcyBBcnJheTRELFxuICAgICAgICB0aGlzLnZhcmlhYmxlc1snZmlyZScgKyBmaXJlSWQgKyAnL2V4cGFuZDF4MV9iOjAnXSBhcyBBcnJheTFELCAxLCAwKTtcbiAgICBjb25zdCBsZWZ0MiA9IHRoaXMubWF0aC5yZWx1KGxlZnQxKTtcblxuICAgIGNvbnN0IHJpZ2h0MSA9IHRoaXMubWF0aC5jb252MmQoXG4gICAgICAgIHkyLCB0aGlzLnZhcmlhYmxlc1snZmlyZScgKyBmaXJlSWQgKyAnL2V4cGFuZDN4M19XOjAnXSBhcyBBcnJheTRELFxuICAgICAgICB0aGlzLnZhcmlhYmxlc1snZmlyZScgKyBmaXJlSWQgKyAnL2V4cGFuZDN4M19iOjAnXSBhcyBBcnJheTFELCAxLCAxKTtcbiAgICBjb25zdCByaWdodDIgPSB0aGlzLm1hdGgucmVsdShyaWdodDEpO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0aC5jb25jYXQzRChsZWZ0MiwgcmlnaHQyLCAyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHRvcEsgY2xhc3NlcyBmb3IgcHJlLXNvZnRtYXggbG9naXRzLiBSZXR1cm5zIGEgbWFwIG9mIGNsYXNzTmFtZVxuICAgKiB0byBzb2Z0bWF4IG5vcm1hbGl6ZWQgcHJvYmFiaWxpdHkuXG4gICAqXG4gICAqIEBwYXJhbSBsb2dpdHMgUHJlLXNvZnRtYXggbG9naXRzIGFycmF5LlxuICAgKiBAcGFyYW0gdG9wSyBIb3cgbWFueSB0b3AgY2xhc3NlcyB0byByZXR1cm4uXG4gICAqL1xuICBnZXRUb3BLQ2xhc3Nlcyhsb2dpdHM6IEFycmF5MUQsIHRvcEs6IG51bWJlcik6IHtbY2xhc3NOYW1lOiBzdHJpbmddOiBudW1iZXJ9IHtcbiAgICBjb25zdCBwcmVkaWN0aW9ucyA9IHRoaXMubWF0aC5zb2Z0bWF4KGxvZ2l0cyk7XG4gICAgY29uc3QgdG9wayA9IG5ldyBOREFycmF5TWF0aENQVSgpLnRvcEsocHJlZGljdGlvbnMsIHRvcEspO1xuICAgIGNvbnN0IHRvcGtJbmRpY2VzID0gdG9way5pbmRpY2VzLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHRvcGtWYWx1ZXMgPSB0b3BrLnZhbHVlcy5nZXRWYWx1ZXMoKTtcblxuICAgIGNvbnN0IHRvcENsYXNzZXNUb1Byb2JhYmlsaXR5OiB7W2NsYXNzTmFtZTogc3RyaW5nXTogbnVtYmVyfSA9IHt9O1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdG9wa0luZGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHRvcENsYXNzZXNUb1Byb2JhYmlsaXR5W2ltYWdlbmV0X2NsYXNzZXNcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuSU1BR0VORVRfQ0xBU1NFU1t0b3BrSW5kaWNlc1tpXV1dID1cbiAgICAgICAgICB0b3BrVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gdG9wQ2xhc3Nlc1RvUHJvYmFiaWxpdHk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuLyoqXG4gKiBAZmlsZW92ZXJ2aWV3XG4gKlxuICogRGVmaW5lcyBhbiBpbnRlcmZhY2UgZm9yIGNyZWF0aW5nIFBvbHltZXIgZWxlbWVudHMgaW4gVHlwZXNjcmlwdCB3aXRoIHRoZVxuICogY29ycmVjdCB0eXBpbmdzLiBBIFBvbHltZXIgZWxlbWVudCBzaG91bGQgYmUgZGVmaW5lZCBsaWtlIHRoaXM6XG4gKlxuICogYGBgXG4gKiBsZXQgTXlFbGVtZW50UG9seW1lciA9IFBvbHltZXJFbGVtZW50KHtcbiAqICAgaXM6ICdteS1wb2x5bWVyLWVsZW1lbnQnLFxuICogICBwcm9wZXJ0aWVzOiB7XG4gKiAgICAgZm9vOiBzdHJpbmcsXG4gKiAgICAgYmFyOiBBcnJheVxuICogICB9XG4gKiB9KTtcbiAqXG4gKiBjbGFzcyBNeUVsZW1lbnQgZXh0ZW5kcyBNeUVsZW1lbnRQb2x5bWVyIHtcbiAqICAgZm9vOiBzdHJpbmc7XG4gKiAgIGJhcjogbnVtYmVyW107XG4gKlxuICogICByZWFkeSgpIHtcbiAqICAgICBjb25zb2xlLmxvZygnTXlFbGVtZW50IGluaXRpYWxpemVkIScpO1xuICogICB9XG4gKiB9XG4gKlxuICogZG9jdW1lbnQucmVnaXN0ZXJFbGVtZW50KE15RWxlbWVudC5wcm90b3R5cGUuaXMsIE15RWxlbWVudCk7XG4gKiBgYGBcbiAqL1xuXG5leHBvcnQgdHlwZSBTcGVjID0ge1xuICBpczogc3RyaW5nOyBwcm9wZXJ0aWVzOiB7XG4gICAgW2tleTogc3RyaW5nXTogKEZ1bmN0aW9ufHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHR5cGU6IEZ1bmN0aW9uLCB2YWx1ZT86IGFueTtcbiAgICAgIHJlZmxlY3RUb0F0dHJpYnV0ZT86IGJvb2xlYW47XG4gICAgICByZWFkb25seT86IGJvb2xlYW47XG4gICAgICBub3RpZnk/OiBib29sZWFuO1xuICAgICAgY29tcHV0ZWQ/OiBzdHJpbmc7XG4gICAgICBvYnNlcnZlcj86IHN0cmluZztcbiAgICB9KVxuICB9O1xuICBvYnNlcnZlcnM/OiBzdHJpbmdbXTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBQb2x5bWVyRWxlbWVudChzcGVjOiBTcGVjKSB7XG4gIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgcmV0dXJuIFBvbHltZXIuQ2xhc3Moc3BlYyBhcyBhbnkpIGFzIHtuZXcgKCk6IFBvbHltZXJIVE1MRWxlbWVudH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUG9seW1lckhUTUxFbGVtZW50IGV4dGVuZHMgSFRNTEVsZW1lbnQsIHBvbHltZXIuQmFzZSB7fVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2hlY2twb2ludFZhcmlhYmxlIHtcbiAgZmlsZW5hbWU6IHN0cmluZztcbiAgc2hhcGU6IG51bWJlcltdO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IHR5cGUgQ2hlY2twb2ludE1hbmlmZXN0ID0ge1xuICBbdmFyTmFtZTogc3RyaW5nXTogQ2hlY2twb2ludFZhcmlhYmxlXG59O1xuXG5jb25zdCBNQU5JRkVTVF9GSUxFID0gJ21hbmlmZXN0Lmpzb24nO1xuXG5leHBvcnQgY2xhc3MgQ2hlY2twb2ludExvYWRlciB7XG4gIHByaXZhdGUgY2hlY2twb2ludE1hbmlmZXN0OiBDaGVja3BvaW50TWFuaWZlc3Q7XG4gIHByaXZhdGUgdmFyaWFibGVzOiB7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgdXJsUGF0aDogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMudXJsUGF0aC5jaGFyQXQodGhpcy51cmxQYXRoLmxlbmd0aCAtIDEpICE9PSAnLycpIHtcbiAgICAgIHRoaXMudXJsUGF0aCArPSAnLyc7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBsb2FkTWFuaWZlc3QoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgeGhyLm9wZW4oJ0dFVCcsIHRoaXMudXJsUGF0aCArIE1BTklGRVNUX0ZJTEUpO1xuXG4gICAgICB4aHIub25sb2FkID0gKCkgPT4ge1xuICAgICAgICB0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCA9IEpTT04ucGFyc2UoeGhyLnJlc3BvbnNlVGV4dCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH07XG4gICAgICB4aHIub25lcnJvciA9IChlcnJvcikgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgJHtNQU5JRkVTVF9GSUxFfSBub3QgZm91bmQgYXQgJHt0aGlzLnVybFBhdGh9LiBgICsgZXJyb3IpO1xuICAgICAgfTtcbiAgICAgIHhoci5zZW5kKCk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRDaGVja3BvaW50TWFuaWZlc3QoKTogUHJvbWlzZTxDaGVja3BvaW50TWFuaWZlc3Q+IHtcbiAgICBpZiAodGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPENoZWNrcG9pbnRNYW5pZmVzdD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLmxvYWRNYW5pZmVzdCgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHJlc29sdmUodGhpcy5jaGVja3BvaW50TWFuaWZlc3QpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFByb21pc2U8Q2hlY2twb2ludE1hbmlmZXN0PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICByZXNvbHZlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldEFsbFZhcmlhYmxlcygpOiBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+IHtcbiAgICBpZiAodGhpcy52YXJpYWJsZXMgIT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgcmVzb2x2ZSh0aGlzLnZhcmlhYmxlcyk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2U8e1t2YXJOYW1lOiBzdHJpbmddOiBOREFycmF5fT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy5nZXRDaGVja3BvaW50TWFuaWZlc3QoKS50aGVuKFxuICAgICAgICAgIChjaGVja3BvaW50RGVmaW5pdGlvbjogQ2hlY2twb2ludE1hbmlmZXN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB2YXJpYWJsZU5hbWVzID0gT2JqZWN0LmtleXModGhpcy5jaGVja3BvaW50TWFuaWZlc3QpO1xuXG4gICAgICAgICAgICBjb25zdCB2YXJpYWJsZVByb21pc2VzOiBBcnJheTxQcm9taXNlPE5EQXJyYXk+PiA9IFtdO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YXJpYWJsZU5hbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgIHZhcmlhYmxlUHJvbWlzZXMucHVzaCh0aGlzLmdldFZhcmlhYmxlKHZhcmlhYmxlTmFtZXNbaV0pKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgUHJvbWlzZS5hbGwodmFyaWFibGVQcm9taXNlcykudGhlbih2YXJpYWJsZXMgPT4ge1xuICAgICAgICAgICAgICB0aGlzLnZhcmlhYmxlcyA9IHt9O1xuICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhcmlhYmxlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHRoaXMudmFyaWFibGVzW3ZhcmlhYmxlTmFtZXNbaV1dID0gdmFyaWFibGVzW2ldO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJlc29sdmUodGhpcy52YXJpYWJsZXMpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRWYXJpYWJsZSh2YXJOYW1lOiBzdHJpbmcpOiBQcm9taXNlPE5EQXJyYXk+IHtcbiAgICBpZiAoISh2YXJOYW1lIGluIHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgbG9hZCBub24tZXhpc3RhbnQgdmFyaWFibGUgJyArIHZhck5hbWUpO1xuICAgIH1cblxuICAgIGNvbnN0IHZhcmlhYmxlUmVxdWVzdFByb21pc2VNZXRob2QgPVxuICAgICAgICAocmVzb2x2ZTogKG5kYXJyYXk6IE5EQXJyYXkpID0+IHZvaWQsIHJlamVjdDogKCkgPT4gdm9pZCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICAgIHhoci5yZXNwb25zZVR5cGUgPSAnYXJyYXlidWZmZXInO1xuICAgICAgICAgIGNvbnN0IGZuYW1lID0gdGhpcy5jaGVja3BvaW50TWFuaWZlc3RbdmFyTmFtZV0uZmlsZW5hbWU7XG4gICAgICAgICAgeGhyLm9wZW4oJ0dFVCcsIHRoaXMudXJsUGF0aCArIGZuYW1lKTtcblxuICAgICAgICAgIHhoci5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHhoci5yZXNwb25zZSk7XG4gICAgICAgICAgICBjb25zdCBuZGFycmF5ID1cbiAgICAgICAgICAgICAgICBOREFycmF5Lm1ha2UodGhpcy5jaGVja3BvaW50TWFuaWZlc3RbdmFyTmFtZV0uc2hhcGUsIHt2YWx1ZXN9KTtcbiAgICAgICAgICAgIHJlc29sdmUobmRhcnJheSk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICB4aHIub25lcnJvciA9IChlcnJvcikgPT4ge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICdDb3VsZCBub3QgZmV0Y2ggdmFyaWFibGUgJyArIHZhck5hbWUgKyAnOiAnICsgZXJyb3IpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgeGhyLnNlbmQoKTtcbiAgICAgICAgfTtcblxuICAgIGlmICh0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2U8TkRBcnJheT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLmxvYWRNYW5pZmVzdCgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIG5ldyBQcm9taXNlPE5EQXJyYXk+KHZhcmlhYmxlUmVxdWVzdFByb21pc2VNZXRob2QpLnRoZW4ocmVzb2x2ZSk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZTxOREFycmF5Pih2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuY29uc3QgU1RBVFNfU0FNUExFX1BFUkNFTlRBR0UgPSAwLjE7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRGF0YVN0YXRzIHtcbiAgZXhhbXBsZUNvdW50OiBudW1iZXI7XG4gIGlucHV0TWluOiBudW1iZXI7XG4gIGlucHV0TWF4OiBudW1iZXI7XG4gIHNoYXBlOiBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIE5vcm1hbGl6YXRpb25JbmZvIHtcbiAgaXNOb3JtYWxpemVkOiBib29sZWFuO1xuICAvLyBCb3VuZHMgb2YgdGhlIG5vcm1hbGl6YXRpb24gaWYgbm9ybWFsaXplZC5cbiAgbG93ZXJCb3VuZD86IG51bWJlcjtcbiAgdXBwZXJCb3VuZD86IG51bWJlcjtcbiAgLy8gTWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgZm9yIGVhY2ggZGltZW5zaW9uIG9mIHRoZSBvcmlnaW5hbCBkYXRhLiBUaGVzZVxuICAvLyBhcmUgdGhlIHNhbWUgc2l6ZSBhcyBhbiBpbnB1dCBleGFtcGxlLiBUaGVzZSBhcmUgY29tcHV0ZWQgbGF6aWx5LCBvbmx5IGlmXG4gIC8vIG5vcm1hbGl6YXRpb24gaXMgcmVxdWVzdGVkLiBJZiB0aGUgZGF0YSBpcyB1bi1ub3JtYWxpemVkLCB0aGVzZSBhcmUga2VwdFxuICAvLyBhcm91bmQgc28gdGhleSBkb24ndCBoYXZlIHRvIGJlIHJlY29tcHV0ZWQuXG4gIG1pblZhbHVlczogRmxvYXQzMkFycmF5O1xuICBtYXhWYWx1ZXM6IEZsb2F0MzJBcnJheTtcbn1cblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEluTWVtb3J5RGF0YXNldCB7XG4gIHByb3RlY3RlZCBkYXRhc2V0OiBOREFycmF5W11bXXxudWxsO1xuXG4gIC8vIENvbnRhaW5zIGluZm9ybWF0aW9uIG5lY2Vzc2FyeSBmb3IgcmVjb25zdHJ1Y3Rpb24gb2YgdGhlIG9yaWdpbmFsIGRhdGFcbiAgLy8gYWZ0ZXIgbm9ybWFsaXphdGlvbi5cbiAgcHJpdmF0ZSBub3JtYWxpemF0aW9uSW5mbzoge1tkYXRhSW5kZXg6IG51bWJlcl06IE5vcm1hbGl6YXRpb25JbmZvfTtcblxuICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgZGF0YVNoYXBlczogbnVtYmVyW11bXSkge1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm8gPSB7fTtcbiAgfVxuXG4gIGdldERhdGFTaGFwZShkYXRhSW5kZXg6IG51bWJlcik6IG51bWJlcltdIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhU2hhcGVzW2RhdGFJbmRleF07XG4gIH1cblxuICBhYnN0cmFjdCBmZXRjaERhdGEoKTogUHJvbWlzZTx2b2lkPjtcblxuICBnZXREYXRhKCk6IE5EQXJyYXlbXVtdfG51bGwge1xuICAgIHJldHVybiB0aGlzLmRhdGFzZXQ7XG4gIH1cblxuICBnZXRTdGF0cygpOiBEYXRhU3RhdHNbXSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5kYXRhc2V0Lm1hcChkID0+IHRoaXMuZ2V0U3RhdHNGb3JEYXRhKGQpKTtcbiAgfVxuXG4gIC8vIENvbXB1dGVzIHN0YXRzIGFjcm9zcyBhIHNhbXBsZWQgcG9ydGlvbiBvZiB0aGUgZGF0YS5cbiAgcHJpdmF0ZSBnZXRTdGF0c0ZvckRhdGEoZGF0YTogTkRBcnJheVtdKTogRGF0YVN0YXRzIHtcbiAgICBsZXQgaW5wdXRNaW4gPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IGlucHV0TWF4ID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuXG4gICAgbGV0IGV4YW1wbGVJbmRpY2VzID0gZGF0YS5tYXAoKGV4YW1wbGUsIGkpID0+IGkpO1xuICAgIHV0aWwuc2h1ZmZsZShleGFtcGxlSW5kaWNlcyk7XG4gICAgZXhhbXBsZUluZGljZXMgPVxuICAgICAgICBleGFtcGxlSW5kaWNlcy5zbGljZShleGFtcGxlSW5kaWNlcy5sZW5ndGggKiBTVEFUU19TQU1QTEVfUEVSQ0VOVEFHRSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGV4YW1wbGVJbmRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGRhdGFbZXhhbXBsZUluZGljZXNbaV1dLmdldFZhbHVlcygpO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dFZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgICAgICBpbnB1dE1pbiA9IE1hdGgubWluKGlucHV0TWluLCBpbnB1dFZhbHVlc1tqXSk7XG4gICAgICAgIGlucHV0TWF4ID0gTWF0aC5tYXgoaW5wdXRNYXgsIGlucHV0VmFsdWVzW2pdKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgaW5wdXRNaW4sXG4gICAgICBpbnB1dE1heCxcbiAgICAgIGV4YW1wbGVDb3VudDogZGF0YS5sZW5ndGgsXG4gICAgICBzaGFwZTogZGF0YVswXS5zaGFwZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBleGFtcGxlcyBOREFycmF5cyB0byBiZSBub3JtYWxpemVkLlxuICAgKiBAcGFyYW0gY3VyTG93ZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyB0aGUgbWluaW11bSB2YWx1ZSBmb3IgZWFjaFxuICAgKiBkaW1lbnNpb24gb3IgYSBmaXhlZCBtaW5pbXVtIHZhbHVlLlxuICAgKiBAcGFyYW0gY3VyVXBwZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyB0aGUgbWF4aW11bSB2YWx1ZSBmb3IgZWFjaFxuICAgKiBkaW1lbnNpb24gb3IgYSBmaXhlZCBtYXhpbXVtIHZhbHVlLlxuICAgKiBAcGFyYW0gbmV3TG93ZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyBuZXcgbWluaW11bSB2YWx1ZXMgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uLCBvciBhIGZpeGVkIG1pbnVtdW0gdmFsdWUgdG8gbm9ybWFsaXplIHRoZSBkYXRhIHRvLlxuICAgKiBAcGFyYW0gbmV3VXBwZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyBuZXcgbWF4aW11bSB2YWx1ZXMgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uLCBvciBhIGZpeGVkIG1heGltdW0gdmFsdWUgdG8gbm9ybWFsaXplIHRoZSBkYXRhIHRvLlxuICAgKi9cbiAgcHJpdmF0ZSBub3JtYWxpemVFeGFtcGxlc1RvUmFuZ2UoXG4gICAgICBleGFtcGxlczogTkRBcnJheVtdLCBjdXJMb3dlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcixcbiAgICAgIGN1clVwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyLCBuZXdMb3dlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcixcbiAgICAgIG5ld1VwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyKTogTkRBcnJheVtdIHtcbiAgICBjb25zdCBjdXJCb3VuZHNJc1BlckRpbWVuc2lvbiA9XG4gICAgICAgIChjdXJVcHBlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSAmJlxuICAgICAgICAgY3VyTG93ZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpO1xuICAgIGNvbnN0IG5ld0JvdW5kc0lzUGVyRGltZW5zaW9uID1cbiAgICAgICAgKG5ld0xvd2VyQm91bmRzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5ICYmXG4gICAgICAgICBuZXdVcHBlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSk7XG5cbiAgICBjb25zdCBpbnB1dFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoZXhhbXBsZXNbMF0uc2hhcGUpO1xuICAgIGNvbnN0IG5ld0V4YW1wbGVzOiBOREFycmF5W10gPSBbXTtcblxuICAgIGV4YW1wbGVzLmZvckVhY2goZXhhbXBsZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGV4YW1wbGUuZ2V0VmFsdWVzKCk7XG4gICAgICBjb25zdCBub3JtYWxpemVkVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShpbnB1dFNpemUpO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dFNpemU7IGorKykge1xuICAgICAgICBjb25zdCBjdXJMb3dlckJvdW5kID0gY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKGN1ckxvd2VyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgY3VyTG93ZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBjdXJVcHBlckJvdW5kID0gY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKGN1clVwcGVyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgY3VyVXBwZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBjdXJSYW5nZSA9IGN1clVwcGVyQm91bmQgLSBjdXJMb3dlckJvdW5kO1xuXG4gICAgICAgIGNvbnN0IG5ld0xvd2VyQm91bmQgPSBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA/XG4gICAgICAgICAgICAobmV3TG93ZXJCb3VuZHMgYXMgRmxvYXQzMkFycmF5KVtqXSA6XG4gICAgICAgICAgICBuZXdMb3dlckJvdW5kcyBhcyBudW1iZXI7XG4gICAgICAgIGNvbnN0IG5ld1VwcGVyQm91bmQgPSBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA/XG4gICAgICAgICAgICAobmV3VXBwZXJCb3VuZHMgYXMgRmxvYXQzMkFycmF5KVtqXSA6XG4gICAgICAgICAgICBuZXdVcHBlckJvdW5kcyBhcyBudW1iZXI7XG4gICAgICAgIGNvbnN0IG5ld1JhbmdlID0gbmV3VXBwZXJCb3VuZCAtIG5ld0xvd2VyQm91bmQ7XG5cbiAgICAgICAgaWYgKGN1clJhbmdlID09PSAwKSB7XG4gICAgICAgICAgbm9ybWFsaXplZFZhbHVlc1tqXSA9IG5ld0xvd2VyQm91bmQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbm9ybWFsaXplZFZhbHVlc1tqXSA9IG5ld0xvd2VyQm91bmQgK1xuICAgICAgICAgICAgICBuZXdSYW5nZSAqIChpbnB1dFZhbHVlc1tqXSAtIGN1ckxvd2VyQm91bmQpIC8gY3VyUmFuZ2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIG5ld0V4YW1wbGVzLnB1c2goTkRBcnJheS5tYWtlKGV4YW1wbGUuc2hhcGUsIHt2YWx1ZXM6IG5vcm1hbGl6ZWRWYWx1ZXN9KSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ld0V4YW1wbGVzO1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wdXRlQm91bmRzKGRhdGFJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdWzBdLnNoYXBlKTtcblxuICAgIC8vIENvbXB1dGUgbWluIGFuZCBtYXggdmFsdWVzIGZvciBldmVyeSBkaW1lbnNpb24uXG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdID0ge1xuICAgICAgaXNOb3JtYWxpemVkOiBmYWxzZSxcbiAgICAgIG1pblZhbHVlczogbmV3IEZsb2F0MzJBcnJheShzaXplKSxcbiAgICAgIG1heFZhbHVlczogbmV3IEZsb2F0MzJBcnJheShzaXplKVxuICAgIH07XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNpemU7IGkrKykge1xuICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlc1tpXSA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNbaV0gPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgfVxuXG4gICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0uZm9yRWFjaChleGFtcGxlID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VmFsdWVzID0gZXhhbXBsZS5nZXRWYWx1ZXMoKTtcbiAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgc2l6ZTsgaysrKSB7XG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNba10gPSBNYXRoLm1pbihcbiAgICAgICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNba10sIGlucHV0VmFsdWVzW2tdKTtcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlc1trXSA9IE1hdGgubWF4KFxuICAgICAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlc1trXSwgaW5wdXRWYWx1ZXNba10pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbm9ybWFsaXplV2l0aGluQm91bmRzKFxuICAgICAgZGF0YUluZGV4OiBudW1iZXIsIGxvd2VyQm91bmQ6IG51bWJlciwgdXBwZXJCb3VuZDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG4gICAgaWYgKGRhdGFJbmRleCA+PSB0aGlzLmRhdGFzZXQubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2RhdGFJbmRleCBvdXQgb2YgYm91bmRzLicpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0gPT0gbnVsbCkge1xuICAgICAgdGhpcy5jb21wdXRlQm91bmRzKGRhdGFJbmRleCk7XG4gICAgfVxuXG4gICAgLy8gY3VyTG93ZXIvVXBwZXJCb3VuZHMgb2YgdGhlIGN1cnJlbnQgZGF0YSBzZXQgY2FuIGVpdGhlciBiZSBmaXhlZCBudW1iZXJzXG4gICAgLy8gaWYgdGhlIGRhdGEgaGFzIGFscmVhZHkgYmVlbiBub3JtYWxpemVkLCBvciBjdXJMb3dlci9VcHBlciBmb3IgZWFjaFxuICAgIC8vIGRpbWVuc2lvbiBpZiBpdCBoYXNuJ3QgYmVlbiBub3JtYWxpemVkIHlldC5cbiAgICBsZXQgY3VyTG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXI7XG4gICAgbGV0IGN1clVwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyO1xuXG4gICAgaWYgKHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQpIHtcbiAgICAgIGN1ckxvd2VyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhO1xuICAgICAgY3VyVXBwZXJCb3VuZHMgPSB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCE7XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1ckxvd2VyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlcztcbiAgICAgIGN1clVwcGVyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlcztcbiAgICB9XG5cbiAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSA9IHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSwgY3VyTG93ZXJCb3VuZHMsIGN1clVwcGVyQm91bmRzLCBsb3dlckJvdW5kLFxuICAgICAgICB1cHBlckJvdW5kKTtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkID0gdHJ1ZTtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubG93ZXJCb3VuZCA9IGxvd2VyQm91bmQ7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQgPSB1cHBlckJvdW5kO1xuICB9XG5cbiAgcHJpdmF0ZSBpc05vcm1hbGl6ZWQoZGF0YUluZGV4OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5ub3JtYWxpemF0aW9uSW5mbyAhPSBudWxsICYmXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQ7XG4gIH1cblxuICByZW1vdmVOb3JtYWxpemF0aW9uKGRhdGFJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RyYWluaW5nIG9yIHRlc3QgZGF0YSBpcyBudWxsLicpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc05vcm1hbGl6ZWQoZGF0YUluZGV4KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdID0gdGhpcy5ub3JtYWxpemVFeGFtcGxlc1RvUmFuZ2UoXG4gICAgICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdLCB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubG93ZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS51cHBlckJvdW5kISxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlcyxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlcyk7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmlzTm9ybWFsaXplZCA9IGZhbHNlO1xuICB9XG5cbiAgdW5ub3JtYWxpemVFeGFtcGxlcyhleGFtcGxlczogTkRBcnJheVtdLCBkYXRhSW5kZXg6IG51bWJlcik6IE5EQXJyYXlbXSB7XG4gICAgaWYgKCF0aGlzLmlzTm9ybWFsaXplZChkYXRhSW5kZXgpKSB7XG4gICAgICByZXR1cm4gZXhhbXBsZXM7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICBleGFtcGxlcywgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXMsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXMpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5kYXRhc2V0ID09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZGF0YXNldC5sZW5ndGg7IGkrKykge1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmRhdGFzZXRbaV0ubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgdGhpcy5kYXRhc2V0W2ldW2pdLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5kYXRhc2V0ID0gW107XG4gIH1cbn1cblxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoTGF5ZXJzfSBmcm9tICcuL2dyYXBoX2xheWVycyc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vbWF0aC9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogR3JhcGggaXMgdGhlIHByaW1hcnkgY29udGFpbmVyIHN0cnVjdHVyZSBmb3IgbGVhcm4uanMgb3BlcmF0aW9ucy4gR3JhcGhcbiAqIGhvbGRzIHRoZSB0b3BvbG9neSBvZiBvcGVyYXRpb24gbm9kZXMgYW5kIHRoZSBjb25uZWN0aXZpdHkgYmV0d2VlbiB0aGVtLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGgge1xuICBsYXllcnM6IEdyYXBoTGF5ZXJzO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMubGF5ZXJzID0gbmV3IEdyYXBoTGF5ZXJzKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuYW1lZCB2YXJpYWJsZS4gVmFyaWFibGVzIGFyZSB0ZW5zb3JzIHRoYXQgbWFpbnRhaW4gc3RhdGUgYWNyb3NzXG4gICAqIHNlc3Npb24gY2FsbHMgYW5kIHdob3NlIHZhbHVlcyBhcmUgYWRqdXN0ZWQgZHVyaW5nIGJhY2twcm9wYWdhdGlvblxuICAgKiB0cmFpbmluZy5cbiAgICogQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhpcyB2YXJpYWJsZS5cbiAgICogQHBhcmFtIGRhdGEgVGhlIE5EQXJyYXkgdG8gYXNzb2NpYXRlIHdpdGggdGhpcyB2YXJpYWJsZSB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHZhcmlhYmxlLlxuICAgKi9cbiAgdmFyaWFibGUobmFtZTogc3RyaW5nLCBkYXRhOiBOREFycmF5KTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBWYXJpYWJsZU5vZGUodGhpcywgbmFtZSwgZGF0YSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluc2VydHMgYSBwbGFjZWhvbGRlciBmb3IgYSB0ZW5zb3IgdGhhdCB3aWxsIGJlIGFsd2F5cyBmZWQuIFBsYWNlaG9sZGVyc1xuICAgKiBhcmUgaW5wdXQgdGVuc29ycyB3aG9zZSB2YWx1ZXMgYXJlIHByb3ZpZGVkIGJ5IHRoZSBjbGllbnQgdmlhIGZlZWRcbiAgICogZGljdGlvbmFyaWVzLiBQbGFjZWhvbGRlcnMgYXJlIG5vdCB1cGRhdGVkIGFzIHBhcnQgb2YgdHJhaW5pbmc7IHRoZXkgYXJlXG4gICAqIG9ubHkgdXNlZCBhcyBpbW11dGFibGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgcGxhY2Vob2xkZXIuXG4gICAqIEBwYXJhbSBzaGFwZSBUaGUgc2hhcGUgb2YgdGhlIHBsYWNlaG9sZGVyIHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgcGxhY2Vob2xkZXIuXG4gICAqL1xuICBwbGFjZWhvbGRlcihuYW1lOiBzdHJpbmcsIHNoYXBlOiBudW1iZXJbXSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUGxhY2Vob2xkZXJOb2RlKHRoaXMsIG5hbWUsIHNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uc3RhbnQgdmFsdWUgdGhhdCBwZXJzaXN0cyBhY3Jvc3Mgc2Vzc2lvbiBjYWxscy5cbiAgICogQHBhcmFtIHZhbHVlIFRoZSB2YWx1ZSB0byByZXR1cm4uXG4gICAqIEByZXR1cm4gQSBub2RlIG91dHB1dGluZyB0aGUgY29uc3RhbnQgdmFsdWUuXG4gICAqL1xuICBjb25zdGFudCh2YWx1ZTogQXJyYXlEYXRhKTogVGVuc29yIHtcbiAgICBsZXQgZmluYWxWYWx1ZTogTkRBcnJheTtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJykge1xuICAgICAgZmluYWxWYWx1ZSA9IFNjYWxhci5uZXcodmFsdWUpO1xuICAgIH0gZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICBmaW5hbFZhbHVlID0gdmFsdWU7XG4gICAgfSBlbHNlIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICBjb25zdCB2YWxzID0gbmV3IEZsb2F0MzJBcnJheSh1dGlsLmZsYXR0ZW4odmFsdWUpKTtcbiAgICAgIGZpbmFsVmFsdWUgPSBOREFycmF5Lm1ha2UodXRpbC5pbmZlclNoYXBlKHZhbHVlKSwge3ZhbHVlczogdmFsc30pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3VuaW1wbGVtZW50ZWQgY29uc3RhbnQgdHlwZS4nKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29uc3RhbnROb2RlKHRoaXMsIGZpbmFsVmFsdWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNoYXBlIHRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gYmUgcmVzaGFwZWQuXG4gICAqIEBwYXJhbSBzaGFwZSBUaGUgc2hhcGUgb2YgdGhlIG91dHB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHJlc2hhcGUgb3BlcmF0aW9uLlxuICAgKi9cbiAgcmVzaGFwZSh4OiBUZW5zb3IsIHNoYXBlOiBudW1iZXJbXSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChcbiAgICAgICAgbmV3IFJlc2hhcGVOb2RlKHRoaXMsICdSZXNoYXBlJywgeCwgc2hhcGUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIGZ1c2VkIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0d28gdGVuc29ycy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci4gU2FtZSBzaGFwZSBhcyB0MS5cbiAgICogQHBhcmFtIGMxIENvZWZmaWNpZW50IG9mIHQxLiBNdXN0IGJlIHNpemUgMS5cbiAgICogQHBhcmFtIGMyIENvZWZmaWNpZW50IG9mIHQyLiBNdXN0IGJlIHNpemUgMS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyBjMSp0MStjMip0Mi5cbiAgICovXG4gIGZ1c2VkTGluZWFyQ29tYmluYXRpb24oeDE6IFRlbnNvciwgeDI6IFRlbnNvciwgYzE6IFRlbnNvciwgYzI6IFRlbnNvcik6XG4gICAgICBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSh0aGlzLCB4MSwgeDIsIGMxLCBjMikpO1xuICB9XG5cblxuICAvKipcbiAgICogQWRkcyB0d28gdGVuc29ycyAoZWxlbWVudHdpc2UpLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpcyBzY2FsYXIuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdDErdDIuXG4gICAqL1xuICBhZGQoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQWRkTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJ0cmFjdHMgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxLXQyLlxuICAgKi9cbiAgc3VidHJhY3QoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgU3VidHJhY3ROb2RlKHRoaXMsIHgxLCB4MikpO1xuICB9XG5cbiAgLyoqXG4gICAqIE11bHRpcGx5IHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MSp0Mi5cbiAgICovXG4gIG11bHRpcGx5KHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IE11bHRpcGx5Tm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXZpZGUgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxIC8gdDIuXG4gICAqL1xuICBkaXZpZGUoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgRGl2aWRlTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc3VtIG9mIGVsZW1lbnRzIGluIHRoZSB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqL1xuICByZWR1Y2VTdW0oeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBSZWR1Y2VTdW1Ob2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25jYXRzIHR3byAzRCB0ZW5zb3JzIGFsb25nIGEgZ2l2ZW4gYXhpcy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyBjb25jYXQgb2YgdHdvIHRlbnNvcnMgYWxvbmcgYXhpcy5cbiAgICovXG4gIGNvbmNhdDNkKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IsIGF4aXM6IG51bWJlcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29uY2F0M0ROb2RlKHRoaXMsIHgxLCB4MiwgYXhpcykpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBkb3QgcHJvZHVjdCBiZXR3ZWVuIHR3byBtYXRyaWNlcy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgZG90IHByb2R1Y3Qgb2YgeDEgYW5kIHgyLlxuICAgKi9cbiAgbWF0bXVsKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IE1hdE11bE5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSAyRCBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdyBUaGUgd2VpZ2h0IHRlbnNvciB1c2VkIGJ5IHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBiIFRoZSBiaWFzIHRlbnNvciB1c2VkIGJ5IHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBmaWVsZFNpemUgVGhlIHNpemUgb2YgdGhlIGNvbnZvbHV0aW9uYWwga2VybmVsLlxuICAgKiBAcGFyYW0gb3V0cHV0RGVwdGggVGhlIG91dHB1dCBkZXB0aCBvZiB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHplcm9QYWQgVGhlIGFtb3VudCBvZiB6ZXJvIHBhZGRpbmcgb24gYWxsIHNpZGVzIG9mIHRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICovXG4gIGNvbnYyZChcbiAgICAgIHg6IFRlbnNvciwgdzogVGVuc29yLCBiOiBUZW5zb3IsIGZpZWxkU2l6ZTogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgICAgc3RyaWRlID0gMSwgemVyb1BhZD86IG51bWJlcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29udm9sdXRpb24yRE5vZGUoXG4gICAgICAgIHRoaXMsIHgsIHcsIGIsIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgMkQgbWF4IHBvb2wgb2YgeC5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgbWF4IHBvb2wgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBjb252b2x1dGlvbmFsIGtlcm5lbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSBhbW91bnQgb2YgemVybyBwYWRkaW5nIG9uIGFsbCBzaWRlcyBvZiB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBtYXggcG9vbCBvcGVyYXRpb24uXG4gICAqL1xuICBtYXhQb29sKHg6IFRlbnNvciwgZmllbGRTaXplOiBudW1iZXIsIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBNYXhQb29sTm9kZSh0aGlzLCB4LCBmaWVsZFNpemUsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGV4cG9uZW50aWFsIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBleHAuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGUgXiB4IG9wZXJhdGlvbi5cbiAgICovXG4gIGV4cCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEV4cE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGxvZyBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgbG9nLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBsbih4KSBvcGVyYXRpb24uXG4gICAqL1xuICBsb2coeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBMb2dOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBSZUxVIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBSZUxVLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBSZUxVIG9wZXJhdGlvbi5cbiAgICovXG4gIHJlbHUoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBSZUxVTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgVGFuSCBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgVGFuSC5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgVGFuSCBvcGVyYXRpb24uXG4gICAqL1xuICB0YW5oKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgVGFuSE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIFNpZ21vaWQgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIHNpZ21vaWQuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHNpZ21vaWQgb3BlcmF0aW9uLlxuICAgKi9cbiAgc2lnbW9pZCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNpZ21vaWROb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzcXVhcmUgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIHNxdWFyZS5cbiAgICovXG4gIHNxdWFyZSh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNxdWFyZU5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHNvZnRtYXggcHJvYmFiaWxpdGllcyBmcm9tIGxvZ2l0cy5cbiAgICpcbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGxvZ2l0cy5cbiAgICogQHJldHVybiBUaGUgc29mdG1heCBwcm9iYWJpbGl0aWVzLlxuICAgKi9cbiAgc29mdG1heCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNvZnRtYXhOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byBjbGFzc2lmeS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgc29mdG1heENyb3NzRW50cm9weUNvc3QoeDogVGVuc29yLCB0YXJnZXQ6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChcbiAgICAgICAgbmV3IFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSh0aGlzLCB4LCB0YXJnZXQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbWVhbi1zcXVhcmVkIGNvc3Qgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAgICogQHBhcmFtIGxhYmVsIFRoZSBsYWJlbCB0ZW5zb3IuXG4gICAqIEBwYXJhbSBwcmVkaWN0aW9uIFRoZSBwcmVkaWN0aW9uIHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbWVhbi1zcXVhcmVkIGNvc3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWVhblNxdWFyZWRDb3N0KGxhYmVsOiBUZW5zb3IsIHByZWRpY3Rpb246IFRlbnNvcikge1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBNZWFuU3F1YXJlZENvc3ROb2RlKHRoaXMsIGxhYmVsLCBwcmVkaWN0aW9uKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgZmxhdHRlbmVkIGluZGV4IG9mIHRoZSBtYXhpbXVtIGVudHJ5IGluIHRoZSB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSB0ZW5zb3Igd2l0aCB0aGUgdmFsdWUuXG4gICAqIEByZXR1cm4gQSBTY2FsYXIgdGVuc29yIHdpdGggdGhlIGluZGV4IG9mIHRoZSBtYXhpbXVtIGVudHJ5LlxuICAgKi9cbiAgYXJnbWF4KHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQXJnTWF4Tm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBhcmdtYXggZXF1YWxzIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSB4MSBGaXJzdCBpbnB1dCB0ZW5zb3IgdG8gY2hlY2sgYWdhaW5zdC5cbiAgICogQHBhcmFtIHgyIFNlY29uZCBpbnB1dCB0ZW5zb3IgdG8gY2hlY2sgYWdhaW5zdC5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgYXJnbWF4IGVxdWFscyBvcGVyYXRpb24uXG4gICAqL1xuICBhcmdtYXhFcXVhbHMoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQXJnTWF4RXF1YWxzTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIHByaXZhdGUgYWRkTm9kZUFuZFJldHVybk91dHB1dChub2RlOiBOb2RlKTogVGVuc29yIHtcbiAgICB0aGlzLm5vZGVzLnB1c2gobm9kZSk7XG4gICAgbm9kZS52YWxpZGF0ZSgpO1xuICAgIHJldHVybiBub2RlLm91dHB1dDtcbiAgfVxuXG4gIGdldE5vZGVzKCk6IE5vZGVbXSB7XG4gICAgcmV0dXJuIHRoaXMubm9kZXM7XG4gIH1cblxuICBwcml2YXRlIG5vZGVzOiBOb2RlW10gPSBbXTtcbn1cblxuLyoqXG4gKiBUZW5zb3IgcmVwcmVzZW50cyB0aGUgb3V0cHV0IG9mIGFuIG9wZXJhdGlvbiBub2RlIGluIHRoZSBncmFwaC5cbiAqIFRlbnNvcnMgaGF2ZSBubyBkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGVtLCBidXQgbWFpbnRhaW4gYSBzaGFwZSBhcnJheVxuICogdG8gZGV0ZXJtaW5lIG9wZXJhdGlvbiBjb21wYXRpYmlsaXR5LiBBbGwgZ3JhcGggbWV0aG9kcyB0aGF0IGNyZWF0ZSBncmFwaFxuICogb3BlcmF0aW9ucyByZXR1cm4gVGVuc29yIG9iamVjdHMsIHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzICdoYW5kbGVzJyB0b1xuICogb3BlcmF0aW9ucy5cbiAqL1xuZXhwb3J0IGNsYXNzIFRlbnNvciB7XG4gIG5vZGU6IE5vZGU7XG4gIGlkOiBudW1iZXI7XG4gIC8qKlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoaXMgdGVuc29yLCBpbiBkaW1lbnNpb24gc2l6ZXMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgdGhpcy5pZCA9IFRlbnNvci5uZXh0SUQrKztcbiAgfVxuICBwcml2YXRlIHN0YXRpYyBuZXh0SUQgPSAwO1xufVxuXG4vKipcbiAqIE5vZGUgaXMgdGhlIGNvbmNyZXRlIGJhc2UgY2xhc3MgZm9yIGFsbCBvcGVyYXRpb25zIGluIHRoZSBncmFwaC5cbiAqIFVzZXJzIGdlbmVyYWxseSBkb24ndCBuZWVkIHRvIGludGVyYWN0IGRpcmVjdGx5IHdpdGggTm9kZSBpbnN0YW5jZXMsIGJ1dCB0aGV5XG4gKiBhcmUgcHJvdmlkZWQgZm9yIGluZm9ybWF0aW9uYWwgYW5kIGludHJvc3BlY3Rpb24gcHVycG9zZXMuXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTm9kZSB7XG4gIC8qKlxuICAgKiBAcGFyYW0gZ3JhcGggVGhlIGdyYXBoIGNvbnRhaW5pbmcgdGhpcyBub2RlXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgbm9kZVxuICAgKiBAcGFyYW0gaW5wdXRzIEEgZGljdGlvbmFyeSBvZiBuYW1lZCBUZW5zb3JzIHRoYXQgY29tcHJpc2UgdGhpcyBub2RlJ3NcbiAgICogaW5wdXRzLlxuICAgKiBAcGFyYW0gb3V0cHV0IFRoaXMgbm9kZSdzIG91dHB1dCBUZW5zb3JcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHVibGljIGdyYXBoOiBHcmFwaCwgcHVibGljIG5hbWU6IHN0cmluZyxcbiAgICAgIHB1YmxpYyBpbnB1dHM6IHtbbmFtZTogc3RyaW5nXTogVGVuc29yfSwgcHVibGljIG91dHB1dDogVGVuc29yKSB7XG4gICAgdGhpcy5pZCA9IE5vZGUubmV4dElEKys7XG4gICAgb3V0cHV0Lm5vZGUgPSB0aGlzO1xuICB9XG4gIGFic3RyYWN0IHZhbGlkYXRlKCk6IHZvaWQ7XG4gIGlkOiBudW1iZXI7XG4gIHByaXZhdGUgc3RhdGljIG5leHRJRCA9IDA7XG59XG5cbi8qKlxuICogVmFyaWFibGVOb2RlIHJlcHJlc2VudHMgYSB2YXJpYWJsZSwgYSB1c2VyLXByb3ZpZGVkIE5EQXJyYXkgdGhhdCdzXG4gKiBhZGp1c3RlZCBkdXJpbmcgYmFja3Byb3BhZ2F0aW9uIHRyYWluaW5nLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFZhcmlhYmxlTm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIG5hbWU6IHN0cmluZywgcHVibGljIGRhdGE6IE5EQXJyYXkpIHtcbiAgICBzdXBlcihncmFwaCwgbmFtZSwge30sIG5ldyBUZW5zb3IoZGF0YS5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLmRhdGEgIT0gbnVsbCxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyB2YXJpYWJsZSBvcDogRGF0YSBmb3IgdmFyaWFibGUgXFwnJyArIHRoaXMubmFtZSArXG4gICAgICAgICAgICAnXFwnIGlzIG51bGwgb3IgdW5kZWZpbmVkJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBQbGFjZWhvbGRlck5vZGUgcmVwcmVzZW50cyBhIHBsYWNlaG9sZGVyLCBhIHVzZXItcHJvdmlkZWQgTkRBcnJheVxuICogdGhhdCdzIHVzZWQgYXMgaW1tdXRhYmxlIGlucHV0IGR1cmluZyBpbmZlcmVuY2UgYW5kIHRyYWluaW5nLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFBsYWNlaG9sZGVyTm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIG5hbWU6IHN0cmluZywgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt9LCBuZXcgVGVuc29yKHNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIENvbnN0YW50Tm9kZSByZXByZXNlbnRzIGEgY29uc3RhbnQgdmFsdWUgaW4gdGhlIGdyYXBoLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbnN0YW50Tm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHB1YmxpYyBkYXRhOiBOREFycmF5KSB7XG4gICAgc3VwZXIoZ3JhcGgsICdDb25zdGFudCcsIHt9LCBuZXcgVGVuc29yKGRhdGEuc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5kYXRhICE9IG51bGwsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29uc3RhbnQ6IGRhdGEgZm9yIHBsYWNlaG9sZGVyIFxcJycgKyB0aGlzLm5hbWUgK1xuICAgICAgICAgICAgJ1xcJyBpcyBudWxsIG9yIHVuZGVmaW5lZCcpO1xuICB9XG59XG5cbi8qKlxuICogUmVzaGFwZU5vZGUgcmVwcmVzZW50cyBhIHJlc2hhcGUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNoYXBlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHB1YmxpYyBuYW1lOiBzdHJpbmcsIHByaXZhdGUgeDogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBzaGFwZTogbnVtYmVyW10pIHtcbiAgICBzdXBlcihncmFwaCwgbmFtZSwge3h9LCBuZXcgVGVuc29yKHNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgY29uc3QgeFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy54LnNoYXBlKTtcbiAgICBjb25zdCBzaGFwZVNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy5zaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHhTaXplID09PSBzaGFwZVNpemUsXG4gICAgICAgICdFcnJvciBtYWtpbmcgcmVzaGFwZSBvcGVyYXRpb246IGlucHV0IFRlbnNvciB0byByZXNoYXBlIFxcJycgK1xuICAgICAgICAgICAgdGhpcy5uYW1lICsgJ1xcJyBvZiBzaGFwZSAoJyArIHRoaXMueC5zaGFwZSArXG4gICAgICAgICAgICAnKSBkb2VzIG5vdCBtYXRjaCBzaXplIG9mIHJlcXVlc3RlZCBzaGFwZSAnICsgdGhpcy5zaGFwZSArICcuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBMaW5lYXJDb21iaW5hdGlvbk5vZGUgcmVwcmVzZW50cyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0d28gdGVuc29ycy5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEZ1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG4gIHN0YXRpYyByZWFkb25seSBDMSA9ICdjMSc7XG4gIHN0YXRpYyByZWFkb25seSBDMiA9ICdjMic7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvciwgcHJpdmF0ZSBjMTogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBjMjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdMaW5lYXIgQ29tYmluYXRpb24nLCB7dDEsIHQyLCBjMSwgYzJ9LCBuZXcgVGVuc29yKHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpO1xuICAgIGlmICghdXRpbC5pc1NjYWxhclNoYXBlKHRoaXMuYzEuc2hhcGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBmdXNlZExpbmVhckNvbWJpbmF0aW9uOiBjMSBpcyBub3QgYSBzY2FsYXIsIGdvdCAnICtcbiAgICAgICAgICAnc2hhcGU6ICcgKyB0aGlzLmMxLnNoYXBlKTtcbiAgICB9XG4gICAgaWYgKCF1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy5jMi5zaGFwZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIGZ1c2VkTGluZWFyQ29tYmluYXRpb246IGMyIGlzIG5vdCBhIHNjYWxhciwgZ290ICcgK1xuICAgICAgICAgICdzaGFwZTogJyArIHRoaXMuYzIuc2hhcGUpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFkZE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnQWRkJywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBhZGQgb3BlcmF0aW9uIG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTdWJ0cmFjdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnU3VidHJhY3QnLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIHN1YnRyYWN0IG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNdWx0aXBseU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTXVsdGlwbHknLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIG11bHRpcGx5IG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBEaXZpZGVOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0RpdmlkZScsIHt0MSwgdDJ9LFxuICAgICAgICBuZXcgVGVuc29yKHV0aWwuc2l6ZUZyb21TaGFwZSh0MS5zaGFwZSkgPT09IDEgPyB0Mi5zaGFwZSA6IHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50Mi5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy50MS5zaGFwZSwgdGhpcy50Mi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgZGl2aWRlIG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZWR1Y2VTdW1Ob2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdSZWR1Y2VTdW0nLCB7eH0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBDb25jYXQzRE5vZGUgcmVwcmVzZW50cyBhIDNEIGNvbmNhdGVuYXRpb24gb2YgdHdvIHRlbnNvcnMgYWxvbmcgYW4gYXhpcy5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbmNhdDNETm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBzdGF0aWMgcmVhZG9ubHkgQVhJUyA9ICdheGlzJztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yLFxuICAgICAgcHVibGljIGF4aXM6IG51bWJlcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0NvbmNhdDNEJywge3gxLCB4Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZShcbiAgICAgICAgICAgIHgxLnNoYXBlLCB4Mi5zaGFwZSwgYXhpcykpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIHRoaXMueDEuc2hhcGUsIHRoaXMueDIuc2hhcGUsIHRoaXMuYXhpcyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0TWF0TXVsT3V0cHV0U2hhcGUoeDFTaGFwZTogbnVtYmVyW10sIHgyU2hhcGU6IG51bWJlcltdKTogbnVtYmVyW10ge1xuICBpZiAoeDFTaGFwZS5sZW5ndGggPT09IDEgJiYgeDJTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gWzFdO1xuICB9IGVsc2UgaWYgKHgxU2hhcGUubGVuZ3RoID09PSAxICYmIHgyU2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgcmV0dXJuIFt4MlNoYXBlWzFdXTtcbiAgfSBlbHNlIGlmICh4MVNoYXBlLmxlbmd0aCA9PT0gMiAmJiB4MlNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgIHJldHVybiBbeDFTaGFwZVswXV07XG4gIH1cbiAgcmV0dXJuIFt4MVNoYXBlWzBdLCB4MlNoYXBlWzFdXTtcbn1cblxuLyoqXG4gKiBNYXRNdWxOb2RlIHJlcHJlc2VudHMgYSBmdWxseSBjb25uZWN0ZWQgbGF5ZXIgaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF0TXVsTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTWF0TXVsJywge3gxLCB4Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IoZ2V0TWF0TXVsT3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlKSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzFdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBpbm5lciBzaGFwZXMgb2YgbWF0cmljZXMgd2l0aCBzaGFwZXMgJyArXG4gICAgICAgICAgICAgIHRoaXMueDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy54Mi5zaGFwZSArICcgbXVzdCBtYXRjaC4nKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzFdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBzZWNvbmQgZGltZW5zaW9uIG9mIG1hdHJpeCB3aXRoIHNoYXBlICcgK1xuICAgICAgICAgICAgICB0aGlzLngxLnNoYXBlICsgJyBtdXN0IG1hdGNoIHNpemUgb2YgdmVjdG9yIHdpdGggc2hhcGUgJyArXG4gICAgICAgICAgICAgIHRoaXMueDIuc2hhcGUgKyAnLicpO1xuICAgIH0gZWxzZSBpZiAodGhpcy54MS5zaGFwZS5sZW5ndGggPT09IDEgJiYgdGhpcy54Mi5zaGFwZS5sZW5ndGggPT09IDIpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMueDEuc2hhcGVbMF0gPT09IHRoaXMueDIuc2hhcGVbMF0sXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXRtdWwgb3A6IHNpemUgb2YgdmVjdG9yIHdpdGggc2hhcGUgJyArIHRoaXMueDEuc2hhcGUgK1xuICAgICAgICAgICAgICAnIG11c3QgbWF0Y2ggZmlyc3QgZGltZW5zaW9uIG9mIG1hdHJpeCB3aXRoICcgK1xuICAgICAgICAgICAgICAnc2hhcGUgJyArIHRoaXMueDIuc2hhcGUgKyAnLicpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXRtdWwgb3A6IGlucHV0cyBtdXN0IGJlIHZlY3RvcnMgb3IgbWF0cmljZXMuJyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ29udm9sdXRpb24yRE5vZGUgcmVwcmVzZW50cyBhIDJkIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb252b2x1dGlvbjJETm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgc3RhdGljIHJlYWRvbmx5IFcgPSAndyc7XG4gIHN0YXRpYyByZWFkb25seSBCID0gJ2InO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgdzogVGVuc29yLCBwcml2YXRlIGI6IFRlbnNvcixcbiAgICAgIHB1YmxpYyBmaWVsZFNpemU6IG51bWJlciwgcHVibGljIG91dHB1dERlcHRoOiBudW1iZXIsIHB1YmxpYyBzdHJpZGUgPSAxLFxuICAgICAgcHVibGljIHplcm9QYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdDb252b2x1dGlvbiAyRCcsIHt4LCB3LCBifSxcbiAgICAgICAgbmV3IFRlbnNvcihjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgICAgICB4LnNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLFxuICAgICAgICAgICAgemVyb1BhZCkpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBjb252MmQgb3A6IGlucHV0IG11c3QgYmUgb2YgcmFuayAzLCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMueC5zaGFwZSArICcuJyk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMudy5zaGFwZS5sZW5ndGggPT09IDQsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiB3ZWlnaHRzIG11c3QgYmUgb2YgcmFuayA0LCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMudy5zaGFwZSArICcuJyk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuYi5zaGFwZS5sZW5ndGggPT09IDEsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBiaWFzZXMgbXVzdCBiZSBvZiByYW5rIDEsIGJ1dCBnb3Qgc2hhcGU6ICcgK1xuICAgICAgICAgICAgdGhpcy5iLnNoYXBlICsgJy4nKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGVbMl0gPT09IHRoaXMudy5zaGFwZVsyXSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBjb252MmQgb3A6IGRlcHRoIG9mIGlucHV0ICgnICsgdGhpcy54LnNoYXBlWzJdICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgKCcgKyB0aGlzLncuc2hhcGVbMl0gKyAnKS4nKTtcbiAgfVxufVxuXG4vKipcbiAqIE1heFBvb2xOb2RlIHJlcHJlc2VudHMgYSAyZCBtYXggcG9vbCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF4UG9vbE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHg6IFRlbnNvciwgcHVibGljIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgICAgcHVibGljIHN0cmlkZSA9IDEsIHB1YmxpYyB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTWF4IHBvb2wnLCB7eH0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgICAgeC5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZSwgeC5zaGFwZVsyXSwgc3RyaWRlLFxuICAgICAgICAgICAgemVyb1BhZCkpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXhQb29sIG9wOiBpbnB1dCBtdXN0IGJlIG9mIHJhbmsgMywgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLnguc2hhcGUgKyAnLicpO1xuICB9XG59XG5cbi8qKlxuICogUmVMVU5vZGUgcmVwcmVzZW50cyBhIFJlTFUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlTFVOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnUmVMVScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIEV4cE5vZGUgcmVwcmVzZW50cyBhIEV4cG9uZW50aWF0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBFeHBOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnRXhwJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogTG9nTm9kZSByZXByZXNlbnRzIGEgRXhwb25lbnRpYXRpb24gb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIExvZ05vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdMb2cnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBUYW5ITm9kZSByZXByZXNlbnRzIGEgdGFuaCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgVGFuSE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdUYW5IJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogU2lnbW9pZE5vZGUgcmVwcmVzZW50cyBhIHNpZ21vaWQgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNpZ21vaWROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU2lnbW9pZCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNxdWFyZSBub2RlIHJlcHJlc2VudHMgYW4gZWxlbWVudC13aXNlIHNxdWFyZSBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3F1YXJlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NxdWFyZScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSByZXByZXNlbnRzIGEgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uXG4gKiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIHN0YXRpYyByZWFkb25seSBUQVJHRVQgPSAndGFyZ2V0JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHg6IFRlbnNvciwgcHJpdmF0ZSB0YXJnZXQ6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU29mdG1heENyb3NzRW50cm9weUNvc3QnLCB7eCwgdGFyZ2V0fSwgbmV3IFRlbnNvcihbXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMueC5zaGFwZSwgdGhpcy50YXJnZXQuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIHNvZnRtYXhDcm9zc0VudHJvcHlDb3N0IG9wOiB4IHNoYXBlICgnICsgdGhpcy54LnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggdGFyZ2V0IHNoYXBlICgnICsgdGhpcy50YXJnZXQuc2hhcGUgKyAnKS4nKTtcbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNvZnRtYXhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NvZnRtYXgnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAnVGhlIGlucHV0IHRvIGEgc29mdG1heCBtdXN0IGJlIGEgMS1EIHRlbnNvcicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGVbMF0gPj0gMixcbiAgICAgICAgJ1RoZSBpbnB1dCB0byBhIHNvZnRtYXggbXVzdCBoYXZlIGF0IGxlYXN0IDIgdmFsdWVzJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBNZWFuU3F1YXJlZENvc3ROb2RlIHJlcHJlc2VudHMgYSBtZWFuIHNxdWFyZWQgY29zdCBvcGVyYXRpb25cbiAqIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNZWFuU3F1YXJlZENvc3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBMQUJFTCA9ICdsYWJlbCc7XG4gIHN0YXRpYyByZWFkb25seSBQUkVESUNUSU9OID0gJ3ByZWRpY3Rpb24nO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgbGFiZWw6IFRlbnNvciwgcHJpdmF0ZSBwcmVkaWN0aW9uOiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ01lYW4gU3F1YXJlZCBDb3N0Jywge2xhYmVsLCBwcmVkaWN0aW9ufSwgbmV3IFRlbnNvcihbXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMubGFiZWwuc2hhcGUsIHRoaXMucHJlZGljdGlvbi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgbWVhblNxdWFyZWRDb3N0IG9wOiBsYWJlbCBzaGFwZSAoJyArIHRoaXMubGFiZWwuc2hhcGUgK1xuICAgICAgICAgICAgJykgbXVzdCBtYXRjaCBwcmVkaWN0aW9uIHNoYXBlICgnICsgdGhpcy5wcmVkaWN0aW9uLnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBBcmdNYXhOb2RlIHJlcHJlc2VudHMgYW4gYXJnbWF4IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHB1YmxpYyB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0FyZ01heCcsIHt4fSwgbmV3IFRlbnNvcihbMV0pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMueC5zaGFwZSkgPiAwLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGFyZ21heCBvcDogaW5wdXQgdGVuc29yIG11c3QgaGF2ZSBhdCBsZWFzdCBvbmUgZW50cnkuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBBcmdNYXhFcXVhbHNOb2RlIHJlcHJlc2VudHMgYSBhcmdtYXggZXF1YWxzIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXhFcXVhbHNOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYMSA9ICd4MSc7XG4gIHN0YXRpYyByZWFkb25seSBYMiA9ICd4Mic7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4MTogVGVuc29yLCBwcml2YXRlIHgyOiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0FyZ01heEVxdWFscycsIHt4MSwgeDJ9LCBuZXcgVGVuc29yKFsxXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMueDEuc2hhcGUsIHRoaXMueDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIEFyZ01heEVxdWFscyBvcDogeDEgc2hhcGUgKCcgKyB0aGlzLngxLnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggeDIgc2hhcGUgKCcgKyB0aGlzLngyLnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBTcGxpdCBub2RlcyBhcmUgdXNlZCB0byBhY2N1bXVsYXRlIGJhY2twcm9wIGRlcml2YXRpdmVzIHdoZW4gYSBub2RlJ3Mgb3V0cHV0XG4gKiB0ZW5zb3IgaXMgY29uc3VtZWQgYnkgbXVsdGlwbGUgbm9kZXMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcGxpdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG5cbiAgb3V0cHV0czogVGVuc29yW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU3BsaXROb2RlJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlcnMgYSBuZXcgY29uc3VtZXIgb2YgdGhpcyBzcGxpdCBub2RlLCBpLmUuIGEgbmV3IG5vZGUgdGhhdCB1c2VzIHRoZVxuICAgKiBub2RlJ3Mgb3V0cHV0IHRlbnNvci5cbiAgICovXG4gIGdldE5ld091dHB1dFRlbnNvcigpOiBUZW5zb3Ige1xuICAgIGNvbnN0IG91dHB1dCA9IG5ldyBUZW5zb3IodGhpcy5pbnB1dHNbU3BsaXROb2RlLlhdLnNoYXBlKTtcbiAgICBvdXRwdXQubm9kZSA9IHRoaXM7XG4gICAgdGhpcy5vdXRwdXRzLnB1c2gob3V0cHV0KTtcbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCB0eXBlIEFycmF5RGF0YSA9XG4gICAgTkRBcnJheXxudW1iZXJ8bnVtYmVyW118bnVtYmVyW11bXXxudW1iZXJbXVtdW118bnVtYmVyW11bXVtdW107XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R3JhcGgsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQge0luaXRpYWxpemVyLCBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciwgWmVyb3NJbml0aWFsaXplcn0gZnJvbSAnLi9pbml0aWFsaXplcnMnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogQSBsYXllcnMgc3VnYXIgY2xhc3MgYXJvdW5kIHRoZSBncmFwaCB0aGF0IGluaXRpYWxpemVzIHZhcmlhYmxlc1xuICogYXV0b21hdGljYWxseSBmb3IgbGF5ZXJzLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGhMYXllcnMge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGc6IEdyYXBoKSB7fVxuXG4gIGRlbnNlKFxuICAgICAgbmFtZTogc3RyaW5nLCB4OiBUZW5zb3IsIHVuaXRzOiBudW1iZXIsXG4gICAgICBhY3RpdmF0aW9uOiAoKHg6IFRlbnNvcikgPT4gVGVuc29yKXxudWxsID0gbnVsbCwgdXNlQmlhcyA9IHRydWUsXG4gICAgICBrZXJuZWxJbml0aWFsaXplcjogSW5pdGlhbGl6ZXIgPSBuZXcgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIoKSxcbiAgICAgIGJpYXNJbml0aWFsaXplcjogSW5pdGlhbGl6ZXIgPSBuZXcgWmVyb3NJbml0aWFsaXplcigpKSB7XG4gICAgY29uc3Qgd2VpZ2h0cyA9IHRoaXMuZy52YXJpYWJsZShcbiAgICAgICAgbmFtZSArICctd2VpZ2h0cycsXG4gICAgICAgIGtlcm5lbEluaXRpYWxpemVyLmluaXRpYWxpemUoW3guc2hhcGVbMF0sIHVuaXRzXSwgeC5zaGFwZVswXSwgdW5pdHMpKTtcblxuICAgIGxldCBvdXQgPSB0aGlzLmcubWF0bXVsKHgsIHdlaWdodHMpO1xuXG4gICAgaWYgKHVzZUJpYXMpIHtcbiAgICAgIGNvbnN0IGJpYXMgPSB0aGlzLmcudmFyaWFibGUoXG4gICAgICAgICAgbmFtZSArICctYmlhcycsXG4gICAgICAgICAgYmlhc0luaXRpYWxpemVyLmluaXRpYWxpemUoW3VuaXRzXSwgeC5zaGFwZVswXSwgdW5pdHMpKTtcbiAgICAgIG91dCA9IHRoaXMuZy5hZGQob3V0LCBiaWFzKTtcbiAgICB9XG5cbiAgICBpZiAoYWN0aXZhdGlvbiAhPSBudWxsKSB7XG4gICAgICBvdXQgPSBhY3RpdmF0aW9uKG91dCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dDtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBkYXRhc2V0IGZyb20gJy4vZGF0YXNldCc7XG5pbXBvcnQge0dyYXBoLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheU1hdGhDUFV9IGZyb20gJy4vbWF0aC9tYXRoX2NwdSc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCB7Q29zdFJlZHVjdGlvbiwgRmVlZEVudHJ5LCBTZXNzaW9ufSBmcm9tICcuL3Nlc3Npb24nO1xuXG5jb25zdCBERUZBVUxUX0VWQUxfSU5URVJWQUxfTVMgPSAxNTAwO1xuY29uc3QgREVGQVVMVF9DT1NUX0lOVEVSVkFMX01TID0gNTAwO1xuY29uc3QgREVGQVVMVF9JTkZFUkVOQ0VfRVhBTVBMRV9JTlRFUlZBTF9NUyA9IDMwMDA7XG5cbmV4cG9ydCBpbnRlcmZhY2UgR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyIHtcbiAgYmF0Y2hlc1RyYWluZWRDYWxsYmFjaz86ICh0b3RhbEJhdGNoZXNUcmFpbmVkOiBudW1iZXIpID0+IHZvaWQ7XG4gIGF2Z0Nvc3RDYWxsYmFjaz86IChhdmdDb3N0OiBTY2FsYXIpID0+IHZvaWQ7XG4gIG1ldHJpY0NhbGxiYWNrPzogKG1ldHJpYzogTkRBcnJheSkgPT4gdm9pZDtcbiAgaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjaz86XG4gICAgICAoZmVlZHM6IEZlZWRFbnRyeVtdW10sIGluZmVyZW5jZVZhbHVlczogTkRBcnJheVtdKSA9PiB2b2lkO1xuICBpbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrPzogKGV4YW1wbGVzUGVyU2VjOiBudW1iZXIpID0+IHZvaWQ7XG4gIHRyYWluRXhhbXBsZXNQZXJTZWNDYWxsYmFjaz86IChleGFtcGxlc1BlclNlYzogbnVtYmVyKSA9PiB2b2lkO1xuICB0b3RhbFRpbWVDYWxsYmFjaz86ICh0b3RhbFRpbWVTZWM6IG51bWJlcikgPT4gdm9pZDtcbiAgZG9uZVRyYWluaW5nQ2FsbGJhY2s/OiAoKSA9PiB2b2lkO1xufVxuXG5leHBvcnQgZW51bSBNZXRyaWNSZWR1Y3Rpb24ge1xuICBTVU0sXG4gIE1FQU5cbn1cblxuLyoqXG4gKiBBIGNsYXNzIHRoYXQgZHJpdmVzIHRoZSB0cmFpbmluZyBvZiBhIGdyYXBoIG1vZGVsIGdpdmVuIGEgZGF0YXNldC4gSXQgYWxsb3dzXG4gKiB0aGUgdXNlciB0byBwcm92aWRlIGEgc2V0IG9mIGNhbGxiYWNrcyBmb3IgbWVhc3VyZW1lbnRzIGxpa2UgY29zdCwgYWNjdXJhY3ksXG4gKiBhbmQgc3BlZWQgb2YgdHJhaW5pbmcuXG4gKi9cbmV4cG9ydCBjbGFzcyBHcmFwaFJ1bm5lciB7XG4gIHByaXZhdGUgY29zdFRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIHRyYWluRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdO1xuICBwcml2YXRlIGJhdGNoU2l6ZTogbnVtYmVyO1xuICBwcml2YXRlIG9wdGltaXplcjogT3B0aW1pemVyO1xuICBwcml2YXRlIGN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIGNvc3RJbnRlcnZhbE1zOiBudW1iZXI7XG5cbiAgcHJpdmF0ZSBtZXRyaWNUZW5zb3I6IFRlbnNvcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBtZXRyaWNCYXRjaFNpemU6IG51bWJlcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljUmVkdWN0aW9uOiBNZXRyaWNSZWR1Y3Rpb247XG4gIHByaXZhdGUgbWV0cmljSW50ZXJ2YWxNczogbnVtYmVyO1xuXG4gIHByaXZhdGUgaW5mZXJlbmNlVGVuc29yOiBUZW5zb3I7XG4gIHByaXZhdGUgaW5mZXJlbmNlRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBpbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNczogbnVtYmVyO1xuICBwcml2YXRlIGluZmVyZW5jZUV4YW1wbGVDb3VudDogbnVtYmVyO1xuXG4gIC8vIFJ1bnRpbWUgaW5mb3JtYXRpb24uXG4gIHByaXZhdGUgaXNUcmFpbmluZzogYm9vbGVhbjtcbiAgcHJpdmF0ZSB0b3RhbEJhdGNoZXNUcmFpbmVkOiBudW1iZXI7XG4gIHByaXZhdGUgYmF0Y2hlc1RyYWluZWRUaGlzUnVuOiBudW1iZXI7XG4gIHByaXZhdGUgbGFzdENvbXB1dGVkTWV0cmljOiBOREFycmF5O1xuXG4gIHByaXZhdGUgaXNJbmZlcnJpbmc6IGJvb2xlYW47XG4gIHByaXZhdGUgY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXM6IG51bWJlcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgaW5mZXJlbmNlUGFzc2VzVGhpc1J1bjogbnVtYmVyO1xuXG4gIHByaXZhdGUgdHJhaW5TdGFydFRpbWVzdGFtcDogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RDb3N0VGltZXN0YW1wID0gMDtcbiAgcHJpdmF0ZSBsYXN0RXZhbFRpbWVzdGFtcCA9IDA7XG5cbiAgcHJpdmF0ZSBsYXN0U3RvcFRpbWVzdGFtcDogbnVtYmVyfG51bGw7XG4gIHByaXZhdGUgdG90YWxJZGxlVGltZU1zID0gMDtcblxuICBwcml2YXRlIHplcm9TY2FsYXI6IFNjYWxhcjtcbiAgcHJpdmF0ZSBtZXRyaWNCYXRjaFNpemVTY2FsYXI6IFNjYWxhcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGgsIHByaXZhdGUgc2Vzc2lvbjogU2Vzc2lvbixcbiAgICAgIHByaXZhdGUgZXZlbnRPYnNlcnZlcjogR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyKSB7XG4gICAgdGhpcy5yZXNldFN0YXRpc3RpY3MoKTtcbiAgICB0aGlzLnplcm9TY2FsYXIgPSBTY2FsYXIubmV3KDApO1xuICB9XG5cbiAgcmVzZXRTdGF0aXN0aWNzKCkge1xuICAgIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCA9IDA7XG4gICAgdGhpcy50b3RhbElkbGVUaW1lTXMgPSAwO1xuICAgIHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0IHRoZSB0cmFpbmluZyBsb29wIHdpdGggYW4gb3B0aW9uYWwgbnVtYmVyIG9mIGJhdGNoZXMgdG8gdHJhaW4gZm9yLlxuICAgKiBPcHRpb25hbGx5IHRha2VzIGEgbWV0cmljIHRlbnNvciBhbmQgZmVlZCBlbnRyaWVzIHRvIGNvbXB1dGUgcGVyaW9kaWNhbGx5LlxuICAgKiBUaGlzIGNhbiBiZSB1c2VkIGZvciBjb21wdXRpbmcgYWNjdXJhY3ksIG9yIGEgc2ltaWxhciBtZXRyaWMuXG4gICAqL1xuICB0cmFpbihcbiAgICAgIGNvc3RUZW5zb3I6IFRlbnNvciwgdHJhaW5GZWVkRW50cmllczogRmVlZEVudHJ5W10sIGJhdGNoU2l6ZTogbnVtYmVyLFxuICAgICAgb3B0aW1pemVyOiBPcHRpbWl6ZXIsIG51bUJhdGNoZXM/OiBudW1iZXIsIG1ldHJpY1RlbnNvcj86IFRlbnNvcixcbiAgICAgIG1ldHJpY0ZlZWRFbnRyaWVzPzogRmVlZEVudHJ5W10sIG1ldHJpY0JhdGNoU2l6ZT86IG51bWJlcixcbiAgICAgIG1ldHJpY1JlZHVjdGlvbiA9IE1ldHJpY1JlZHVjdGlvbi5NRUFOLFxuICAgICAgZXZhbEludGVydmFsTXMgPSBERUZBVUxUX0VWQUxfSU5URVJWQUxfTVMsXG4gICAgICBjb3N0SW50ZXJ2YWxNcyA9IERFRkFVTFRfQ09TVF9JTlRFUlZBTF9NUykge1xuICAgIHRoaXMuY29zdFRlbnNvciA9IGNvc3RUZW5zb3I7XG4gICAgdGhpcy50cmFpbkZlZWRFbnRyaWVzID0gdHJhaW5GZWVkRW50cmllcztcbiAgICB0aGlzLm1ldHJpY1RlbnNvciA9IG1ldHJpY1RlbnNvcjtcbiAgICB0aGlzLm1ldHJpY0ZlZWRFbnRyaWVzID0gbWV0cmljRmVlZEVudHJpZXM7XG4gICAgaWYgKG1ldHJpY0JhdGNoU2l6ZSAhPSBudWxsICYmIHRoaXMubWV0cmljQmF0Y2hTaXplICE9PSBtZXRyaWNCYXRjaFNpemUpIHtcbiAgICAgIGlmICh0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhtZXRyaWNCYXRjaFNpemUpO1xuICAgIH1cbiAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZSA9IG1ldHJpY0JhdGNoU2l6ZTtcbiAgICB0aGlzLm1ldHJpY1JlZHVjdGlvbiA9IG1ldHJpY1JlZHVjdGlvbjtcbiAgICB0aGlzLmJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICB0aGlzLm9wdGltaXplciA9IG9wdGltaXplcjtcblxuICAgIHRoaXMubWV0cmljSW50ZXJ2YWxNcyA9IGV2YWxJbnRlcnZhbE1zO1xuICAgIHRoaXMuY29zdEludGVydmFsTXMgPSBjb3N0SW50ZXJ2YWxNcztcbiAgICB0aGlzLmN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzID0gbnVtQmF0Y2hlcztcblxuICAgIHRoaXMuYmF0Y2hlc1RyYWluZWRUaGlzUnVuID0gMDtcbiAgICB0aGlzLmlzVHJhaW5pbmcgPSB0cnVlO1xuICAgIHRoaXMudHJhaW5TdGFydFRpbWVzdGFtcCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIHRoaXMudHJhaW5OZXR3b3JrKCk7XG4gIH1cblxuICBzdG9wVHJhaW5pbmcoKSB7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0U3RvcFRpbWVzdGFtcCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICB9XG5cbiAgcmVzdW1lVHJhaW5pbmcoKSB7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gdHJ1ZTtcbiAgICBpZiAodGhpcy5sYXN0U3RvcFRpbWVzdGFtcCAhPSBudWxsKSB7XG4gICAgICB0aGlzLnRvdGFsSWRsZVRpbWVNcyArPSBwZXJmb3JtYW5jZS5ub3coKSAtIHRoaXMubGFzdFN0b3BUaW1lc3RhbXA7XG4gICAgfVxuICAgIHRoaXMudHJhaW5OZXR3b3JrKCk7XG4gIH1cblxuICBwcml2YXRlIHRyYWluTmV0d29yaygpIHtcbiAgICBpZiAodGhpcy5iYXRjaGVzVHJhaW5lZFRoaXNSdW4gPT09IHRoaXMuY3VycmVudFRyYWluTG9vcE51bUJhdGNoZXMpIHtcbiAgICAgIHRoaXMuc3RvcFRyYWluaW5nKCk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmlzVHJhaW5pbmcpIHtcbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuZG9uZVRyYWluaW5nQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIuZG9uZVRyYWluaW5nQ2FsbGJhY2soKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnN0IHNob3VsZENvbXB1dGVDb3N0ID0gdGhpcy5ldmVudE9ic2VydmVyLmF2Z0Nvc3RDYWxsYmFjayAhPSBudWxsICYmXG4gICAgICAgIChzdGFydCAtIHRoaXMubGFzdENvc3RUaW1lc3RhbXAgPiB0aGlzLmNvc3RJbnRlcnZhbE1zKTtcbiAgICBpZiAoc2hvdWxkQ29tcHV0ZUNvc3QpIHtcbiAgICAgIHRoaXMubGFzdENvc3RUaW1lc3RhbXAgPSBzdGFydDtcbiAgICB9XG5cbiAgICBjb25zdCBjb3N0UmVkdWN0aW9uID1cbiAgICAgICAgc2hvdWxkQ29tcHV0ZUNvc3QgPyBDb3N0UmVkdWN0aW9uLk1FQU4gOiBDb3N0UmVkdWN0aW9uLk5PTkU7XG5cbiAgICB0aGlzLm1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGF2Z0Nvc3QgPSB0aGlzLnNlc3Npb24udHJhaW4oXG4gICAgICAgICAgdGhpcy5jb3N0VGVuc29yLCB0aGlzLnRyYWluRmVlZEVudHJpZXMsIHRoaXMuYmF0Y2hTaXplLFxuICAgICAgICAgIHRoaXMub3B0aW1pemVyLCBjb3N0UmVkdWN0aW9uKTtcblxuICAgICAgaWYgKHNob3VsZENvbXB1dGVDb3N0KSB7XG4gICAgICAgIGNvbnN0IHRyYWluVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQ7XG5cbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmF2Z0Nvc3RDYWxsYmFjayEoYXZnQ29zdCk7XG5cbiAgICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci50cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICAgIGNvbnN0IGV4YW1wbGVzUGVyU2VjID0gKHRoaXMuYmF0Y2hTaXplICogMTAwMCAvIHRyYWluVGltZSk7XG4gICAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLnRyYWluRXhhbXBsZXNQZXJTZWNDYWxsYmFjayhleGFtcGxlc1BlclNlYyk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5tZXRyaWNDYWxsYmFjayAhPSBudWxsICYmXG4gICAgICAgICAgdGhpcy5tZXRyaWNGZWVkRW50cmllcyAhPSBudWxsICYmXG4gICAgICAgICAgc3RhcnQgLSB0aGlzLmxhc3RFdmFsVGltZXN0YW1wID4gdGhpcy5tZXRyaWNJbnRlcnZhbE1zKSB7XG4gICAgICAgIHRoaXMubGFzdEV2YWxUaW1lc3RhbXAgPSBzdGFydDtcblxuICAgICAgICBpZiAodGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMgIT0gbnVsbCkge1xuICAgICAgICAgIHRoaXMubGFzdENvbXB1dGVkTWV0cmljLmRpc3Bvc2UoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYyA9IHRoaXMuY29tcHV0ZU1ldHJpYygpO1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIubWV0cmljQ2FsbGJhY2sodGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLnRvdGFsVGltZUNhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLnRvdGFsVGltZUNhbGxiYWNrKFxuICAgICAgICAgICAgKHN0YXJ0IC0gdGhpcy50cmFpblN0YXJ0VGltZXN0YW1wKSAvIDEwMDApO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmJhdGNoZXNUcmFpbmVkVGhpc1J1bisrO1xuICAgICAgdGhpcy50b3RhbEJhdGNoZXNUcmFpbmVkKys7XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuYmF0Y2hlc1RyYWluZWRDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5iYXRjaGVzVHJhaW5lZENhbGxiYWNrKHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCk7XG4gICAgICB9XG5cbiAgICB9KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMudHJhaW5OZXR3b3JrKCkpO1xuICB9XG5cbiAgaW5mZXIoXG4gICAgICBpbmZlcmVuY2VUZW5zb3I6IFRlbnNvciwgaW5mZXJlbmNlRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLFxuICAgICAgaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMgPSBERUZBVUxUX0lORkVSRU5DRV9FWEFNUExFX0lOVEVSVkFMX01TLFxuICAgICAgaW5mZXJlbmNlRXhhbXBsZUNvdW50ID0gNSwgbnVtUGFzc2VzPzogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrID09IG51bGwgJiZcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdDYW5ub3Qgc3RhcnQgaW5mZXJlbmNlIGxvb3AsIG5vIGluZmVyZW5jZSBleGFtcGxlIG9yICcgK1xuICAgICAgICAgICdleGFtcGxlcy9zZWMgb2JzZXJ2ZXIgcHJvdmlkZWQuJyk7XG4gICAgfVxuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBmZWVkIHZhbHVlcyBhcmUgcHJvdmlkZXJzLCBhbmQgbm90IE5EQXJyYXlzLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5mZXJlbmNlRmVlZEVudHJpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGZlZWRFbnRyeSA9IGluZmVyZW5jZUZlZWRFbnRyaWVzW2ldO1xuXG4gICAgICBpZiAoZmVlZEVudHJ5LmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdDYW5ub3Qgc3RhcnQgaW5mZXJlbmNlIG9uIHRoZSBtb2RlbCBydW5uZXIgd2l0aCBmZWVkIGVudHJpZXMgb2YgJyArXG4gICAgICAgICAgICAndHlwZSBOREFycmF5LiBQbGVhc2UgdXNlIElucHV0UHJvdmlkZXJzLicpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMgPSBpbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNcztcbiAgICB0aGlzLmluZmVyZW5jZVRlbnNvciA9IGluZmVyZW5jZVRlbnNvcjtcbiAgICB0aGlzLmluZmVyZW5jZUZlZWRFbnRyaWVzID0gaW5mZXJlbmNlRmVlZEVudHJpZXM7XG4gICAgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgPSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ7XG4gICAgdGhpcy5jdXJyZW50SW5mZXJlbmNlTG9vcE51bVBhc3NlcyA9IG51bVBhc3NlcztcbiAgICBpZiAoIXRoaXMuaXNJbmZlcnJpbmcpIHtcbiAgICAgIHRoaXMuaW5mZXJlbmNlUGFzc2VzVGhpc1J1biA9IDA7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuaW5mZXJOZXR3b3JrKCkpO1xuICAgIH1cbiAgICB0aGlzLmlzSW5mZXJyaW5nID0gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgaW5mZXJOZXR3b3JrKCkge1xuICAgIGlmICghdGhpcy5pc0luZmVycmluZyB8fFxuICAgICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4gPT09IHRoaXMuY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLm1hdGguc2NvcGUoKGtlZXAsIHRyYWNrKSA9PiB7XG4gICAgICBjb25zdCBmZWVkczogRmVlZEVudHJ5W11bXSA9IFtdO1xuICAgICAgY29uc3QgaW5mZXJlbmNlVmFsdWVzOiBOREFycmF5W10gPSBbXTtcblxuICAgICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQ7IGkrKykge1xuICAgICAgICAvLyBQb3B1bGF0ZSBhIG5ldyBGZWVkRW50cnlbXSBwb3B1bGF0ZWQgd2l0aCBOREFycmF5cy5cbiAgICAgICAgY29uc3QgbmRhcnJheUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5mZXJlbmNlRmVlZEVudHJpZXMhLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgY29uc3QgZmVlZEVudHJ5ID0gdGhpcy5pbmZlcmVuY2VGZWVkRW50cmllcyFbal07XG4gICAgICAgICAgbmRhcnJheUZlZWRFbnRyaWVzLnB1c2goe1xuICAgICAgICAgICAgdGVuc29yOiBmZWVkRW50cnkudGVuc29yLFxuICAgICAgICAgICAgZGF0YTpcbiAgICAgICAgICAgICAgICB0cmFjaygoZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcikuZ2V0TmV4dENvcHkodGhpcy5tYXRoKSlcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBmZWVkcy5wdXNoKG5kYXJyYXlGZWVkRW50cmllcyk7XG5cbiAgICAgICAgaW5mZXJlbmNlVmFsdWVzLnB1c2goXG4gICAgICAgICAgICB0aGlzLnNlc3Npb24uZXZhbCh0aGlzLmluZmVyZW5jZVRlbnNvciwgbmRhcnJheUZlZWRFbnRyaWVzKSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIC8vIEZvcmNlIGEgR1BVIGRvd25sb2FkLCBzaW5jZSBpbmZlcmVuY2UgcmVzdWx0cyBhcmUgZ2VuZXJhbGx5IG5lZWRlZCBvblxuICAgICAgICAvLyB0aGUgQ1BVIGFuZCBpdCdzIG1vcmUgZmFpciB0byBpbmNsdWRlIGJsb2NraW5nIG9uIHRoZSBHUFUgdG8gY29tcGxldGVcbiAgICAgICAgLy8gaXRzIHdvcmsgZm9yIHRoZSBpbmZlcmVuY2UgbWVhc3VyZW1lbnQuXG4gICAgICAgIGluZmVyZW5jZVZhbHVlc1tpbmZlcmVuY2VWYWx1ZXMubGVuZ3RoIC0gMV0uZ2V0VmFsdWVzKCk7XG5cbiAgICAgICAgY29uc3QgaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNUaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydDtcblxuICAgICAgICBjb25zdCBleGFtcGxlc1BlclNlYyA9XG4gICAgICAgICAgICAodGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgKiAxMDAwIC8gaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNUaW1lKTtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2shKGV4YW1wbGVzUGVyU2VjKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzQ2FsbGJhY2soZmVlZHMsIGluZmVyZW5jZVZhbHVlcyk7XG4gICAgICB9XG4gICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4rKztcblxuICAgIH0pO1xuICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5pbmZlck5ldHdvcmsoKSwgdGhpcy5pbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNcyk7XG4gIH1cblxuICBzdG9wSW5mZXJyaW5nKCkge1xuICAgIHRoaXMuaXNJbmZlcnJpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIGlzSW5mZXJlbmNlUnVubmluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc0luZmVycmluZztcbiAgfVxuXG4gIGNvbXB1dGVNZXRyaWMoKTogU2NhbGFyIHtcbiAgICBpZiAodGhpcy5tZXRyaWNGZWVkRW50cmllcyA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBjb21wdXRlIG1ldHJpYywgbm8gbWV0cmljIEZlZWRFbnRyaWVzIHByb3ZpZGVkLicpO1xuICAgIH1cblxuICAgIGxldCBtZXRyaWMgPSB0aGlzLnplcm9TY2FsYXI7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubWV0cmljQmF0Y2hTaXplITsgaSsrKSB7XG4gICAgICAgIGNvbnN0IG1ldHJpY1ZhbHVlID1cbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbi5ldmFsKHRoaXMubWV0cmljVGVuc29yISwgdGhpcy5tZXRyaWNGZWVkRW50cmllcyEpO1xuXG4gICAgICAgIG1ldHJpYyA9IHRoaXMubWF0aC5hZGQobWV0cmljLCBtZXRyaWNWYWx1ZSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLm1ldHJpY1JlZHVjdGlvbiA9PT0gTWV0cmljUmVkdWN0aW9uLk1FQU4pIHtcbiAgICAgICAgbWV0cmljID0gdGhpcy5tYXRoLmRpdmlkZShtZXRyaWMsIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG1ldHJpYztcbiAgICB9KTtcbiAgfVxuXG4gIGdldFRvdGFsQmF0Y2hlc1RyYWluZWQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b3RhbEJhdGNoZXNUcmFpbmVkO1xuICB9XG5cbiAgZ2V0TGFzdENvbXB1dGVkTWV0cmljKCk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMubGFzdENvbXB1dGVkTWV0cmljO1xuICB9XG5cbiAgc2V0TWF0aChtYXRoOiBOREFycmF5TWF0aCkge1xuICAgIHRoaXMubWF0aCA9IG1hdGg7XG4gIH1cblxuICBzZXRTZXNzaW9uKHNlc3Npb246IFNlc3Npb24pIHtcbiAgICB0aGlzLnNlc3Npb24gPSBzZXNzaW9uO1xuICB9XG5cbiAgc2V0SW5mZXJlbmNlVGVuc29yKGluZmVyZW5jZVRlbnNvcjogVGVuc29yKSB7XG4gICAgdGhpcy5pbmZlcmVuY2VUZW5zb3IgPSBpbmZlcmVuY2VUZW5zb3I7XG4gIH1cblxuICBzZXRJbmZlcmVuY2VFeGFtcGxlQ291bnQoaW5mZXJlbmNlRXhhbXBsZUNvdW50OiBudW1iZXIpIHtcbiAgICB0aGlzLmluZmVyZW5jZUV4YW1wbGVDb3VudCA9IGluZmVyZW5jZUV4YW1wbGVDb3VudDtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0NvbnN0YW50Tm9kZSwgR3JhcGgsIE5vZGUsIFBsYWNlaG9sZGVyTm9kZSwgVGVuc29yLCBWYXJpYWJsZU5vZGV9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgcHJpb3JpdHlfcXVldWUgZnJvbSAnLi9wcmlvcml0eV9xdWV1ZSc7XG5pbXBvcnQge1ByaW9yaXR5UXVldWV9IGZyb20gJy4vcHJpb3JpdHlfcXVldWUnO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuLyoqXG4gKiBHaXZlbiBhIHRhcmdldCBub2RlIGluIGEgZ3JhcGgsIGFjY3VtdWxhdGUgdGhlIHNldCBvZiBhbGwgbm9kZXMgdGhhdCBuZWVkIHRvXG4gKiBiZSBldmFsdWF0ZWQgaW4gb3JkZXIgdG8gZXZhbHVhdGUgdGhlIHRhcmdldCBncmFwaC4gVHJhdmVyc2FsIHN0b3BzIGFueXdoZXJlXG4gKiBhIG5vZGUncyB2YWx1ZXMgYXJlIGZlZCBpbiBleHRlcm5hbGx5IHZpYSBcImZlZWQgZGljdHNcIi5cbiAqIEBwYXJhbSBub2RlcyBUaGUgbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIHRlcm1pbmF0aW5nTm9kZXMgVGhlIHNldCBvZiBub2RlcyB0aGF0IHN0b3AgdHJhdmVyc2FsLlxuICogQHJldHVybiBUaGUgdW5vcmRlcmVkIHNldCBvZiBub2RlcyB0aGF0IG5lZWQgdG8gYmUgZXZhbHVhdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5vcmRlcmVkRXZhbHVhdGlvblNldChcbiAgICBub2RlczogTm9kZVtdLCB0ZXJtaW5hdGluZ05vZGVzOiBOb2RlW10pOiBOb2RlW10ge1xuICBjb25zdCB0ZXJtaW5hdGluZ05vZGVNYXA6IHtbaWQ6IG51bWJlcl06IE5vZGV9ID0ge307XG4gIGNvbnN0IHNlZW46IHtbaWQ6IG51bWJlcl06IE5vZGV9ID0ge307XG4gIGNvbnN0IHNldDogTm9kZVtdID0gW107XG4gIGNvbnN0IHZpc2l0OiBOb2RlW10gPSBub2Rlcy5zbGljZSgpO1xuICB0ZXJtaW5hdGluZ05vZGVzLmZvckVhY2gobm9kZSA9PiB0ZXJtaW5hdGluZ05vZGVNYXBbbm9kZS5pZF0gPSBub2RlKTtcbiAgLyogRmxvb2QgZmlsbDogV2hpbGUgdGhlICd0byB2aXNpdCcgc3RhY2sgaXMgbm90IGVtcHR5LCBwb3AgYSBub2RlIG9mZiBvZiBpdC5cbiAgICogSWYgdGhlIG5vZGUgaGFzIG5vdCB5ZXQgYmVlbiB2aXNpdGVkLCBhZGQgaXQgdG8gdGhlIHNldCwgbWFyayBpdCBhcyBzZWVuLFxuICAgKiBhbmQgZW5xdWV1ZSBhbGwgb2YgaXRzIGFuY2VzdG9yIChpbnB1dCkgbm9kZXMuICovXG4gIHdoaWxlICh2aXNpdC5sZW5ndGggIT09IDApIHtcbiAgICBjb25zdCBjdXIgPSB2aXNpdC5wb3AoKSE7XG4gICAgaWYgKHNlZW5bY3VyLmlkXSA9PSBudWxsKSB7XG4gICAgICBpZiAodGVybWluYXRpbmdOb2RlTWFwW2N1ci5pZF0gPT0gbnVsbCkge1xuICAgICAgICBPYmplY3Qua2V5cyhjdXIuaW5wdXRzKVxuICAgICAgICAgICAgLm1hcChpbnB1dE5hbWUgPT4gY3VyLmlucHV0c1tpbnB1dE5hbWVdKVxuICAgICAgICAgICAgLmZvckVhY2goaW5wdXQgPT4gdmlzaXQucHVzaChpbnB1dC5ub2RlKSk7XG4gICAgICB9XG4gICAgICBzZXQucHVzaChjdXIpO1xuICAgICAgc2VlbltjdXIuaWRdID0gY3VyO1xuICAgIH1cbiAgfVxuICByZXR1cm4gc2V0O1xufVxuXG4vKipcbiAqIEdpdmVuIGEgc2V0IG9mIG5vZGVzLCBjb21wdXRlIHRoZWlyIG9yZGVyIHN1Y2ggdGhhdCBhbGwgZGVwZW5kZW50IG5vZGVzIGFyZVxuICogZXZhbHVhdGVkIGFmdGVyIHRoZWlyIGRlcGVuZGVlcy4gVGhpcyBpcyB0aGUgJ2luZmVyZW5jZSBvcmRlcicgZm9yIG5vZGVzIGluXG4gKiB0aGUgb3BlcmF0aW9uIGdyYXBoLlxuICogQHBhcmFtIHVub3JkZXJlZEV2YWx1YXRpb25TZXQgVGhlIHVub3JkZXJlZCBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvIGJlXG4gKiBldmFsdWF0ZWQuXG4gKiBAcmV0dXJuIFRoZSBpbnB1dCBub2RlcyBpbiBmb3J3YXJkIGV2YWx1YXRpb24gb3JkZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRlcmVkRXZhbHVhdGlvblNldCh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0OiBOb2RlW10pOlxuICAgIE5vZGVbXSB7XG4gIC8qIEEgcHJpb3JpdHkgcXVldWUgaXMgdXNlZCwgd2hlcmUgdGhlIHByaW9yaXR5IGlzIHRoZSByZW1haW5pbmcgbnVtYmVyIG9mXG4gICAqIHVuZXZhbHVhdGVkIG5vZGVzIHdob3NlIGlucHV0cyBjb21lIGZyb20gdGhlIGVsZW1lbnQgbm9kZS4gVGhpcyBndWFyYW50ZWVzXG4gICAqIHRoYXQgYWxsIGRvd25zdHJlYW0gbm9kZXMgd2lsbCBiZSBkZXF1ZXVlZCBiZWZvcmUgdGhlaXIgYW5jZXN0b3JzLiAqL1xuICBjb25zdCBzZXQ6IE5vZGVbXSA9IFtdO1xuICBjb25zdCBub2RlSW5kaWNlczoge1tpZDogbnVtYmVyXTogbnVtYmVyfSA9IHt9O1xuICBjb25zdCBwZW5kaW5nRGVwZW5kZW5jaWVzOiB7W2lkOiBudW1iZXJdOiBudW1iZXJ9ID0ge307XG5cbiAgLyogVGhlIHF1ZXVlIHByaW9yaXR5IGNhbGxiYWNrIGxvb2tzIGF0IHRoZSBudW1iZXIgb2YgcGVuZGluZyBkZXBlbmRlbmNpZXMgb2ZcbiAgICogYSBnaXZlbiBub2RlLiBUaGUgcXVldWUgaW5kZXggb2JzZXJ2ZXIgY2FsbGJhY2sgbWFpbnRhaW5zIHRoZSBsb2NhdGlvbiBvZlxuICAgKiBlYWNoIG5vZGUgaW4gdGhlIGFycmF5LCBmb3IgcHJpb3JpdHkgdXBkYXRlcy4gKi9cbiAgY29uc3Qgbm9kZVF1ZXVlID0gbmV3IFByaW9yaXR5UXVldWU8Tm9kZT4oXG4gICAgICAoYTogTm9kZSwgYjogTm9kZSkgPT4gcHJpb3JpdHlfcXVldWUuZGVmYXVsdENvbXBhcmUoXG4gICAgICAgICAgcGVuZGluZ0RlcGVuZGVuY2llc1thLmlkXSwgcGVuZGluZ0RlcGVuZGVuY2llc1tiLmlkXSksXG4gICAgICAobm9kZTogTm9kZSwgbmV3SW5kZXg6IG51bWJlcikgPT4gbm9kZUluZGljZXNbbm9kZS5pZF0gPSBuZXdJbmRleCk7XG5cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4gcGVuZGluZ0RlcGVuZGVuY2llc1tub2RlLmlkXSA9IDApO1xuXG4gIC8qIEZvciBldmVyeSBkZXNjZW5kZW50IG9mIGEgbm9kZSAob3V0cHV0IG9mIGFuY2VzdG9yIGlzIGlucHV0IHRvIGRlc2NlbmRhbnQpLFxuICAgKiBpbmNyZW1lbnQgdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIGZvciB0aGUgYW5jZXN0b3IuIFRoaXMgcHJlcGFyZXNcbiAgICogdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIGFzIGEgcHJpb3JpdHkgbWFwLiAqL1xuICB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmZvckVhY2goXG4gICAgICBub2RlID0+IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKVxuICAgICAgICAgICAgICAgICAgLm1hcChrZXkgPT4gbm9kZS5pbnB1dHNba2V5XSlcbiAgICAgICAgICAgICAgICAgIC5mb3JFYWNoKGlucHV0ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHVub3JkZXJlZEV2YWx1YXRpb25TZXQuaW5kZXhPZihpbnB1dC5ub2RlKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgICBwZW5kaW5nRGVwZW5kZW5jaWVzW2lucHV0Lm5vZGUuaWRdKys7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH0pKTtcblxuICB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiBub2RlUXVldWUuZW5xdWV1ZShub2RlKSk7XG5cbiAgd2hpbGUgKCFub2RlUXVldWUuZW1wdHkoKSkge1xuICAgIHNldC51bnNoaWZ0KG5vZGVRdWV1ZS5kZXF1ZXVlKCkpO1xuICAgIC8qIEFzIGVhY2ggbm9kZSBpcyB2aXNpdGVkLCBkZWNyZW1lbnQgdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIG9mXG4gICAgICogZWFjaCBhbmNlc3RvciwgYW5kIHRlbGwgdGhlIHByaW9yaXR5IHF1ZXVlIHRoYXQgdGhlIHByaW9yaXR5IGhhcyBjaGFuZ2VkLlxuICAgICAqL1xuICAgIE9iamVjdC5rZXlzKHNldFswXS5pbnB1dHMpLm1hcChrZXkgPT4gc2V0WzBdLmlucHV0c1trZXldKS5mb3JFYWNoKGlucHV0ID0+IHtcbiAgICAgIGlmICh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmluZGV4T2YoaW5wdXQubm9kZSkgPT09IC0xKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHBlbmRpbmdEZXBlbmRlbmNpZXNbaW5wdXQubm9kZS5pZF0tLTtcbiAgICAgIG5vZGVRdWV1ZS51cGRhdGUoaW5wdXQubm9kZSwgbm9kZUluZGljZXNbaW5wdXQubm9kZS5pZF0pO1xuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIHNldDtcbn1cblxuLyoqXG4gKiBAcmV0dXJuIFRydWUgaWZmIHRoZSBub2RlIGlzIGFuIGlucHV0IG5vZGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0lucHV0Tm9kZShub2RlOiBOb2RlKTogYm9vbGVhbiB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhub2RlLmlucHV0cykubGVuZ3RoID09PSAwO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkQmFja1Byb3AodDogVGVuc29yKTogYm9vbGVhbiB7XG4gIHJldHVybiAhKHQubm9kZSBpbnN0YW5jZW9mIENvbnN0YW50Tm9kZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1Bhc3N0aHJvdWdoTm9kZShub2RlOiBOb2RlLCBtYXA6IFRlbnNvckFycmF5TWFwKTogYm9vbGVhbiB7XG4gIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhub2RlLmlucHV0cyk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGlucHV0ID0gbm9kZS5pbnB1dHNba2V5c1tpXV07XG4gICAgaWYgKG1hcC5nZXQoaW5wdXQsIHRydWUpID09PSBtYXAuZ2V0KG5vZGUub3V0cHV0LCB0cnVlKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0ICogYXMgZ3BncHVfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyByZW5kZXJfbmRhcnJheV9ncHVfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvd2ViZ2xfdXRpbCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbmV4cG9ydCB7Q2hlY2twb2ludExvYWRlcn0gZnJvbSAnLi9jaGVja3BvaW50X2xvYWRlcic7XG5leHBvcnQge0RhdGFTdGF0cywgSW5NZW1vcnlEYXRhc2V0fSBmcm9tICcuL2RhdGFzZXQnO1xuZXhwb3J0IHtHcmFwaCwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmV4cG9ydCB7R3JhcGhSdW5uZXIsIEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlciwgTWV0cmljUmVkdWN0aW9ufSBmcm9tICcuL2dyYXBoX3J1bm5lcic7XG5leHBvcnQge0NvbnN0YW50SW5pdGlhbGl6ZXIsIEluaXRpYWxpemVyLCBOREFycmF5SW5pdGlhbGl6ZXIsIE9uZXNJbml0aWFsaXplciwgUmFuZG9tTm9ybWFsSW5pdGlhbGl6ZXIsIFJhbmRvbVRydW5jYXRlZE5vcm1hbEluaXRpYWxpemVyLCBSYW5kb21Vbmlmb3JtSW5pdGlhbGl6ZXIsIFZhcmlhbmNlU2NhbGluZ0luaXRpYWxpemVyLCBaZXJvc0luaXRpYWxpemVyfSBmcm9tICcuL2luaXRpYWxpemVycyc7XG5leHBvcnQge0luQ1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciwgSW5HUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyLCBJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmV4cG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5leHBvcnQge05EQXJyYXlNYXRoQ1BVfSBmcm9tICcuL21hdGgvbWF0aF9jcHUnO1xuZXhwb3J0IHtOREFycmF5TWF0aEdQVX0gZnJvbSAnLi9tYXRoL21hdGhfZ3B1JztcbmV4cG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgQXJyYXkzRCwgQXJyYXk0RCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5leHBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuZXhwb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmV4cG9ydCB7Q29zdFJlZHVjdGlvbiwgRmVlZEVudHJ5LCBTZXNzaW9ufSBmcm9tICcuL3Nlc3Npb24nO1xuZXhwb3J0IHtTR0RPcHRpbWl6ZXJ9IGZyb20gJy4vc2dkX29wdGltaXplcic7XG4vLyBTZWNvbmQgbGV2ZWwgZXhwb3J0cy5cbmV4cG9ydCB7Y29udl91dGlsLCBncGdwdV91dGlsLCByZW5kZXJfbmRhcnJheV9ncHVfdXRpbCwgdXRpbCwgd2ViZ2xfdXRpbH07XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIEluaXRpYWxpemVyIGludGVyZmFjZSwgYWxsIGluaXRpYWxpemVyIGltcGxlbWVudCB0aGlzIGludGVyZmFjZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbml0aWFsaXplciB7XG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXk7XG59XG5cbmV4cG9ydCBjbGFzcyBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHNjYWxlID0gMS4wLFxuICAgICAgcHJpdmF0ZSBtb2RlOiAnZmFuX2luJ3wnZmFuX291dCd8J2Zhbl9hdmcnID0gJ2Zhbl9pbicsXG4gICAgICBwcml2YXRlIGRpc3RyaWJ1dGlvbjogJ3VuaWZvcm0nfCdub3JtYWwnID0gJ25vcm1hbCcpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgbGV0IG4gPSAwO1xuICAgIGlmICh0aGlzLm1vZGUgPT09ICdmYW5faW4nKSB7XG4gICAgICBuID0gaW5wdXRVbml0cztcbiAgICB9IGVsc2UgaWYgKHRoaXMubW9kZSA9PT0gJ2Zhbl9vdXQnKSB7XG4gICAgICBuID0gb3V0cHV0VW5pdHM7XG4gICAgfSBlbHNlIGlmICh0aGlzLm1vZGUgPT09ICdmYW5fYXZnJykge1xuICAgICAgbiA9IChpbnB1dFVuaXRzICsgb3V0cHV0VW5pdHMpIC8gMjtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdVbmV4cGVjdGVkIG1vZGUgZm9yIHZhcmlhbmNlIHNjYWxpbmcgaW5pdGlhbGl6ZXI6ICcgKyB0aGlzLm1vZGUpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmRpc3RyaWJ1dGlvbiA9PT0gJ25vcm1hbCcpIHtcbiAgICAgIHJldHVybiBOREFycmF5LnJhbmRUcnVuY2F0ZWROb3JtYWwoXG4gICAgICAgICAgd2VpZ2h0c1NoYXBlLCAwLjAsIE1hdGguc3FydCh0aGlzLnNjYWxlIC8gbikpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5kaXN0cmlidXRpb24gPT09ICd1bmlmb3JtJykge1xuICAgICAgcmV0dXJuIE5EQXJyYXkucmFuZFVuaWZvcm0oXG4gICAgICAgICAgd2VpZ2h0c1NoYXBlLCAwLjAsIE1hdGguc3FydCgzICogdGhpcy5zY2FsZSAvIG4pKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdVbmV4cGVjdGVkIGRpc3RyaWJ1dGlvbiBmb3IgdmFyaWFuY2Ugc2NhbGluZyBpbml0aWFsaXplcjogJyArXG4gICAgICAgICAgdGhpcy5kaXN0cmlidXRpb24pO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgWmVyb3NJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIE9uZXNJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIGNvbnN0IHZhbHVlcyA9IE5EQXJyYXkuemVyb3Mod2VpZ2h0c1NoYXBlKTtcbiAgICB2YWx1ZXMuZmlsbCgxKTtcbiAgICByZXR1cm4gdmFsdWVzO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDb25zdGFudEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHZhbHVlID0gMCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gICAgdmFsdWVzLmZpbGwodGhpcy52YWx1ZSk7XG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheUluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG5kYXJyYXk6IE5EQXJyYXkpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMubmRhcnJheTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmFuZG9tTm9ybWFsSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbWVhbiA9IDAsIHByaXZhdGUgc3RkZXYgPSAuMDUpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZE5vcm1hbCh3ZWlnaHRzU2hhcGUsIHRoaXMubWVhbiwgdGhpcy5zdGRldik7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFJhbmRvbVRydW5jYXRlZE5vcm1hbEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1lYW4gPSAwLCBwcml2YXRlIHN0ZGV2ID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmRUcnVuY2F0ZWROb3JtYWwod2VpZ2h0c1NoYXBlLCB0aGlzLm1lYW4sIHRoaXMuc3RkZXYpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSYW5kb21Vbmlmb3JtSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbWludmFsID0gLS4wNSwgcHJpdmF0ZSBtYXh2YWwgPSAuMDUpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZFVuaWZvcm0od2VpZ2h0c1NoYXBlLCB0aGlzLm1pbnZhbCwgdGhpcy5tYXh2YWwpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIFRoZSBpbnRlcmZhY2UgZm9yIGlucHV0IHByb3ZpZGVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbnB1dFByb3ZpZGVyIHtcbiAgLyoqXG4gICAqIEdldCB0aGUgbmV4dCBpbnB1dCBhcyBhIGNvcHkuIFRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2UgdGhlIGRhdGEgbWlnaHRcbiAgICogZ2V0IHVwbG9hZGVkIHRvIHRoZSBHUFUgYW5kIG1vZGlmeSB0aGUgb3JpZ2luYWwgZGF0YS5cbiAgICogQHBhcmFtIG1hdGggTkRBcnJheU1hdGhcbiAgICovXG4gIGdldE5leHRDb3B5KG1hdGg6IE5EQXJyYXlNYXRoKTogTkRBcnJheTtcbiAgLyoqXG4gICAqIERpc3Bvc2UgdGhlIGlucHV0IGNvcHkuXG4gICAqIEBwYXJhbSBtYXRoIE5EQXJyYXlNYXRoXG4gICAqIEBwYXJhbSBjb3B5IFRoZSBjb3B5IHByb3ZpZGVkIGZyb20gZ2V0TmV4dENvcHlcbiAgICovXG4gIGRpc3Bvc2VDb3B5KG1hdGg6IE5EQXJyYXlNYXRoLCBjb3B5OiBOREFycmF5KTogdm9pZDtcbn1cblxuLyoqXG4gKiBBIGNvbW1vbiBpbnRlcmZhY2UgZm9yIHNodWZmbGVkIGlucHV0IHByb3ZpZGVyIGJ1aWxkZXJzLiBUaGlzIHJldHVybnNcbiAqIElucHV0UHJvdmlkZXJzIHRoYXQgYXJlIHN5bmNocm9uaXplZC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIHtcbiAgZ2V0SW5wdXRQcm92aWRlcnMoKTogSW5wdXRQcm92aWRlcltdO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciBpbXBsZW1lbnRzXG4gICAgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIHByb3RlY3RlZCBzaHVmZmxlZEluZGljZXM6IFVpbnQzMkFycmF5O1xuICBwcm90ZWN0ZWQgbnVtSW5wdXRzOiBudW1iZXI7XG5cbiAgcHJvdGVjdGVkIGlkeCA9IDA7XG4gIC8vIENvdW50ZXIgZm9yIGhvdyBtYW55IHRpbWVzIHRoZSBjdXJyZW50IGluZGV4IGhhcyBiZWVuIGNhbGxlZC4gUmVzZXRzIHRvIDBcbiAgLy8gd2hlbiBpdCByZWFjaGVzIHRoZSBudW1iZXIgb2YgaW5wdXRzLlxuICBwcm90ZWN0ZWQgaW5wdXRDb3VudGVyID0gMDtcbiAgcHJvdGVjdGVkIGVwb2NoID0gMDtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhbiBgSW5NZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJgLiBBbGwgb2YgdGhlIGlucHV0cyBtdXN0IGJlXG4gICAqIGluIG1lbW9yeS5cbiAgICogQHBhcmFtIGlucHV0cyBBbGwgb2YgdGhlIGlucHV0cywgc2l6ZTogW251bWJlciBvZiBpbnB1dHNdW251bWJlciBvZlxuICAgKiBleGFtcGxlc10uXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgaW5wdXRzOiBOREFycmF5W11bXSkge1xuICAgIHRoaXMuc2h1ZmZsZWRJbmRpY2VzID0gdXRpbC5jcmVhdGVTaHVmZmxlZEluZGljZXMoaW5wdXRzWzBdLmxlbmd0aCk7XG4gICAgdGhpcy5udW1JbnB1dHMgPSBpbnB1dHMubGVuZ3RoO1xuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBudW1iZXIgb2YgZXhhbXBsZXMgaW4gZWFjaCBpbnB1dCBtYXRjaGVzLlxuICAgIGNvbnN0IG51bUV4YW1wbGVzID0gdGhpcy5pbnB1dHNbMF0ubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1JbnB1dHM7IGkrKykge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy5pbnB1dHNbaV0ubGVuZ3RoID09PSBudW1FeGFtcGxlcyxcbiAgICAgICAgICAnTnVtYmVyIG9mIGV4YW1wbGVzIG11c3QgbWF0Y2ggYWNyb3NzIGRpZmZlcmVudCBpbnB1dHMuJyk7XG4gICAgfVxuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBzaGFwZXMgd2l0aGluIGlucHV0cyBhbGwgbWF0Y2guXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bUlucHV0czsgaSsrKSB7XG4gICAgICBjb25zdCBpbnB1dFNoYXBlID0gdGhpcy5pbnB1dHNbaV1bMF0uc2hhcGU7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRzW2ldLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goaW5wdXRTaGFwZSwgdGhpcy5pbnB1dHNbaV1bal0uc2hhcGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBnZXRDdXJyZW50RXhhbXBsZUluZGV4KCk6IG51bWJlciB7XG4gICAgY29uc3QgcmV0dXJuSWR4ID0gdGhpcy5pZHg7XG5cbiAgICB0aGlzLmlucHV0Q291bnRlcisrO1xuICAgIGlmICh0aGlzLmlucHV0Q291bnRlciA+PSB0aGlzLm51bUlucHV0cykge1xuICAgICAgdGhpcy5pZHgrKztcbiAgICAgIHRoaXMuaW5wdXRDb3VudGVyID0gMDtcblxuICAgICAgaWYgKHRoaXMuaWR4ID49IHRoaXMuaW5wdXRzWzBdLmxlbmd0aCkge1xuICAgICAgICB0aGlzLmlkeCA9IDA7XG4gICAgICAgIHRoaXMuZXBvY2grKztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJldHVybklkeDtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZXROZXh0SW5wdXQoaW5wdXRJZDogbnVtYmVyKTogTkRBcnJheSB7XG4gICAgY29uc3QgY3VycmVudEV4YW1wbGVJbmRleCA9IHRoaXMuZ2V0Q3VycmVudEV4YW1wbGVJbmRleCgpO1xuXG4gICAgcmV0dXJuIHRoaXMuaW5wdXRzW2lucHV0SWRdW3RoaXMuc2h1ZmZsZWRJbmRpY2VzW2N1cnJlbnRFeGFtcGxlSW5kZXhdXTtcbiAgfVxuXG4gIGdldEVwb2NoKCkge1xuICAgIHJldHVybiB0aGlzLmVwb2NoO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaW5wdXQgcHJvdmlkZXJzIHdoaWNoIHNodWZmbGUgdGhlIGlucHV0cyBhbmQgc3RheSBpbiBzeW5jLlxuICAgKi9cbiAgZ2V0SW5wdXRQcm92aWRlcnMoKTogSW5wdXRQcm92aWRlcltdIHtcbiAgICBjb25zdCBpbnB1dFByb3ZpZGVyczogSW5wdXRQcm92aWRlcltdID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubnVtSW5wdXRzOyBpKyspIHtcbiAgICAgIGlucHV0UHJvdmlkZXJzLnB1c2godGhpcy5nZXRJbnB1dFByb3ZpZGVyKGkpKTtcbiAgICB9XG4gICAgcmV0dXJuIGlucHV0UHJvdmlkZXJzO1xuICB9XG5cbiAgYWJzdHJhY3QgZ2V0SW5wdXRQcm92aWRlcihpbnB1dElkOiBudW1iZXIpOiBJbnB1dFByb3ZpZGVyO1xufVxuXG4vKipcbiAqIEFuIGluIENQVSBtZW1vcnkgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB0aGF0IHNodWZmbGVzIE5EQXJyYXlzIG9uIHRoZVxuICogQ1BVIGFuZCBrZWVwcyB0aGVtIG11dHVhbGx5IGluIHN5bmMuXG4gKi9cbmV4cG9ydCBjbGFzcyBJbkNQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgZXh0ZW5kc1xuICAgIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKSB7XG4gICAgY29uc3Qgc2h1ZmZsZWRJbnB1dFByb3ZpZGVyID0gdGhpcztcblxuICAgIHJldHVybiB7XG4gICAgICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXkge1xuICAgICAgICByZXR1cm4gTkRBcnJheS5saWtlKHNodWZmbGVkSW5wdXRQcm92aWRlci5nZXROZXh0SW5wdXQoaW5wdXRJZCkpO1xuICAgICAgfSxcbiAgICAgIGRpc3Bvc2VDb3B5KG1hdGg6IE5EQXJyYXlNYXRoLCBjb3B5OiBOREFycmF5KSB7XG4gICAgICAgIGNvcHkuZGlzcG9zZSgpO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn1cblxuLyoqXG4gKiBBbiBpbiBHUFUgbWVtb3J5IFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgdGhhdCBzaHVmZmxlcyBOREFycmF5cyBvbiB0aGVcbiAqIEdQVSBhbmQga2VlcHMgdGhlbSBtdXR1YWxseSBpbiBzeW5jLiBUaGlzIGlzIG1vcmUgcGVyZm9ybWFudCB0aGFuIHRoZSBDUFVcbiAqIHZlcnNpb24gYXMgdGV4dHVyZXMgd2lsbCBzdGF5IGluIG1lbW9yeSwgaG93ZXZlciB0aGlzIGlzIG1vcmUgR1BVIG1lbW9yeVxuICogaW50ZW5zaXZlIGFzIGl0IGtlZXBzIHRleHR1cmVzIHJlc2lkZW50IGluIEdQVSBtZW1vcnkuXG4gKi9cbmV4cG9ydCBjbGFzcyBJbkdQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgZXh0ZW5kc1xuICAgIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKSB7XG4gICAgY29uc3Qgc2h1ZmZsZWRJbnB1dFByb3ZpZGVyID0gdGhpcztcblxuICAgIHJldHVybiB7XG4gICAgICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXkge1xuICAgICAgICByZXR1cm4gbWF0aC5jbG9uZShzaHVmZmxlZElucHV0UHJvdmlkZXIuZ2V0TmV4dElucHV0KGlucHV0SWQpKTtcbiAgICAgIH0sXG4gICAgICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSkge1xuICAgICAgICBjb3B5LmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuLyoqIEEgbm9kZSdzIGFjdGl2YXRpb24gZnVuY3Rpb24gYW5kIGl0cyBkZXJpdmF0aXZlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQ8VCBleHRlbmRzIE5EQXJyYXk+KG1hdGg6IE5EQXJyYXlNYXRoLCBpbnB1dDogVCk6IFQ7XG4gIGRlcjxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIGlucHV0OiBULCBvdXRwdXQ6IFQpOiBUO1xufVxuXG5leHBvcnQgY2xhc3MgVGFuSEZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC50YW5oKHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgY29uc3QgeVNxdWFyZWQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHksIHkpO1xuICAgICAgLy8gMSAtIHleMi5cbiAgICAgIHJldHVybiBtYXRoLnNjYWxhck1pbnVzQXJyYXkoU2NhbGFyLk9ORSwgeVNxdWFyZWQpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSZUxVRnVuYyBpbXBsZW1lbnRzIEFjdGl2YXRpb25GdW5jdGlvbiB7XG4gIG91dHB1dChtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIHJldHVybiBtYXRoLnJlbHUoeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5zdGVwKHgpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTaWdtb2lkRnVuYyBpbXBsZW1lbnRzIEFjdGl2YXRpb25GdW5jdGlvbiB7XG4gIG91dHB1dChtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIHJldHVybiBtYXRoLnNpZ21vaWQoeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICAvLyB5ICogKDEgLSB5KSA9IHkgLSB5XjJcbiAgICAgIGNvbnN0IHlTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh5LCB5KTtcbiAgICAgIHJldHVybiBtYXRoLnN1Yih5LCB5U3F1YXJlZCk7XG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNxdWFyZUZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5lbGVtZW50V2lzZU11bCh4LCB4KTtcbiAgICB9KTtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSwgeTogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIC8vIGR5L2R4ID0gMip4LlxuICAgICAgcmV0dXJuIG1hdGguc2NhbGFyVGltZXNBcnJheShTY2FsYXIuVFdPLCB4KTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0Q29uY2F0M0RTaGFwZXNNYXRjaChcbiAgICB4MVNoYXBlOiBudW1iZXJbXSwgeDJTaGFwZTogbnVtYmVyW10sIGF4aXM6IG51bWJlcixcbiAgICBlcnJvck1lc3NhZ2VQcmVmaXggPSAnJykge1xuICB1dGlsLmFzc2VydChcbiAgICAgIHgxU2hhcGUubGVuZ3RoID09PSAzLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgJ0NvbmNhdDNEIHgxIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgeDJTaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICBlcnJvck1lc3NhZ2VQcmVmaXggKyAnQ29uY2F0M0QgeDIgc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcblxuICB1dGlsLmFzc2VydChcbiAgICAgIGF4aXMgPj0gMCAmJiBheGlzIDwgMywgJ0F4aXMgZm9yIGNvbmNhdDNEIG11c3QgYmUgYmV0d2VlbiAwIGFuZCAyLicpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgMzsgaSsrKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIChpID09PSBheGlzKSB8fCAoeDFTaGFwZVtpXSA9PT0geDJTaGFwZVtpXSksXG4gICAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArXG4gICAgICAgICAgICBgU2hhcGUgKCR7eDFTaGFwZX0pIGRvZXMgbm90IG1hdGNoICgke3gyU2hhcGV9KSBhbG9uZyBgICtcbiAgICAgICAgICAgIGBub24tY29uY2F0ZW5hdGVkIGF4aXMuYCk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVDb25jYXQzRE91dHB1dFNoYXBlKFxuICAgIHgxU2hhcGU6IG51bWJlcltdLCB4MlNoYXBlOiBudW1iZXJbXSxcbiAgICBheGlzOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICB1dGlsLmFzc2VydCh4MVNoYXBlLmxlbmd0aCA9PT0gMywgJ0NvbmNhdDNEIHgxIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG4gIHV0aWwuYXNzZXJ0KHgyU2hhcGUubGVuZ3RoID09PSAzLCAnQ29uY2F0M0QgeDJzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuXG4gIGNvbnN0IG91dHB1dFNoYXBlID0geDFTaGFwZS5zbGljZSgpO1xuICBvdXRwdXRTaGFwZVtheGlzXSArPSB4MlNoYXBlW2F4aXNdO1xuICByZXR1cm4gb3V0cHV0U2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgIGlucHV0U2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemU6IG51bWJlcixcbiAgICBkZXB0aDogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZD86IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gIGlmICh6ZXJvUGFkID09IG51bGwpIHtcbiAgICB6ZXJvUGFkID0gY29tcHV0ZURlZmF1bHRQYWQoaW5wdXRTaGFwZVJvd0NvbERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSk7XG4gIH1cbiAgY29uc3QgaW5wdXRSb3dzID0gaW5wdXRTaGFwZVJvd0NvbERlcHRoWzBdO1xuICBjb25zdCBpbnB1dENvbHMgPSBpbnB1dFNoYXBlUm93Q29sRGVwdGhbMV07XG4gIGNvbnN0IG91dHB1dFJvd3MgPSAoaW5wdXRSb3dzIC0gZmllbGRTaXplICsgMiAqIHplcm9QYWQpIC8gc3RyaWRlICsgMTtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB1dGlsLmlzSW50KG91dHB1dFJvd3MpLFxuICAgICAgYFRoZSBvdXRwdXQgIyBvZiByb3dzICgke291dHB1dFJvd3N9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSB0aGUgYCArXG4gICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuXG4gIGNvbnN0IG91dHB1dENvbHMgPSAoaW5wdXRDb2xzIC0gZmllbGRTaXplICsgMiAqIHplcm9QYWQpIC8gc3RyaWRlICsgMTtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB1dGlsLmlzSW50KG91dHB1dENvbHMpLFxuICAgICAgYFRoZSBvdXRwdXQgIyBvZiBjb2x1bW5zICgke291dHB1dENvbHN9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSBgICtcbiAgICAgICAgICBgdGhlIHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuXG4gIHJldHVybiBbb3V0cHV0Um93cywgb3V0cHV0Q29scywgZGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgaW5wdXRTaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemU6IG51bWJlcixcbiAgICBzdHJpZGU6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLmZsb29yKChpbnB1dFNoYXBlWzBdICogKHN0cmlkZSAtIDEpIC0gc3RyaWRlICsgZmllbGRTaXplKSAvIDIpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZVRleFNoYXBlRnJvbTNEKFxuICAgIHNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW3NoYXBlUm93Q29sRGVwdGhbMF0sIHNoYXBlUm93Q29sRGVwdGhbMV0gKiBzaGFwZVJvd0NvbERlcHRoWzJdXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVXZWlnaHRzU2hhcGU0RChcbiAgICBpbnB1dERlcHRoOiBudW1iZXIsIG91dHB1dERlcHRoOiBudW1iZXIsXG4gICAgZlNpemU6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtmU2l6ZSwgZlNpemUsIGlucHV0RGVwdGgsIG91dHB1dERlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgaW5wdXREZXB0aDogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIGZpZWxkU2l6ZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbZmllbGRTaXplICogZmllbGRTaXplICogaW5wdXREZXB0aCwgb3V0cHV0RGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFsxLCBvdXRwdXREZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlRGlsYXRlZFJDKFxuICAgIHJjOiBbbnVtYmVyLCBudW1iZXJdLCBvcmlnU3RyaWRlOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3Qgcm93c0RpbGF0ZWQgPSAocmNbMF0gLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICBjb25zdCBjb2xzRGlsYXRlZCA9IChyY1sxXSAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gIHJldHVybiBbcm93c0RpbGF0ZWQsIGNvbHNEaWxhdGVkXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlU2hhcGVzKFxuICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGNvbnN0IHNyY0FyZWEgPSBzb3VyY2VTaXplWzBdICogc291cmNlU2l6ZVsxXTtcbiAgY29uc3QgZHN0QXJlYSA9IGRlc3RTaXplWzBdICogZGVzdFNpemVbMV07XG4gIGlmIChzcmNBcmVhICE9PSBkc3RBcmVhKSB7XG4gICAgY29uc3Qgc3JjU3RyID0gJ1snICsgc291cmNlU2l6ZVswXSArICcsICcgKyBzb3VyY2VTaXplWzFdICsgJ10nO1xuICAgIGNvbnN0IGRzdFN0ciA9ICdbJyArIGRlc3RTaXplWzBdICsgJywgJyArIGRlc3RTaXplWzFdICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ2NvcHkyRCBzaGFwZXMgaGF2ZSBkaWZmZXJlbnQgYXJlYXM6XFxuICBzb3VyY2VTaXplICcgKyBzcmNTdHIgK1xuICAgICAgICAnLCBhcmVhICcgKyBzcmNBcmVhICsgJ1xcbiAgZGVzdFNpemUgJyArIGRzdFN0ciArICcsIGFyZWEgJyArIGRzdEFyZWEpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuLyoqXG4gKiBBbiBlcnJvciBmdW5jdGlvbiBhbmQgaXRzIGRlcml2YXRpdmUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24ge1xuICBjb3N0PFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgeDE6IFQsIHgyOiBUKTogVDtcbiAgZGVyPFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgeDE6IFQsIHgyOiBUKTogVDtcbiAgZGlzcG9zZSgpOiB2b2lkO1xufVxuXG5leHBvcnQgY2xhc3MgU3F1YXJlQ29zdEZ1bmMgaW1wbGVtZW50cyBFbGVtZW50V2lzZUNvc3RGdW5jdGlvbiB7XG4gIHByaXZhdGUgaGFsZk9uZSA9IFNjYWxhci5uZXcoMC41KTtcblxuICBjb3N0KG1hdGg6IE5EQXJyYXlNYXRoLCB4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBOREFycmF5IHtcbiAgICBjb25zdCBkaWZmID0gbWF0aC5zdWIoeDEsIHgyKTtcbiAgICBjb25zdCBkaWZmU3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoZGlmZiwgZGlmZik7XG4gICAgY29uc3QgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHRoaXMuaGFsZk9uZSwgZGlmZlNxdWFyZWQpO1xuXG4gICAgZGlmZi5kaXNwb3NlKCk7XG4gICAgZGlmZlNxdWFyZWQuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogTkRBcnJheSB7XG4gICAgcmV0dXJuIG1hdGguc3ViKHgxLCB4Mik7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIHRoaXMuaGFsZk9uZS5kaXNwb3NlKCk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyZF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuXG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IHR5cGUgU2NvcGVSZXN1bHQgPSBOREFycmF5W118TkRBcnJheXx2b2lkO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTkRBcnJheU1hdGgge1xuICBwcml2YXRlIG5kYXJyYXlTY29wZXM6IE5EQXJyYXlbXVtdID0gW107XG4gIHByaXZhdGUgYWN0aXZlU2NvcGU6IE5EQXJyYXlbXTtcblxuICBwcml2YXRlIG5kYXJyYXlzVG9LZWVwOiBOREFycmF5W11bXSA9IFtdO1xuICBwcml2YXRlIGFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXA6IE5EQXJyYXlbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gc2FmZU1vZGUgSW4gc2FmZSBtb2RlLCB5b3UgbXVzdCB1c2UgbWF0aCBvcGVyYXRpb25zIGluc2lkZVxuICAgKiBhIG1hdGguc2NvcGUoKSB3aGljaCB3aWxsIGF1dG9tYXRpY2FsbHkgY2xlYW4gdXAgaW50ZXJtZWRpYXRlIE5EQXJyYXlzLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBzYWZlTW9kZTogYm9vbGVhbikge31cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IG1hdGggc2NvcGUuIFB1dCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucyBpbnNpZGUgYSBzY29wZVxuICAgKiBmdW5jdGlvbiBjbG9zdXJlIHNvIHRoYXQgdGhlIGxpYnJhcnkgYXV0b21hdGljYWxseSBjbGVhbnMgdXAgTkRBcnJheXNcbiAgICogZnJvbSBpbnRlcm1lZGlhdGUgbWF0aCBvcGVyYXRpb25zLiBZb3UgbXVzdCBjcmVhdGUgYSBzY29wZSBpbiBzYWZlIG1vZGVcbiAgICogdG8gY2FsbCBtYXRoIG9wZXJhdGlvbnMuIElmIGEgcmVzdWx0IGlzIHJldHVybmVkIGZyb20gdGhlIHNjb3BlLCBpdCB3aWxsXG4gICAqIGFsc28gYmUgdHJhY2tlZCwgd2hpY2ggbWVhbnMgdGhlcmUgbXVzdCBiZSB5ZXQgYW5vdGhlciB3cmFwcGluZyBzY29wZS5cbiAgICogQHBhcmFtIHNjb3BlRm4gVGhlIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgd2l0aCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucy5cbiAgICovXG4gIHNjb3BlPFQgZXh0ZW5kcyBTY29wZVJlc3VsdD4oXG4gICAgICBzY29wZUZuOlxuICAgICAgICAgIChrZWVwOiA8VDEgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMSkgPT4gVDEsXG4gICAgICAgICAgIHRyYWNrOiA8VDIgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMikgPT4gVDIpID0+IFQpIHtcbiAgICB0aGlzLnN0YXJ0U2NvcGUoKTtcblxuICAgIGNvbnN0IGtlZXBGbiA9IDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQgPT4gdGhpcy5rZWVwKG5kYXJyYXkpO1xuICAgIGNvbnN0IHRyYWNrRm4gPSA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUID0+IHRoaXMudHJhY2sobmRhcnJheSk7XG4gICAgY29uc3QgcmVzdWx0ID0gc2NvcGVGbihrZWVwRm4sIHRyYWNrRm4pO1xuXG4gICAgdGhpcy5lbmRTY29wZShyZXN1bHQpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIGVuZFNjb3BlKCkgdG8gYWNoaWV2ZSB0aGUgc2FtZSBmdW5jdGlvbmFsaXR5XG4gICAqIGFzIHNjb3BlKCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgYSBmdW5jdGlvbiBjbG9zdXJlLlxuICAgKi9cbiAgc3RhcnRTY29wZSgpIHtcbiAgICBjb25zdCBuZXdTY29wZTogTkRBcnJheVtdID0gW107XG4gICAgdGhpcy5uZGFycmF5U2NvcGVzLnB1c2gobmV3U2NvcGUpO1xuICAgIHRoaXMuYWN0aXZlU2NvcGUgPSBuZXdTY29wZTtcblxuICAgIGNvbnN0IG5ld05EQXJyYXlzVG9LZWVwOiBOREFycmF5W10gPSBbXTtcbiAgICB0aGlzLm5kYXJyYXlzVG9LZWVwLnB1c2gobmV3TkRBcnJheXNUb0tlZXApO1xuICAgIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCA9IG5ld05EQXJyYXlzVG9LZWVwO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIHN0YXJ0U2NvcGUoKSB0byBhY2hpZXZlIHRoZSBzYW1lIGZ1bmN0aW9uYWxpdHlcbiAgICogYXMgc2NvcGUoKSB3aXRob3V0IHRoZSBuZWVkIGZvciBhIGZ1bmN0aW9uIGNsb3N1cmUuXG4gICAqL1xuICBlbmRTY29wZShyZXN1bHQ6IFNjb3BlUmVzdWx0KSB7XG4gICAgLy8gRGlzcG9zZSB0aGUgY3VycmVudCBzY29wZS5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuYWN0aXZlU2NvcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG5kYXJyYXkgPSB0aGlzLmFjdGl2ZVNjb3BlW2ldO1xuXG4gICAgICBpZiAodGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KG5kYXJyYXksIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCkgfHxcbiAgICAgICAgICAocmVzdWx0ICE9IG51bGwgJiYgcmVzdWx0IGluc3RhbmNlb2YgTkRBcnJheSAmJlxuICAgICAgICAgICBuZGFycmF5LmdldERhdGEoKSA9PT0gKHJlc3VsdCBhcyBOREFycmF5KS5nZXREYXRhKCkpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgbmRhcnJheS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgLy8gUG9wIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgIHRoaXMubmRhcnJheVNjb3Blcy5wb3AoKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlID0gdGhpcy5uZGFycmF5U2NvcGVzLmxlbmd0aCA9PT0gMCA/XG4gICAgICAgIG51bGwhIDpcbiAgICAgICAgdGhpcy5uZGFycmF5U2NvcGVzW3RoaXMubmRhcnJheVNjb3Blcy5sZW5ndGggLSAxXTtcblxuICAgIC8vIFRyYWNrIHRoZSBjdXJyZW50IHJlc3VsdCBpbiB0aGUgcGFyZW50IHNjb3BlLlxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBOREFycmF5ICYmXG4gICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QocmVzdWx0LCB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXApKSB7XG4gICAgICB0aGlzLnRyYWNrKHJlc3VsdCk7XG4gICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkpIHtcbiAgICAgIHJlc3VsdC5mb3JFYWNoKHIgPT4ge1xuICAgICAgICBpZiAociBpbnN0YW5jZW9mIE5EQXJyYXkgJiZcbiAgICAgICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QociwgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwKSkge1xuICAgICAgICAgIHRoaXMudHJhY2socik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMubmRhcnJheXNUb0tlZXAucG9wKCk7XG4gICAgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwID0gdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggPT09IDAgP1xuICAgICAgICBudWxsISA6XG4gICAgICAgIHRoaXMubmRhcnJheXNUb0tlZXBbdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHByaXZhdGUgaXNOREFycmF5RGF0YUluTGlzdChuZGFycmF5OiBOREFycmF5LCBuZGFycmF5TGlzdDogTkRBcnJheVtdKSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZGFycmF5TGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKG5kYXJyYXlMaXN0W2ldLmdldERhdGEoKSA9PT0gbmRhcnJheS5nZXREYXRhKCkpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBLZWVwcyBhbiBOREFycmF5IGluIHRoZSBjdXJyZW50IHNjb3BlIGZyb20gYmVpbmcgZGlzcG9zZWQgYXV0b21hdGljYWxseS5cbiAgICogQHBhcmFtIHJlc3VsdCBUaGUgTkRBcnJheSB0byBrZWVwIGZyb20gYmVpbmcgZGlzcG9zZWQuXG4gICAqL1xuICBrZWVwPFQgZXh0ZW5kcyBOREFycmF5PihyZXN1bHQ6IFQpOiBUIHtcbiAgICBpZiAodGhpcy5hY3RpdmVTY29wZSA9PSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5zYWZlTW9kZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnWW91IGFyZSB1c2luZyBtYXRoIGluIHNhZmUgbW9kZS4gRW5jbG9zZSBhbGwgJyArXG4gICAgICAgICAgICAnbWF0aC5tZXRob2QoKSBjYWxscyBpbnNpZGUgYSBzY29wZTogJyArXG4gICAgICAgICAgICAnbWF0aC5zY29wZSgoKSA9PiB7bWF0aC5tZXRob2QoKTsuLi59KSB0byBhdm9pZCBtZW1vcnkgJyArXG4gICAgICAgICAgICAnbGVha3MuJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXAucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogVHJhY2tzIGFuIE5EQXJyYXkgaW4gdGhlIGN1cnJlbnQgc2NvcGUgdG8gYmUgYXV0b21hdGljYWxseSBjbGVhbmVkIHVwIHdoZW5cbiAgICogdGhlIGN1cnJlbnQgc2NvcGUgZW5kcywgYW5kIHJldHVybnMgdGhlIHZhbHVlLlxuICAgKiBAcGFyYW0gcmVzdWx0IFRoZSBOREFycmF5IHRvIHRyYWNrIGluIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgKi9cbiAgdHJhY2s8VCBleHRlbmRzIE5EQXJyYXk+KHJlc3VsdDogVCk6IFQge1xuICAgIGlmICh0aGlzLmFjdGl2ZVNjb3BlID09IG51bGwpIHtcbiAgICAgIGlmICh0aGlzLnNhZmVNb2RlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdZb3UgYXJlIHVzaW5nIG1hdGggaW4gc2FmZSBtb2RlLiBFbmNsb3NlIGFsbCAnICtcbiAgICAgICAgICAgICdtYXRoLm1ldGhvZCgpIGNhbGxzIGluc2lkZSBhIHNjb3BlOiAnICtcbiAgICAgICAgICAgICdtYXRoLnNjb3BlKCgpID0+IHttYXRoLm1ldGhvZCgpOy4uLn0pIHRvIGF2b2lkIG1lbW9yeSAnICtcbiAgICAgICAgICAgICdsZWFrcy4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHRoaXMuYWN0aXZlU2NvcGUucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byBtYXRyaWNlcywgQSAqIEIuIFRoZXNlIG11c3QgYmUgbWF0cmljZXMsXG4gICAqIHVzZSBtYXRyaXhUaW1lc1ZlY3RvciBhbmQgdmVjdG9yVGltZXNNYXRyaXgsIGRvdFByb2R1Y3QsIGFuZCBvdXRlclByb2R1Y3RcbiAgICogaW4gb3RoZXIgY2FzZXMuXG4gICAqIEBwYXJhbSBhIEZpcnN0IG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBiIFNlY29uZCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYU9yaWVudGF0aW9uIFRoZSBNYXRyaXhPcmllbnRhdGlvbiBvZiBBLiBJZiB1c2luZyBUUkFOU1BPU0VELCB3aWxsXG4gICAqIGNvbXB1dGUgQV5UICogQi5cbiAgICogQHBhcmFtIGJPcmllbnRhdGlvbiBUaGUgTWF0cml4T3JpZW50YXRpb24gb2YgQi4gSWYgdXNpbmcgVFJBTlNQT1NFRCwgd2lsbFxuICAgKiBjb21wdXRlIEEgKiBCXlQuXG4gICAqL1xuICBtYXRNdWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEFycmF5MkQge1xuICAgIGNvbnN0IGlubmVyU2hhcGVBID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcbiAgICBjb25zdCBpbm5lclNoYXBlQiA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVswXSA6IGIuc2hhcGVbMV07XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYS5yYW5rID09PSAyICYmIGIucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIG1hdE11bDogaW5wdXRzIG11c3QgYmUgcmFuayAyLCBnb3QgcmFua3MgJHthLnJhbmt9YCArXG4gICAgICAgICAgICBgYW5kICR7Yi5yYW5rfS5gKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBpbm5lclNoYXBlQSA9PT0gaW5uZXJTaGFwZUIsXG4gICAgICAgIGBFcnJvciBpbiBtYXRNdWw6IGlubmVyIHNoYXBlcyAoJHtpbm5lclNoYXBlQX0pIGFuZCAoYCArXG4gICAgICAgICAgICBgJHtpbm5lclNoYXBlQn0pIG9mIE5EQXJyYXlzIHdpdGggc2hhcGVzICR7YS5zaGFwZX0gYW5kIGAgK1xuICAgICAgICAgICAgYCR7Yi5zaGFwZX0gYW5kIG9yaWVudGF0aW9ucyAke01hdHJpeE9yaWVudGF0aW9uW2FPcmllbnRhdGlvbl19YCArXG4gICAgICAgICAgICBgIGFuZCAke01hdHJpeE9yaWVudGF0aW9uW2JPcmllbnRhdGlvbl19IG11c3QgbWF0Y2guYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1hdE11bEludGVybmFsKGEsIGIsIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1hdE11bEludGVybmFsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbixcbiAgICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBBcnJheTJEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSB2ZWN0b3IgYW5kIGEgbWF0cml4LCB2ICogQi5cbiAgICogQHBhcmFtIHYgVGhlIHZlY3RvciBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqL1xuICB2ZWN0b3JUaW1lc01hdHJpeCh2OiBBcnJheTFELCBtYXRyaXg6IEFycmF5MkQpOiBBcnJheTFEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgcmFuayAxLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHt2LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtYXRyaXgucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCBiZSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMF0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgKCR7di5zaXplfSkgYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBpbm5lciBkaW1lbnNpb24gb2Ygc2Vjb25kIHJhbmsgMiBpbnB1dCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7bWF0cml4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYuYXMyRCgxLCB2LnNpemUpLCBtYXRyaXgpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSBtYXRyaXggYW5kIHZlY3RvciwgQSAqIHYuXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2IFRoZSB2ZWN0b3IgaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWF0cml4VGltZXNWZWN0b3IobWF0cml4OiBBcnJheTJELCB2OiBBcnJheTFEKTogQXJyYXkxRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCByYW5rIDEsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke3YucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1hdHJpeC5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgYSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgJHt2LnNpemV9IGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggaW5uZXIgZGltZW5zaW9uIG9mIHNlY29uZCByYW5rIDIgaW5wdXQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgc2hhcGUgJHttYXRyaXguc2hhcGV9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKG1hdHJpeCwgdi5hczJEKHYuc2l6ZSwgMSkpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxICogdjIuXG4gICAqIEBwYXJhbSB2MSBUaGUgZmlyc3QgdmVjdG9yIGluIHRoZSBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2MiBUaGUgc2Vjb25kIHZlY3RvciBpbiB0aGUgZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgZG90UHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB2MS5yYW5rID09PSAxICYmIHYyLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBkb3RQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEuc2l6ZSA9PT0gdjIuc2l6ZSxcbiAgICAgICAgYEVycm9yIGluIGRvdFByb2R1Y3Q6IHNpemUgb2YgaW5wdXRzICgke3YxLnNpemV9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7djIuc2l6ZX0pIG11c3QgbWF0Y2guYCk7XG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYxLmFzMkQoMSwgdjEuc2l6ZSksIHYyLmFzMkQodjIuc2l6ZSwgMSkpLmFzU2NhbGFyKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG91dGVyIHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxIGFuZCB2Mi5cbiAgICogQHBhcmFtIHYxIFRoZSBmaXJzdCB2ZWN0b3IgaW4gdGhlIG91dGVyIHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdjIgVGhlIHNlY29uZCB2ZWN0b3IgaW4gdGhlIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIG91dGVyUHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEucmFuayA9PT0gMSAmJiB2Mi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gb3V0ZXJQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bCh2MS5hczJEKHYxLnNpemUsIDEpLCB2Mi5hczJEKDEsIHYyLnNpemUpKTtcbiAgfVxuXG4gIC8vLy8vLy8vLy8vLy8vL1xuICAvLyBTaGFwZSBvcHMgLy9cbiAgLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIENsb25lcyBhbiBOREFycmF5IG9mIGFueSBzaGFwZS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIE5EQXJyYXkgdG8gY2xvbmUuXG4gICAqL1xuICBjbG9uZTxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBSZXNoYXBlcyBhbiBOREFycmF5IHRvIGEgbmV3IHNoYXBlLiBUaGUgc2l6ZSBvZiB0aGUgaW5wdXQgTkRBcnJheSBtdXN0XG4gICAqIG1hdGNoIHRoZSBzaXplIG9mIHRoZSByZXF1ZXN0ZWQgc2hhcGUuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmV3U2hhcGUgVGhlIG5ldyBzaGFwZSB0byByZXNoYXBlIHRoZSBOREFycmF5IHRvLiBNdXN0IGJlIHRoZSBzYW1lXG4gICAqIHNpemUgYXMgdGhlIE5EQXJyYXkuXG4gICAqL1xuICByZXNoYXBlPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG5kYXJyYXkuc2l6ZSA9PT0gdXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKSxcbiAgICAgICAgYEVycm9yIGluIHJlc2hhcGU6IG9sZCBzaXplICR7bmRhcnJheS5zaXplfSBtdXN0IG1hdGNoIG5ldyBzaXplIGAgK1xuICAgICAgICAgICAgYCR7dXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZXNoYXBlSW50ZXJuYWw8VDEsIFQyPihuZGFycmF5LCBuZXdTaGFwZSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNoYXBlSW50ZXJuYWw8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgbmRhcnJheTogVDEsIG5ld1NoYXBlOiBudW1iZXJbXSk6IFQyO1xuXG4gIC8qKlxuICAgKiBFeHRyYWN0cyBhIHNsaWNlIGZyb20gYSBtYXRyaXguIFRoZSBvcGVyYXRpb24gZXh0cmFjZXMgYSBzbGljZSBmcm9tIGlucHV0XG4gICAqIHRoYXQgc3RhcnRzIGF0IGNvb3JkaW5hdGVzIGBiZWdpbmAgYW5kIGlzIG9mIHNpemUgYHNpemVgLlxuICAgKiBAcGFyYW0gaW5wdXQgVGhlIGlucHV0IG1hdHJpeCB0byBzbGljZSBmcm9tLlxuICAgKiBAcGFyYW0gYmVnaW4gVGhlIDJEIGNvb3JkaW5hdGVzIGluIHRoZSBpbnB1dCBtYXRyaXggdG8gc3RhcnQgdGhlIHNsaWNlXG4gICAqIGZyb20uXG4gICAqIEBwYXJhbSBzaXplIFRoZSBzaWNlIG9mIHRoZSAyRCB3aW5kb3cgdG8gc2xpY2UuXG4gICAqL1xuICBzbGljZTJEKGlucHV0OiBBcnJheTJELCBiZWdpbjogW251bWJlciwgbnVtYmVyXSwgc2l6ZTogW251bWJlciwgbnVtYmVyXSk6XG4gICAgICBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYmVnaW5bMF0gKyBzaXplWzBdIDw9IGlucHV0LnNoYXBlWzBdICYmXG4gICAgICAgICAgICBiZWdpblsxXSArIHNpemVbMV0gPD0gaW5wdXQuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBzbGljZTJEOiByZXF1ZXN0ZWQgc3RhcnQgcG9zaXRpb24gJHtiZWdpbn0gYW5kIHNpemUgYCArXG4gICAgICAgICAgICBgJHtzaXplfSB3b3VsZCBvdmVyZmxvdyBpbnB1dCBvZiBzaGFwZSAke2lucHV0LnNoYXBlfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNsaWNlMkRJbnRlcm5hbChpbnB1dCwgYmVnaW4sIHNpemUpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luOiBbbnVtYmVyLCBudW1iZXJdLCBzaXplOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRDtcblxuICAvKipcbiAgICogQ29waWVzIGEgd2luZG93IGZyb20gdGhlIGBzb3VyY2VgIG1hdHJpeCBzdGFydGluZyBhdCBgc291cmNlQmVnaW5gIGFuZCBpc1xuICAgKiBvZiBzaXplIGBzb3VyY2VTaXplYCB0byBhIHdpbmRvdyBpbiB0aGUgYGRlc3RgIG1hdHJpeCBzdGFydGluZyBhdFxuICAgKiBgZGVzdEJlZ2luYCBhbmQgaXMgb2Ygc2l6ZSBgZGVzdFNpemVgL1xuICAgKiBAcGFyYW0gc291cmNlIFRoZSBzb3VyY2UgbWF0cml4IHRvIGNvcHkgZnJvbS5cbiAgICogQHBhcmFtIHNvdXJjZUJlZ2luIFRoZSBjb29yZGluYXRlcyB0byBzdGFydCB0aGUgY29weSBmcm9tLlxuICAgKiBAcGFyYW0gc291cmNlU2l6ZSBUaGUgc2l6ZSBvZiB0aGUgY29weSB3aW5kb3cuXG4gICAqIEBwYXJhbSBkZXN0IFRoZSBkZXN0aW5hdGlvbiBtYXRyaXggdG8gY29weSB0by5cbiAgICogQHBhcmFtIGRlc3RCZWdpbiBUaGUgY29vcmRpbmF0ZXMgaW4gYGRlc3RgIHRvIGNvcHkgdG8uXG4gICAqIEBwYXJhbSBkZXN0U2l6ZSBUaGUgc2l6ZSBvZiB0aGUgZGVzdGluYXRpb24gd2luZG93LlxuICAgKi9cbiAgY29weTJEKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHNvdXJjZUJlZ2luWzBdICsgc291cmNlU2l6ZVswXSA8PSBzb3VyY2Uuc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIHNvdXJjZUJlZ2luWzFdICsgc291cmNlU2l6ZVsxXSA8PSBzb3VyY2Uuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBzb3VyY2Ugc3RhcnQgcG9zaXRpb24gJHtzb3VyY2VCZWdpbn0gYCArXG4gICAgICAgICAgICBgYW5kIHNvdXJjZSBzaXplICR7c291cmNlU2l6ZX0gd291bGQgb3ZlcmZsb3cgc291cmNlIE5EQXJyYXlgICtcbiAgICAgICAgICAgIGBvZiBzaGFwZSAke3NvdXJjZS5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRlc3RCZWdpblswXSArIGRlc3RTaXplWzBdIDw9IGRlc3Quc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIGRlc3RCZWdpblsxXSArIGRlc3RTaXplWzFdIDw9IGRlc3Quc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBkZXN0IHN0YXJ0IHBvc2l0aW9uICR7ZGVzdEJlZ2lufSBgICtcbiAgICAgICAgICAgIGBhbmQgc291cmNlIHNpemUgJHtkZXN0U2l6ZX0gd291bGQgb3ZlcmZsb3cgZGVzdCBOREFycmF5IG9mYCArXG4gICAgICAgICAgICBgc2hhcGUgJHtkZXN0LnNoYXBlfS5gKTtcbiAgICBjb3B5MmRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplLCBkZXN0U2l6ZSk7XG5cbiAgICByZXR1cm4gdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgc291cmNlLCBzb3VyY2VCZWdpbiwgc291cmNlU2l6ZSwgZGVzdCwgZGVzdEJlZ2luLCBkZXN0U2l6ZSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvcHkyREludGVybmFsKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZDtcblxuICAvKipcbiAgICogQ29uY2F0ZW5hdGVzIHR3byAzRCBuZGFycmF5cyBhbG9uZyBhIGdpdmVuIGF4aXMuXG4gICAqXG4gICAqIEZvciBleGFtcGxlLCBpZjpcbiAgICogQTogc2hhcGUoMiwgMSwgMykgPSB8IHIxLCBnMSwgYjEgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjIsIGcyLCBiMiB8XG4gICAqXG4gICAqIEI6IHNoYXBlKDIsIDEsIDMpID0gfCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBDID0gY29uY2F0M0QoQSwgQiwgYXhpcylcbiAgICpcbiAgICogaWYgYXhpcyA9IDA6XG4gICAqIEM6IHNoYXBlKDQsIDEsIDMpID0gfCByMSwgZzEsIGIxIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogaWYgYXhpcyA9IDE6XG4gICAqIEM6IHNoYXBlKDIsIDIsIDMpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIsIHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBpZiBheGlzID0gMjpcbiAgICogQyA9IHNoYXBlKDIsIDEsIDYpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyLCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogQHBhcmFtIG5kYXJyYXkxIFRoZSBmaXJzdCBhcnJheSB0byBjb25jYXQuXG4gICAqIEBwYXJhbSBuZGFycmF5MiBUaGUgc2Vjb25kIGFycmF5IHRvIGNvbmF0LlxuICAgKiBAcGFyYW0gYXhpcyBUaGUgYXhpcyB0byBjb25jYXRlIGFsb25nLlxuICAgKi9cbiAgY29uY2F0M0QobmRhcnJheTE6IEFycmF5M0QsIG5kYXJyYXkyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIG5kYXJyYXkxLnNoYXBlLCBuZGFycmF5Mi5zaGFwZSwgYXhpcywgJ0Vycm9yIGluIGNvbmNhdDNkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbmNhdDNESW50ZXJuYWwobmRhcnJheTEsIG5kYXJyYXkyLCBheGlzKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbmNhdDNESW50ZXJuYWwoXG4gICAgICBuZGFycmF5MTogQXJyYXkzRCwgbmRhcnJheTI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBSZWR1Y3Rpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRoZSBsb2coc3VtKGUgXiB4KSkgZm9yIGVhY2ggeCBpbiB0aGUgaW5wdXQgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkgdG8gY29tcHV0ZSB0aGUgbG9nU3VtRXhwIG92ZXIuXG4gICAqL1xuICBsb2dTdW1FeHAobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzdW0gb2YgYWxsIHRoZSBlbnRyaWVzIGluIHRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheSB0byBjb21wdXRlIHRoZSBzdW0gb3Zlci5cbiAgICovXG4gIHN1bShuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN1bUludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3VtSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWluaW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01pbihuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01pbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWF4aW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01heChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01heEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogUmV0dXJucyBhIDEgaWYgdGhlIGFyZ01heCBvZiB4MSBhbmQgeDIgYXJlIHRoZSBzYW1lLCBvdGhlcndpc2UgMC5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgYXJnTWF4RXF1YWxzKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaCh4MS5zaGFwZSwgeDIuc2hhcGUsICdFcnJvciBpbiBhcmdNYXhFcXVhbHM6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJnTWF4RXF1YWxzSW50ZXJuYWwoeDEsIHgyKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRvcCBLIHZhbHVlcyBhbmQgZmxhdHRlbmVkIGluZGljZXMuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gayBIb3cgbWFueSB0b3AgdmFsdWVzIHRvIGNvbXB1dGUuXG4gICAqL1xuICB0b3BLKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6IHt2YWx1ZXM6IEFycmF5MUQsIGluZGljZXM6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgayA8PSBuZGFycmF5LnNpemUsXG4gICAgICAgIGBFcnJvciBpbiB0b3BLOiBrIHZhbHVlICgke2t9KSBtdXN0IGJlIGxlc3MgdGhhbiBzaXplIG9mIGlucHV0IGAgK1xuICAgICAgICAgICAgYG5kYXJyYXksIGdvdCBzaGFwZSAke25kYXJyYXkuc2hhcGV9LmApO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMudG9wS0ludGVybmFsKG5kYXJyYXksIGspO1xuICAgIHRoaXMudHJhY2socmVzdWx0LnZhbHVlcyk7XG4gICAgdGhpcy50cmFjayhyZXN1bHQuaW5kaWNlcyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdG9wS0ludGVybmFsKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6XG4gICAgICB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfTtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG1pbmltdW0gdmFsdWUgZnJvbSB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbWluKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWluSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgbWF4aW11bSB2YWx1ZSBmcm9tIHRoZSBpbnB1dC5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBtYXgobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzb2Z0bWF4IG5vcm1hbGl6ZWQgdmVjdG9yIGZyb20gdGhlIGlucHV0IHZlY3Rvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHZlY3Rvci5cbiAgICovXG4gIHNvZnRtYXgoeDogQXJyYXkxRCk6IEFycmF5MUQge1xuICAgIHJldHVybiB0aGlzLnNjb3BlKCgpID0+IHtcbiAgICAgIC8vIERvIGl0IGluIGxvZyBzcGFjZSBmb3IgbnVtZXJpY2FsIHN0YWJpbGl0eS5cbiAgICAgIC8vIGV4cChYIC0gbG9nU3VtRXhwKFgpKVxuICAgICAgY29uc3QgbHNlID0gdGhpcy5sb2dTdW1FeHAoeCk7XG4gICAgICBjb25zdCBsb2dSZXN1bHQgPSB0aGlzLmFycmF5TWludXNTY2FsYXIoeCwgbHNlKTtcbiAgICAgIHJldHVybiB0aGlzLmV4cChsb2dSZXN1bHQpO1xuICAgIH0pO1xuICB9XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBFbGVtZW50LXdpc2Ugb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogU3dpdGNoZXMgZGltZW5zaW9ucyBvZiB0aGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBuZXdEaW0gVGhlIG5ldyBpbmRpY2VzIHRoYXQgZGVmaW5lIHdoaWNoIHNoYXBlcyB2YWx1ZXMgdG8gc3dpdGNoLlxuICAgKi9cbiAgc3dpdGNoRGltPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBuZXdEaW06IG51bWJlcltdKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gbmV3RGltLmxlbmd0aCxcbiAgICAgICAgYEVycm9yIGluIHN3aXRjaERpbTogbGVuZ3RoIG9mIGlucHV0IHNoYXBlICR7YS5zaGFwZX0gYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBzaXplIG9mIG5ld0RpbSBhcnJheSAke25ld0RpbX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zd2l0Y2hEaW1JbnRlcm5hbChhLCBuZXdEaW0pKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIHBsdXMgTkRBcnJheSwgYyArIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBjICsgQS5cbiAgICogQHBhcmFtIGEgVGhlIE5EQXJyYXkgQSBpbiBjICsgQS5cbiAgICovXG4gIHNjYWxhclBsdXNBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxhclBsdXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJQbHVzQXJyYXlJbnRlcm5hbChjLCBhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIG1pbnVzIE5EQXJyYXksIGMgLSBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gYyAtIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gYyAtIEEuXG4gICAqL1xuICBzY2FsYXJNaW51c0FycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGFyTWludXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJNaW51c0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBzY2FsYXIgbWludXMgTkRBcnJheSwgQSAtIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gQSAtIGMuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBBIC0gYy5cbiAgICovXG4gIGFycmF5TWludXNTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheU1pbnVzU2NhbGFyOiBzZWNvbmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheU1pbnVzU2NhbGFySW50ZXJuYWwoYSwgYykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhcnJheU1pbnVzU2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgYzogU2NhbGFyKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgLTEgKiBBIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IGFycmF5LlxuICAgKi9cbiAgbmVnPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5uZWdJbnRlcm5hbChhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVDtcblxuICAvKipcbiAgICogQWRkcyB0d28gTkRBcnJheXMgZWxlbWVudC13aXNlLCBBICsgQi4gSW5wdXRzIG11c3QgYmUgdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIGFkZCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBhZGQgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgYWRkPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gYWRkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFkZEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBTdWJ0cmFjdHMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSwgQSAtIEIuIElucHV0cyBtdXN0IGJlIHRoZSBzYW1lIHNoYXBlLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBzdWI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBzdWI6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc3ViSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzdWJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIE11bHRpcGxpZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgKiBCLiBJbnB1dHMgbXVzdFxuICAgKiBiZSB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgZWxlbWVudFdpc2VNdWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBlbGVtZW50V2lzZU11bDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5lbGVtZW50V2lzZU11bEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZWxlbWVudFdpc2VNdWxJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIERpdmlkZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgLyBCLiBJbnB1dHMgbXVzdCBiZVxuICAgKiB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gZGl2aWRlIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGIgVGhlIHNlY29uZCBOREFycmF5IHRvIGRpdmlkZSBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBkaXZpZGU8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBkaXZpZGU6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZGl2aWRlSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBkaXZpZGVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIGRpdmlkZWQgYnkgYW4gTkRBcnJheSwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAvXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgdmFsdWUgaW4gYyAvIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIGMgLyBBLlxuICAgKi9cbiAgc2NhbGFyRGl2aWRlZEJ5QXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsYXJEaXZpZGVkQnlBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgTkRBcnJheSBvZiByYW5rICR7Yy5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJEaXZpZGVkQnlBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGFuIE5EQXJyYXkgZGl2aWRlZCBieSBhIHNjYWxhciwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgQSAvXG4gICAqIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIEEgLyBjLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIHZhbHVlIGluIEEgLyBjLlxuICAgKi9cbiAgYXJyYXlEaXZpZGVkQnlTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogc2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBgICtcbiAgICAgICAgICAgIGBidXQgZ290IE5EQXJyYXkgb2YgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsKGEsIGMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBhOiBULCBjOiBTY2FsYXIpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBleHBvbmVudGlhbCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuIHkgPSBlIF4geFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGV4cDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZXhwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBleHBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIG5hdHVyYWwgbG9nYXJpdGhtIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZS4geSA9IGxuKHgpXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbG9nPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgcmVjdGlmaWVkIGxpbmVhciBlbGVtZW50LXdpc2UsIG1heCh4LCAwKS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICByZWx1PFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZWx1SW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzaWdtb2lkIGVsZW1lbnQtd2lzZSwgeSA9IDEgLyAoMSArIGV4cCgteCkpLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHNpZ21vaWQ8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpZ21vaWRJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGh5cGVyYm9saWMgdGFuZ2VudCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgdGFuaDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMudGFuaEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc2luIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IHNpbih4KS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzaW48VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2luSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzdGVwIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IDEgaWYgeCA+IDAgfCAwIGlmIHggPD1cbiAgICogMFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHN0ZXA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN0ZXBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN0ZXBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGVkIGFycmF5IGFkZCBvcGVyYXRpb24sIGMxICogQSArIGMyICogQi5cbiAgICogQHBhcmFtIGMxIFRoZSBmaXJzdCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gYzIgVGhlIHNlY29uZCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBjYiBUaGUgc2Vjb25kIE5EQXJyYXkgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqL1xuICBzY2FsZWRBcnJheUFkZDxUIGV4dGVuZHMgTkRBcnJheT4oYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjMS5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6IGZpcnN0IGFyZ3VtZW50IG11c3QgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYCByYW5rICR7YzEucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMyLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsZWRBcnJheUFkZDogdGhpcmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgTkRBcnJheSBvZiByYW5rICR7YzIucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6ICcpO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsKGMxLCBhLCBjMiwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciB0aW1lcyBhcnJheSBvcGVyYXRpb24gYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAqXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgaW4gdGhlIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIEEgdGhlIE5EQXJyYXkgaW4gdGhlIG9wZXJhdGlvbiB0aGF0IHdpbGwgYmUgYnJvYWRjYXN0ZWQgb3Zlci5cbiAgICovXG4gIHNjYWxhclRpbWVzQXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJUaW1lc0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJUaW1lc0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYW4gZWxlbWVudC13aXNlIGJyb2FkY2FzdGVkIG11bHRpcGxpY2F0aW9uIG9mIHR3byBtYXRyaWNlcyBBIGFuZFxuICAgKiBCLiBXaWxsIHJldHVybiBhIG5ldyBtYXRyaXggdGhhdCBpcyB0aGUgbWF4IG9mIEEgYW5kIEIsIHdoZXJlIHRoZSBzbWFsbGVyXG4gICAqIG1hdHJpeCB3aWxsIGJyb2FkY2FzdCBvdmVyIHRoZSBsYXJnZXIgbWF0cml4LlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGluIHRoZSBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBBIHRoZSBOREFycmF5IGluIHRoZSBvcGVyYXRpb24gdGhhdCB3aWxsIGJlIGJyb2FkY2FzdGVkIG92ZXIuXG4gICAqL1xuICBlbGVtZW50V2lzZU11bEJyb2FkY2FzdChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIGVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0OiBmaXJzdCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7YS5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYi5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gZWxlbWVudFdpc2VNdWxCcm9hZGNhc3Q6IHNlY29uZCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7Yi5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0SW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOlxuICAgICAgQXJyYXkyRDtcblxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gQ29udm9sdXRpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIDJEIGNvbnZvbHV0aW9uIG92ZXIgdGhlIGlucHV0IHguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFtyb3dzLCBjb2xzLCBkZXB0aDFdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIGJpYXNlcyBPcHRpb25hbCBiaWFzZXMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDEgb2Ygc2hhcGUgW2RlcHRoMl0uXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSB6ZXJvIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZFxuICAgKiBlcXVhbGx5IG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIGNvbnYyZChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZDogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7d2VpZ2h0cy5yYW5rfS5gKTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGJpYXNlcy5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGJpYXNlcyBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgICBgJHtiaWFzZXMucmFua30uYCk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbMl0sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IG1hdGNoICBgICtcbiAgICAgICAgICAgIGBpbnB1dCBkZXB0aCBmb3Igd2VpZ2h0cyAke3dlaWdodHMuc2hhcGVbMl19LmApO1xuXG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbnYyZEludGVybmFsKHgsIHdlaWdodHMsIGJpYXNlcywgc3RyaWRlLCB6ZXJvUGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGJhY2twcm9wIG9mIGEgMkQgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFt4cm93cywgeGNvbHMsIGRlcHRoMV0uXG4gICAqIEBwYXJhbSBkeSBUaGUgZHkgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbeXJvd3MsIHljb2xzLCBkZXB0aDJdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBvcmlnaW5hbCBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiB0aGUgb3JpZ2luYWwgY29udm9sdXRpb24uXG4gICAqL1xuICBjb252MmRCYWNrUHJvcChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt4LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkeSBtdXN0IGJlIHJhbmsgMywgYnV0IGdvdCBzaGFwZSBgICtcbiAgICAgICAgICAgIGAke2R5LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgd2VpZ2h0cy5yYW5rID09PSA0LFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt3ZWlnaHRzLnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5zaGFwZVsyXSA9PT0gd2VpZ2h0cy5zaGFwZVsyXSxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkZXB0aCBvZiB4ICR7eC5zaGFwZVsyXX0pIG11c3QgYCArXG4gICAgICAgICAgICBgbWF0Y2ggaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVsyXX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzNdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IGRlcHRoIG9mIGR5ICgke2R5LnNoYXBlWzJdfSkgbXVzdCBgICtcbiAgICAgICAgICAgIGBtYXRjaCBvdXRwdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVszXX0pLmApO1xuXG4gICAgY29uc3QgYmFja3Byb3BSZXN1bHQgPVxuICAgICAgICB0aGlzLmNvbnYyZEJhY2tQcm9wSW50ZXJuYWwoeCwgZHksIHdlaWdodHMsIHN0cmlkZSwgcGFkKTtcblxuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZGIpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHcpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHgpO1xuXG4gICAgcmV0dXJuIGJhY2twcm9wUmVzdWx0O1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH07XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0cmFuc3Bvc2VkIDJEIGNvbnZvbHV0aW9uIG9mIGFuIGltYWdlLCBhbHNvIGtub3duIGFzIGFcbiAgICogZGVjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMywgb2Ygc2hhcGUgW3hyb3dzLCB4Y29scywgZGVwdGgxXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBiaWFzZXMgT3B0aW9uYWwgYmlhc2VzIE5EQXJyYXksIG11c3QgYmUgcmFuayAxIG9mIHNoYXBlIFtkZXB0aDJdLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBjb252MmRUcmFuc3Bvc2UoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7d2VpZ2h0cy5yYW5rfWApO1xuICAgIGlmIChiaWFzZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgYmlhc2VzLnJhbmsgPT09IDEsXG4gICAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogYmlhc2VzIG11c3QgYmUgcmFuayAxLCBidXQgZ290ICcgK1xuICAgICAgICAgICAgICAncmFuayAke2JpYXNlcy5yYW5rfS5gKTtcbiAgICB9XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbM10sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IGAgK1xuICAgICAgICAgICAgYG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICR7d2VpZ2h0cy5zaGFwZVszXX0uYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayhcbiAgICAgICAgdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWF4IHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1heFBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGluIG1heFBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICcgKyB4LnJhbmsgKyAnLicpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4UG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgYmFja3Byb3Agb2YgYSBtYXggcG9vbC5cbiAgICogQHBhcmFtIGR5IFRoZSBkeSBlcnJvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgbWF4UG9vbEJhY2twcm9wKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogZHkgbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7ZHkucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhQb29sQmFja3Byb3BJbnRlcm5hbChkeSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heFBvb2xCYWNrcHJvcEludGVybmFsKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWluIHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1pblBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1pblBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1pblBvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWluUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIGF2ZXJhZ2UgcG9vbGluZyBvZiBhbiBpbWFnZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgYXZnUG9vbCh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gYXZnUG9vbDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXZnUG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhdmdQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qXG4gICAqIEJpbGluZWFyIHJlc2l6ZSBhIDNEIGFycmF5IHBlciBlYWNoIGNoYW5uZWwgdG8gYSBuZXcgMkQgc2hhcGUuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBBcnJheTNELlxuICAgKiBAcGFyYW0gbmV3U2hhcGUyRCBUaGUgbmV3IHNoYXBlIHRvIHJlc2l6ZSB0aGUgQXJyYXkzRCB0by4gRWFjaCBjaGFubmVsIGlzXG4gICAqIHJlc2l6ZWQgaW5kaXZpZHVhbGx5LlxuICAgKiBAcGFyYW0gYWxpZ25Db3JuZXJzIEFuIG9wdGlvbmFsIGJvb2wuIERlZmF1bHRzIHRvIEZhbHNlLiBJZiB0cnVlLCByZXNjYWxlXG4gICAqIGlucHV0IGJ5IChuZXdfaGVpZ2h0IC0gMSkgLyAoaGVpZ2h0IC0gMSksIHdoaWNoIGV4YWN0bHkgYWxpZ25zIHRoZSA0XG4gICAqIGNvcm5lcnMgb2YgaW1hZ2VzIGFuZCByZXNpemVkIGltYWdlcy4gSWYgZmFsc2UsIHJlc2NhbGUgYnkgbmV3X2hlaWdodCAvXG4gICAqIGhlaWdodC4gVHJlYXQgc2ltaWxhcmx5IHRoZSB3aWR0aCBkaW1lbnNpb24uXG4gICAqL1xuICByZXNpemVCaWxpbmVhcjNEKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzID0gZmFsc2UpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gcmVzaXplQmlsaW5lYXIzRDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBuZXdTaGFwZTJELmxlbmd0aCA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHJlc2l6ZUJpbGluZWFyM0Q6IG5ldyBzaGFwZSBtdXN0IDJELCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7bmV3U2hhcGUyRH0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2soXG4gICAgICAgIHRoaXMucmVzaXplQmlsaW5lYXIzREludGVybmFsKHgsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVycykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLCBhbGlnbkNvcm5lcnM6IGJvb2xlYW4pOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBCYXRjaCBub3JtYWxpemF0aW9uIDNELiBNZWFuLCB2YXJpYW5jZSwgc2NhbGUsIGFuZCBvZmZzZXQgY2FuIGJlIG9mIHR3b1xuICAgKiBzaGFwZXM6IDEpIFRoZSBzYW1lIHNoYXBlIGFzIHRoZSBpbnB1dDogYW4gQXJyYXkzRC4gMikgSW4gdGhlIGNvbW1vbiBjYXNlLFxuICAgKiB0aGUgZGVwdGggZGltZW5zaW9uIGlzIHRoZSBsYXN0IGRpbWVuc2lvbiBvZiB4LCBzbyB0aGUgdmFsdWVzIHdvdWxkIGJlIGFuXG4gICAqIEFycmF5MUQgb2Ygc2hhcGUgW2RlcHRoXS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBtZWFuIEEgbWVhbiBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2UgQSB2YXJpYW5jZSBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2VFcHNpbG9uIEEgc21hbGwgZmxvYXQgbnVtYmVyIHRvIGF2b2lkIGRpdmlkaW5nIGJ5IDAuXG4gICAqIEBwYXJhbSBzY2FsZSBBIHNjYWxlIE5EQXJyYXkuXG4gICAqIEBwYXJhbSBvZmZzZXQgQW4gb2Zmc2V0IE5EQXJyYXkuXG4gICAqL1xuICBiYXRjaE5vcm1hbGl6YXRpb24zRChcbiAgICAgIHg6IEFycmF5M0QsIG1lYW46IEFycmF5M0R8QXJyYXkxRCwgdmFyaWFuY2U6IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIHZhcmlhbmNlRXBzaWxvbiA9IC4wMDEsIHNjYWxlPzogQXJyYXkzRHxBcnJheTFELFxuICAgICAgb2Zmc2V0PzogQXJyYXkzRHxBcnJheTFEKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGJhdGNoTm9ybWFsaXphdGlvbjNEOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1lYW4ucmFuayA9PT0gMyB8fCBtZWFuLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogbWVhbiBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYnV0IGAgK1xuICAgICAgICAgICAgYGdvdCByYW5rICR7bWVhbi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdmFyaWFuY2UucmFuayA9PT0gMyB8fCB2YXJpYW5jZS5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IHZhcmlhbmNlIG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgIGBidXQgZ290IHJhbmsgJHt2YXJpYW5jZS5yYW5rfS5gKTtcbiAgICBpZiAoc2NhbGUgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgc2NhbGUucmFuayA9PT0gMyB8fCBzY2FsZS5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogc2NhbGUgbXVzdCBiZSByYW5rIDMgb3IgcmFuayAxIGAgK1xuICAgICAgICAgICAgICBgYnV0IGdvdCByYW5rICR7c2NhbGUhLnJhbmt9LmApO1xuICAgIH1cbiAgICBpZiAob2Zmc2V0ICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIG9mZnNldC5yYW5rID09PSAzIHx8IG9mZnNldC5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogb2Zmc2V0IG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgICAgYGJ1dCBnb3QgcmFuayAke29mZnNldCEucmFua30uYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5iYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgICB4LCBtZWFuLCB2YXJpYW5jZSwgdmFyaWFuY2VFcHNpbG9uLCBzY2FsZSwgb2Zmc2V0KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb246IG51bWJlciwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEO1xufVxuXG5leHBvcnQgZW51bSBNYXRyaXhPcmllbnRhdGlvbiB7XG4gIFJFR1VMQVIsXG4gIFRSQU5TUE9TRURcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyRF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbiwgTkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlNYXRoQ1BVIGV4dGVuZHMgTkRBcnJheU1hdGgge1xuICBjb25zdHJ1Y3RvcihzYWZlTW9kZSA9IGZhbHNlKSB7XG4gICAgc3VwZXIoc2FmZU1vZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuZ2V0VmFsdWVzKCkpfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaGFwZUludGVybmFsPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgcmV0dXJuIHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KS5yZXNoYXBlPFQyPihuZXdTaGFwZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IEFycmF5MkQge1xuICAgIGNvbnN0IHJlc3VsdCA9IEFycmF5MkQuemVyb3Moc2l6ZVJvd0NvbCk7XG4gICAgdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgaW5wdXQsIGJlZ2luUm93Q29sLCBzaXplUm93Q29sLCByZXN1bHQsIFswLCAwXSwgc2l6ZVJvd0NvbCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELFxuICAgICAgZGVzdEJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb3B5MkRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCk7XG4gICAgY29uc3Qgc3JjVmFsdWVzID0gc291cmNlLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGRzdFZhbHVlcyA9IGRlc3QuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgbiA9IHNvdXJjZVNpemVSb3dDb2xbMF0gKiBzb3VyY2VTaXplUm93Q29sWzFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgICBjb25zdCBzcmNSb3cgPSBzb3VyY2VCZWdpblJvd0NvbFswXSArIE1hdGguZmxvb3IoaSAvIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjQ29sID0gc291cmNlQmVnaW5Sb3dDb2xbMV0gKyAoaSAlIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjT2ZmID0gc3JjUm93ICogc291cmNlLnNoYXBlWzFdICsgc3JjQ29sO1xuICAgICAgY29uc3QgZHN0Um93ID0gZGVzdEJlZ2luUm93Q29sWzBdICsgTWF0aC5mbG9vcihpIC8gZGVzdFNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3QgZHN0Q29sID0gZGVzdEJlZ2luUm93Q29sWzFdICsgKGkgJSBkZXN0U2l6ZVJvd0NvbFsxXSk7XG4gICAgICBjb25zdCBkc3RPZmYgPSBkc3RSb3cgKiBkZXN0LnNoYXBlWzFdICsgZHN0Q29sO1xuICAgICAgZHN0VmFsdWVzW2RzdE9mZl0gPSBzcmNWYWx1ZXNbc3JjT2ZmXTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgY29uY2F0M0RJbnRlcm5hbCh4MTogQXJyYXkzRCwgeDI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZSh4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpO1xuXG4gICAgY29uc3QgdmFsdWVzID0gTkRBcnJheS56ZXJvczxBcnJheTNEPihvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dFNoYXBlWzBdOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgb3V0cHV0U2hhcGVbMV07IGorKykge1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IG91dHB1dFNoYXBlWzJdOyBrKyspIHtcbiAgICAgICAgICAvLyBTaGFkZXIgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IGluZGV4OiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbaSwgaiwga107XG4gICAgICAgICAgbGV0IHZhbHVlOiBudW1iZXI7XG4gICAgICAgICAgaWYgKGluZGV4W2F4aXNdIDwgeDEuc2hhcGVbYXhpc10pIHtcbiAgICAgICAgICAgIHZhbHVlID0geDEuZ2V0KGksIGosIGspO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpbmRleFtheGlzXSAtPSB4MS5zaGFwZVtheGlzXTtcbiAgICAgICAgICAgIGNvbnN0IFtpMiwgajIsIGsyXSA9IGluZGV4O1xuICAgICAgICAgICAgdmFsdWUgPSB4Mi5nZXQoaTIsIGoyLCBrMik7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdmFsdWVzLnNldCh2YWx1ZSwgaSwgaiwgayk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWVzO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXN1bHRWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IGNWYWwgKyBhVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCkge1xuICAgIGNvbnN0IGNWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYzFWYWwgPSBjMS5nZXQoKTtcbiAgICBjb25zdCBjMlZhbCA9IGMyLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY1ZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY1ZhbHVlc1tpXSA9IGMxVmFsICogYVZhbHVlc1tpXSArIGMyVmFsICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBjVmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBjVmFsICogYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIGNvbnN0IG5lZ0EgPSB0aGlzLm5lZ0ludGVybmFsKGEpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuc2NhbGFyUGx1c0FycmF5SW50ZXJuYWwoYywgbmVnQSk7XG5cbiAgICBuZWdBLmRpc3Bvc2UoKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICBjb25zdCBuZWdDID0gdGhpcy5uZWdJbnRlcm5hbChjKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnNjYWxhclBsdXNBcnJheUludGVybmFsKG5lZ0MsIGEpO1xuXG4gICAgbmVnQy5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuc2NhbGFyVGltZXNBcnJheUludGVybmFsKFNjYWxhci5ORUdfT05FLCBhKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VD4oU2NhbGFyLk9ORSwgYSwgU2NhbGFyLk9ORSwgYik7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsPFQ+KFNjYWxhci5PTkUsIGEsIFNjYWxhci5ORUdfT05FLCBiKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXRNdWxJbnRlcm5hbChcbiAgICAgIGE6IEFycmF5MkQsIGI6IEFycmF5MkQsIGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsXG4gICAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogQXJyYXkyRCB7XG4gICAgY29uc3Qgc2hhcmVkRGltID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcblxuICAgIGNvbnN0IGxlZnREaW0gPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMF0gOiBhLnNoYXBlWzFdO1xuICAgIGNvbnN0IHJpZ2h0RGltID1cbiAgICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBiLnNoYXBlWzFdIDogYi5zaGFwZVswXTtcblxuICAgIGNvbnN0IG5vcm1hbEdldHRlciA9IChtYXRyaXg6IEFycmF5MkQsIGk6IG51bWJlciwgajogbnVtYmVyKSA9PlxuICAgICAgICBtYXRyaXguZ2V0KGksIGopO1xuICAgIGNvbnN0IHRyYW5zcG9zZWRHZXR0ZXIgPSAobWF0cml4OiBBcnJheTJELCBpOiBudW1iZXIsIGo6IG51bWJlcikgPT5cbiAgICAgICAgbWF0cml4LmdldChqLCBpKTtcblxuICAgIGNvbnN0IGFHZXR0ZXIgPSAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/XG4gICAgICAgIG5vcm1hbEdldHRlciA6XG4gICAgICAgIHRyYW5zcG9zZWRHZXR0ZXI7XG4gICAgY29uc3QgYkdldHRlciA9IChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgICAgbm9ybWFsR2V0dGVyIDpcbiAgICAgICAgdHJhbnNwb3NlZEdldHRlcjtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGxlZnREaW0gKiByaWdodERpbSk7XG4gICAgbGV0IGluZGV4ID0gMDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVmdERpbTsgKytpKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHJpZ2h0RGltOyArK2opIHtcbiAgICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgc2hhcmVkRGltOyArK2spIHtcbiAgICAgICAgICAvLyBUT0RPOiBvcHRpbWl6ZSBDUFUgbWF0bXVsLlxuICAgICAgICAgIHN1bSArPSBhR2V0dGVyKGEsIGksIGspICogYkdldHRlcihiLCBrLCBqKTtcbiAgICAgICAgfVxuICAgICAgICB2YWx1ZXNbaW5kZXgrK10gPSBzdW07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbGVmdERpbSwgcmlnaHREaW1dLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOiBBcnJheTJEIHtcbiAgICBjb25zdCBtYXhSb3cgPSBNYXRoLm1heChhLnNoYXBlWzBdLCBiLnNoYXBlWzBdKTtcbiAgICBjb25zdCBtYXhDb2wgPSBNYXRoLm1heChhLnNoYXBlWzFdLCBiLnNoYXBlWzFdKTtcblxuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobWF4Um93ICogbWF4Q29sKTtcbiAgICBsZXQgaW5kZXggPSAwO1xuICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IG1heFJvdzsgcm93KyspIHtcbiAgICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IG1heENvbDsgY29sKyspIHtcbiAgICAgICAgdmFsdWVzW2luZGV4KytdID0gYS5nZXQocm93ICUgYS5zaGFwZVswXSwgY29sICUgYS5zaGFwZVsxXSkgKlxuICAgICAgICAgICAgYi5nZXQocm93ICUgYi5zaGFwZVswXSwgY29sICUgYi5zaGFwZVsxXSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbWF4Um93LCBtYXhDb2xdLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRpdmlkZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAvIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBjVmFsdWUgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gY1ZhbHVlIC8gYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOlxuICAgICAgVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWx1ZSA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldIC8gY1ZhbHVlO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBzdW0gPSAwO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHN1bSArPSB2YWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KHN1bSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgbGV0IG1pbiA9IE51bWJlci5NQVhfVkFMVUU7XG4gICAgbGV0IG1pbkluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPCBtaW4pIHtcbiAgICAgICAgbWluID0gdmFsdWU7XG4gICAgICAgIG1pbkluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWluSW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBtYXggPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IG1heEluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICAgIG1heEluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWF4SW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgYXJnTWF4MSA9IHRoaXMuYXJnTWF4SW50ZXJuYWwoeDEpLmdldCgpO1xuICAgIGNvbnN0IGFyZ01heDIgPSB0aGlzLmFyZ01heEludGVybmFsKHgyKS5nZXQoKTtcbiAgICBpZiAoaXNOYU4oYXJnTWF4MSkgfHwgaXNOYU4oYXJnTWF4MikpIHtcbiAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KCsoYXJnTWF4MSA9PT0gYXJnTWF4MikpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH0ge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgdmFsdWVzQW5kSW5kaWNlczogQXJyYXk8e3ZhbHVlOiBudW1iZXIsIGluZGV4OiBudW1iZXJ9PiA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YWx1ZXNBbmRJbmRpY2VzLnB1c2goe3ZhbHVlOiB2YWx1ZXNbaV0sIGluZGV4OiBpfSk7XG4gICAgfVxuICAgIHZhbHVlc0FuZEluZGljZXMuc29ydCgoYSwgYikgPT4ge1xuICAgICAgcmV0dXJuIGIudmFsdWUgLSBhLnZhbHVlO1xuICAgIH0pO1xuICAgIGNvbnN0IHRvcGtWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGspO1xuICAgIGNvbnN0IHRvcGtJbmRpY2VzID0gbmV3IEZsb2F0MzJBcnJheShrKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGs7IGkrKykge1xuICAgICAgdG9wa1ZhbHVlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0udmFsdWU7XG4gICAgICB0b3BrSW5kaWNlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0uaW5kZXg7XG4gICAgfVxuICAgIHJldHVybiB7dmFsdWVzOiBBcnJheTFELm5ldyh0b3BrVmFsdWVzKSwgaW5kaWNlczogQXJyYXkxRC5uZXcodG9wa0luZGljZXMpfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGxldCBtaW4gPSB2YWx1ZXNbMF07XG4gICAgZm9yIChsZXQgaSA9IDE7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIDwgbWluKSB7XG4gICAgICAgIG1pbiA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhtaW4pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgbGV0IG1heCA9IHZhbHVlc1swXTtcbiAgICBmb3IgKGxldCBpID0gMTsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1heCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IE1hdGguZXhwKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgbmV3VmFsdWVzW2ldID0gTWF0aC5sb2codmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHhNYXggPSB0aGlzLm1heChuZGFycmF5KTtcbiAgICBjb25zdCBhID0gdGhpcy5hcnJheU1pbnVzU2NhbGFyKG5kYXJyYXksIHhNYXgpO1xuICAgIGNvbnN0IGIgPSB0aGlzLmV4cChhKTtcbiAgICBjb25zdCBjID0gdGhpcy5zdW0oYik7XG4gICAgY29uc3QgZCA9IHRoaXMubG9nKGMpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuYWRkKHhNYXgsIGQpO1xuXG4gICAgeE1heC5kaXNwb3NlKCk7XG4gICAgYS5kaXNwb3NlKCk7XG4gICAgYi5kaXNwb3NlKCk7XG4gICAgYy5kaXNwb3NlKCk7XG4gICAgZC5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlbHVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBNYXRoLm1heCgwLCB2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSAxIC8gKDEgKyBNYXRoLmV4cCgtdmFsdWVzW2ldKSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IHV0aWwudGFuaCh2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IE1hdGguc2luKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RlcEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gdmFsdWUgPiAwID8gMSA6ICh2YWx1ZSA8IDAgPyAwIDogdmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgLyoqXG4gICAqIGltYWdlIGlzIG9mIHNoYXBlIFtyLCBjLCBkMV0uXG4gICAqIHdlaWdodHMgaXMgb2Ygc2hhcGUgW0YsIEYsIGQxLCBkMl0uXG4gICAqL1xuICBwcm90ZWN0ZWQgY29udjJkSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdID0geC5zaGFwZTtcbiAgICBjb25zdCBmaWVsZFNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzLCB4Q29scywgaW5wdXREZXB0aF0sIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG4gICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICBmb3IgKGxldCB5UiA9IDA7IHlSIDwgeS5zaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSICogc3RyaWRlIC0gcGFkO1xuICAgICAgICBjb25zdCB4Uk1pbiA9IE1hdGgubWF4KDAsIHhSQ29ybmVyKTtcbiAgICAgICAgY29uc3QgeFJNYXggPSBNYXRoLm1pbih4Um93cywgZmllbGRTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZmllbGRTaXplICsgeENDb3JuZXIpO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSIC0geFJDb3JuZXI7XG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgLSB4Q0Nvcm5lcjtcbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IGlucHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9IHdlaWdodHMuZ2V0KHdSLCB3QywgZDEsIGQyKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJpYXMgPSAoYmlhc2VzICE9IG51bGwpID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH0ge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBkdyA9IHRoaXMuY29udjJkRGVyV2VpZ2h0cyh4LCBkeSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCBkYiA9IHRoaXMuY29udjJkRGVyQmlhcyhkeSk7XG4gICAgY29uc3QgZHggPSB0aGlzLmNvbnYyZFRyYW5zcG9zZUludGVybmFsKGR5LCB3ZWlnaHRzLCBudWxsLCBzdHJpZGUsIHBhZCk7XG4gICAgcmV0dXJuIHtkeCwgZGIsIGR3fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvcmlnT3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIHhEZXB0aF0gPSB4LnNoYXBlO1xuXG4gICAgLy8gRGlsYXRlIHRoZSBpbnB1dC5cbiAgICBjb25zdCB4Um93c0RpbGF0ZWQgPSAoeFJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IHhDb2xzRGlsYXRlZCA9ICh4Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzRGlsYXRlZCwgeENvbHNEaWxhdGVkLCBvcmlnT3V0cHV0RGVwdGhdLCBmU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIDEsXG4gICAgICAgIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvcmlnSW5wdXREZXB0aDsgKytkMikge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCBNYXRoLmNlaWwoeFJDb3JuZXIgLyBvcmlnU3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIChmU2l6ZSArIHhSQ29ybmVyKSAvIG9yaWdTdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCh4Q0Nvcm5lciAvIG9yaWdTdHJpZGUpKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCAoZlNpemUgKyB4Q0Nvcm5lcikgLyBvcmlnU3RyaWRlKTtcblxuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSICogb3JpZ1N0cmlkZSAtIHhSQ29ybmVyO1xuXG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgKiBvcmlnU3RyaWRlIC0geENDb3JuZXI7XG5cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cy5nZXQoZlNpemUgLSAxIC0gd1IsIGZTaXplIC0gMSAtIHdDLCBkMiwgZDEpO1xuICAgICAgICAgICAgICAgIGRvdFByb2QgKz0gcGl4ZWwgKiB3ZWlnaHQ7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgYmlhcyA9IGJpYXNlcyAhPSBudWxsID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZVNoYWRlckxpa2UoXG4gICAgICB4OiBBcnJheTNELCBvcmlnV2VpZ2h0czogQXJyYXk0RCwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgZlNpemUgPSBvcmlnV2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgW3hSb3dzLCB4Q29scywgeERlcHRoXSA9IHguc2hhcGU7XG5cbiAgICAvLyBEaWxhdGUgdGhlIGlucHV0LlxuICAgIGNvbnN0IHhSb3dzRGlsYXRlZCA9ICh4Um93cyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gICAgY29uc3QgeENvbHNEaWxhdGVkID0gKHhDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbeFJvd3NEaWxhdGVkLCB4Q29sc0RpbGF0ZWQsIG9yaWdPdXRwdXREZXB0aF0sIGZTaXplLCBvcmlnSW5wdXREZXB0aCwgMSxcbiAgICAgICAgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3JpZ0lucHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgLy8gU2hhZGVyIGNvZGUgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICAgICAgICBjb25zdCB4UiA9ICh4UkNvcm5lciArIHdSKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICBpZiAoeFIgPCAwIHx8IHhSID49IHhSb3dzIHx8IE1hdGguZmxvb3IoeFIpICE9PSB4Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCB4QyA9ICh4Q0Nvcm5lciArIHdDKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICAgIGlmICh4QyA8IDAgfHwgeEMgPj0geENvbHMgfHwgTWF0aC5mbG9vcih4QykgIT09IHhDKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgb3JpZ1dlaWdodHMuZ2V0KGZTaXplIC0gMSAtIHdSLCBmU2l6ZSAtIDEgLSB3QywgZDIsIGQxKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHkuc2V0KGRvdFByb2QsIHlSLCB5QywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB5O1xuICB9XG5cbiAgY29udjJkRGVyV2VpZ2h0cyhcbiAgICAgIHg6IEFycmF5M0QsIGRZOiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5NEQge1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB4LnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3Qgd2VpZ2h0c1NoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzU2hhcGU0RChpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuICAgIGNvbnN0IGRXID0gQXJyYXk0RC56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuXG4gICAgY29uc3QgeU51bVJvd3MgPSBkWS5zaGFwZVswXTtcbiAgICBjb25zdCB5TnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHhOdW1Sb3dzID0geC5zaGFwZVswXTtcbiAgICBjb25zdCB4TnVtQ29scyA9IHguc2hhcGVbMV07XG5cbiAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgIGNvbnN0IHlSTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKSk7XG4gICAgICBjb25zdCB5Uk1heCA9IE1hdGgubWluKHlOdW1Sb3dzLCAoeE51bVJvd3MgKyB6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKTtcblxuICAgICAgZm9yIChsZXQgd0MgPSAwOyB3QyA8IGZTaXplOyArK3dDKSB7XG4gICAgICAgIGNvbnN0IHlDTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd0MpIC8gc3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHlDTWF4ID0gTWF0aC5taW4oeU51bUNvbHMsICh4TnVtQ29scyArIHplcm9QYWQgLSB3QykgLyBzdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBpbnB1dERlcHRoOyArK2QxKSB7XG4gICAgICAgICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICAgICAgICAvLyBOZWVkIHRvIGNvbnZvbHZlLlxuICAgICAgICAgICAgbGV0IGRvdFByb2QgPSAwO1xuICAgICAgICAgICAgZm9yIChsZXQgeVIgPSB5Uk1pbjsgeVIgPCB5Uk1heDsgKyt5Uikge1xuICAgICAgICAgICAgICBjb25zdCB4UiA9IHdSICsgeVIgKiBzdHJpZGUgLSB6ZXJvUGFkO1xuICAgICAgICAgICAgICBmb3IgKGxldCB5QyA9IHlDTWluOyB5QyA8IHlDTWF4OyArK3lDKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgeEMgPSB3QyArIHlDICogc3RyaWRlIC0gemVyb1BhZDtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHguZ2V0KHhSLCB4QywgZDEpICogZFkuZ2V0KHlSLCB5QywgZDIpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkVy5zZXQoZG90UHJvZCwgd1IsIHdDLCBkMSwgZDIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZFc7XG4gIH1cblxuICBjb252MmREZXJCaWFzKGRZOiBBcnJheTNEKTogQXJyYXkxRCB7XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSBkWS5zaGFwZVsyXTtcbiAgICBjb25zdCBudW1Sb3dzID0gZFkuc2hhcGVbMF07XG4gICAgY29uc3QgbnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkob3V0cHV0RGVwdGgpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICBmb3IgKGxldCByID0gMDsgciA8IG51bVJvd3M7ICsrcikge1xuICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG51bUNvbHM7ICsrYykge1xuICAgICAgICAgIHN1bSArPSBkWS5nZXQociwgYywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB2YWx1ZXNbZDJdID0gc3VtO1xuICAgIH1cbiAgICByZXR1cm4gQXJyYXkxRC5uZXcodmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzd2l0Y2hEaW1JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4odDogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQge1xuICAgIGNvbnN0IG5ld1NoYXBlOiBudW1iZXJbXSA9IG5ldyBBcnJheSh0LnJhbmspO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3U2hhcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIG5ld1NoYXBlW2ldID0gdC5zaGFwZVtuZXdEaW1baV1dO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHQuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gdC5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCByZXN1bHQgPSBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdC5zaXplOyArK2kpIHtcbiAgICAgIGNvbnN0IGxvYyA9IHQuaW5kZXhUb0xvYyhpKTtcblxuICAgICAgLy8gUGVybXV0ZSBsb2NhdGlvbi5cbiAgICAgIGNvbnN0IG5ld0xvYzogbnVtYmVyW10gPSBuZXcgQXJyYXkobG9jLmxlbmd0aCk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld0xvYy5sZW5ndGg7IGkrKykge1xuICAgICAgICBuZXdMb2NbaV0gPSBsb2NbbmV3RGltW2ldXTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbmV3SW5kZXggPSByZXN1bHQubG9jVG9JbmRleChuZXdMb2MpO1xuICAgICAgcmVzdWx0VmFsdWVzW25ld0luZGV4XSA9IHZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgcG9vbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcixcbiAgICAgIHBvb2xUeXBlOiAnbWF4J3wnbWluJ3wnYXZnJykge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGRlcHRoXSA9IHguc2hhcGU7XG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93cywgeENvbHMsIGRlcHRoXSwgZlNpemUsIGRlcHRoLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCB4UkNvcm5lcik7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIGZTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZlNpemUgKyB4Q0Nvcm5lcik7XG5cblxuICAgICAgICAgIGxldCBtaW5NYXhWYWx1ZSA9XG4gICAgICAgICAgICAgIChwb29sVHlwZSA9PT0gJ21heCcgPyBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFkgOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgICAgICAgICBsZXQgYXZnVmFsdWUgPSAwO1xuXG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKGlzTmFOKHBpeGVsKSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGF2Z1ZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmICgocG9vbFR5cGUgPT09ICdtYXgnICYmIHBpeGVsID4gbWluTWF4VmFsdWUpIHx8XG4gICAgICAgICAgICAgICAgICAocG9vbFR5cGUgPT09ICdtaW4nICYmIHBpeGVsIDwgbWluTWF4VmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgbWluTWF4VmFsdWUgPSBwaXhlbDtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycpIHtcbiAgICAgICAgICAgICAgICBhdmdWYWx1ZSArPSBwaXhlbCAvIChmU2l6ZSAqIGZTaXplKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlzTmFOKG1pbk1heFZhbHVlKSkge1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgeS5zZXQocG9vbFR5cGUgPT09ICdhdmcnID8gYXZnVmFsdWUgOiBtaW5NYXhWYWx1ZSwgeVIsIHlDLCBkKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5wb29sKHgsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21heCcpO1xuICB9XG5cbiAgbWF4UG9vbFBvc2l0aW9ucyh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBkZXB0aF0gPSB4LnNoYXBlO1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKHguc2hhcGUsIGZTaXplLCBkZXB0aCwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IG1heFBvc2l0aW9ucyA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IG91dHB1dFNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgeFJDb3JuZXIpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCBmU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IG91dHB1dFNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIHhDQ29ybmVyKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCBmU2l6ZSArIHhDQ29ybmVyKTtcbiAgICAgICAgICBsZXQgbWF4VmFsdWUgPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgICAgICAgbGV0IG1heFBvc2l0aW9uID0gLTE7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKHBpeGVsID4gbWF4VmFsdWUpIHtcbiAgICAgICAgICAgICAgICBtYXhWYWx1ZSA9IHBpeGVsO1xuICAgICAgICAgICAgICAgIG1heFBvc2l0aW9uID0gd1IgKiBmU2l6ZSArIHdDO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIG1heFBvc2l0aW9ucy5zZXQobWF4UG9zaXRpb24sIHlSLCB5QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1heFBvc2l0aW9ucztcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sQmFja3Byb3BJbnRlcm5hbChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgICBvcmlnUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBtYXhQb3NpdGlvbnMgPSB0aGlzLm1heFBvb2xQb3NpdGlvbnMoeCwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgW2R5Um93cywgZHlDb2xzLCBkZXB0aF0gPSBkeS5zaGFwZTtcblxuICAgIC8vIERpbGF0ZSB0aGUgaW5wdXQuXG4gICAgY29uc3QgZHlSb3dzRGlsYXRlZCA9IChkeVJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IGR4Q29sc0RpbGF0ZWQgPSAoZHlDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbZHlSb3dzRGlsYXRlZCwgZHhDb2xzRGlsYXRlZCwgZGVwdGhdLCBmU2l6ZSwgZGVwdGgsIDEsIHBhZCk7XG4gICAgY29uc3QgZHggPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcblxuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgZHhSID0gMDsgZHhSIDwgZHguc2hhcGVbMF07ICsrZHhSKSB7XG4gICAgICAgIGZvciAobGV0IGR4QyA9IDA7IGR4QyA8IGR4LnNoYXBlWzFdOyArK2R4Qykge1xuICAgICAgICAgIC8vIFNoYWRlciBjb2RlIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCBkeVJDb3JuZXIgPSBkeFIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgZHlDQ29ybmVyID0gZHhDIC0gcGFkO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgICAgICAgIGNvbnN0IGR5UiA9IChkeVJDb3JuZXIgKyB3UikgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgaWYgKGR5UiA8IDAgfHwgZHlSID49IGR5Um93cyB8fCBNYXRoLmZsb29yKGR5UikgIT09IGR5Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCBkeUMgPSAoZHlDQ29ybmVyICsgd0MpIC8gb3JpZ1N0cmlkZTtcbiAgICAgICAgICAgICAgaWYgKGR5QyA8IDAgfHwgZHlDID49IGR5Q29scyB8fCBNYXRoLmZsb29yKGR5QykgIT09IGR5Qykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IG1heFBvcyA9IGZTaXplICogZlNpemUgLSAxIC0gbWF4UG9zaXRpb25zLmdldChkeVIsIGR5QywgZCk7XG4gICAgICAgICAgICAgIGNvbnN0IGN1clBvcyA9IHdSICogZlNpemUgKyB3QztcblxuICAgICAgICAgICAgICBjb25zdCBtYXNrID0gbWF4UG9zID09PSBjdXJQb3MgPyAxIDogMDtcbiAgICAgICAgICAgICAgaWYgKG1hc2sgPT09IDApIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0gZHkuZ2V0KGR5UiwgZHlDLCBkKTtcbiAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIG1hc2s7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGR4LnNldChkb3RQcm9kLCBkeFIsIGR4QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGR4O1xuICB9XG5cbiAgcHJvdGVjdGVkIG1pblBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnBvb2woeCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnbWluJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXZnUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIHRoaXMucG9vbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdhdmcnKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRCB7XG4gICAgY29uc3Qgb3V0cHV0ID0gQXJyYXkzRC56ZXJvcyhbbmV3U2hhcGUyRFswXSwgbmV3U2hhcGUyRFsxXSwgeC5zaGFwZVsyXV0pO1xuXG4gICAgY29uc3QgZWZmZWN0aXZlSW5wdXRTaXplID1cbiAgICAgICAgYWxpZ25Db3JuZXJzID8gW3guc2hhcGVbMF0gLSAxLCB4LnNoYXBlWzFdIC0gMSwgeC5zaGFwZVsyXV0gOiB4LnNoYXBlO1xuICAgIGNvbnN0IGVmZmVjdGl2ZU91dHB1dFNpemUgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgICBbb3V0cHV0LnNoYXBlWzBdIC0gMSwgb3V0cHV0LnNoYXBlWzFdIC0gMSwgb3V0cHV0LnNoYXBlWzJdXSA6XG4gICAgICAgIG91dHB1dC5zaGFwZTtcbiAgICBmb3IgKGxldCByID0gMDsgciA8IG91dHB1dC5zaGFwZVswXTsgcisrKSB7XG4gICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG91dHB1dC5zaGFwZVsxXTsgYysrKSB7XG4gICAgICAgIGZvciAobGV0IGQgPSAwOyBkIDwgb3V0cHV0LnNoYXBlWzJdOyBkKyspIHtcbiAgICAgICAgICAvLyBCZWdpbiBzaGFkZXIuXG5cbiAgICAgICAgICAvLyBDb21wdXRlIHRoZSBmcmFjdGlvbmFsIGluZGV4IG9mIHRoZSBzb3VyY2UuXG4gICAgICAgICAgY29uc3Qgc291cmNlRnJhY1JvdyA9XG4gICAgICAgICAgICAgIChlZmZlY3RpdmVJbnB1dFNpemVbMF0pICogciAvIChlZmZlY3RpdmVPdXRwdXRTaXplWzBdKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VGcmFjQ29sID1cbiAgICAgICAgICAgICAgKGVmZmVjdGl2ZUlucHV0U2l6ZVsxXSkgKiBjIC8gKGVmZmVjdGl2ZU91dHB1dFNpemVbMV0pO1xuXG4gICAgICAgICAgY29uc3Qgc291cmNlUm93Rmxvb3IgPSBNYXRoLmZsb29yKHNvdXJjZUZyYWNSb3cpO1xuICAgICAgICAgIGNvbnN0IHNvdXJjZVJvd0NlaWwgPVxuICAgICAgICAgICAgICBNYXRoLm1pbih4LnNoYXBlWzBdIC0gMSwgTWF0aC5jZWlsKHNvdXJjZUZyYWNSb3cpKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VDb2xGbG9vciA9IE1hdGguZmxvb3Ioc291cmNlRnJhY0NvbCk7XG4gICAgICAgICAgY29uc3Qgc291cmNlQ29sQ2VpbCA9XG4gICAgICAgICAgICAgIE1hdGgubWluKHguc2hhcGVbMV0gLSAxLCBNYXRoLmNlaWwoc291cmNlRnJhY0NvbCkpO1xuXG4gICAgICAgICAgY29uc3QgdG9wTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xGbG9vciwgZCk7XG4gICAgICAgICAgY29uc3QgYm90dG9tTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbEZsb29yLCBkKTtcbiAgICAgICAgICBjb25zdCB0b3BSaWdodCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xDZWlsLCBkKTtcbiAgICAgICAgICBjb25zdCBib3R0b21SaWdodCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbENlaWwsIGQpO1xuXG4gICAgICAgICAgY29uc3Qgcm93RnJhYyA9IHNvdXJjZUZyYWNSb3cgLSBzb3VyY2VSb3dGbG9vcjtcbiAgICAgICAgICBjb25zdCBjb2xGcmFjID0gc291cmNlRnJhY0NvbCAtIHNvdXJjZUNvbEZsb29yO1xuXG4gICAgICAgICAgY29uc3QgdG9wID0gdG9wTGVmdCArICh0b3BSaWdodCAtIHRvcExlZnQpICogY29sRnJhYztcbiAgICAgICAgICBjb25zdCBib3R0b20gPSBib3R0b21MZWZ0ICsgKGJvdHRvbVJpZ2h0IC0gYm90dG9tTGVmdCkgKiBjb2xGcmFjO1xuICAgICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gdG9wICsgKGJvdHRvbSAtIHRvcCkgKiByb3dGcmFjO1xuXG4gICAgICAgICAgb3V0cHV0LnNldChuZXdWYWx1ZSwgciwgYywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb24gPSAuMDAxLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhWYWx1ZXMgPSB4LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG1lYW5WYWx1ZXMgPSBtZWFuLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHZhcmlhbmNlVmFsdWVzID0gdmFyaWFuY2UuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3Qgc2NhbGVWYWx1ZXMgPSBzY2FsZSA/IHNjYWxlLmdldFZhbHVlcygpIDogbmV3IEZsb2F0MzJBcnJheShbMV0pO1xuICAgIGNvbnN0IG9mZnNldFZhbHVlcyA9IG9mZnNldCA/IG9mZnNldC5nZXRWYWx1ZXMoKSA6IG5ldyBGbG9hdDMyQXJyYXkoWzBdKTtcbiAgICBjb25zdCBvdXRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHhWYWx1ZXMubGVuZ3RoKTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgeFZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgb3V0VmFsdWVzW2ldID0gb2Zmc2V0VmFsdWVzW2kgJSBvZmZzZXRWYWx1ZXMubGVuZ3RoXSArXG4gICAgICAgICAgKHhWYWx1ZXNbaV0gLSBtZWFuVmFsdWVzW2kgJSBtZWFuVmFsdWVzLmxlbmd0aF0pICpcbiAgICAgICAgICAgICAgc2NhbGVWYWx1ZXNbaSAlIHNjYWxlVmFsdWVzLmxlbmd0aF0gL1xuICAgICAgICAgICAgICBNYXRoLnNxcnQoXG4gICAgICAgICAgICAgICAgICB2YXJpYW5jZVZhbHVlc1tpICUgdmFyaWFuY2VWYWx1ZXMubGVuZ3RoXSArIHZhcmlhbmNlRXBzaWxvbik7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oeC5zaGFwZSwge3ZhbHVlczogb3V0VmFsdWVzfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0ICogYXMgY29uY2F0M2RfdXRpbCBmcm9tICcuL2NvbmNhdDNkX3V0aWwnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4vY29udl91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0ICogYXMgbmRhcnJheSBmcm9tICcuL25kYXJyYXknO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5pbXBvcnQgKiBhcyBhZGRzY2FsZWRtYXRfZ3B1IGZyb20gJy4vd2ViZ2wvYWRkc2NhbGVkbWF0X2dwdSc7XG5pbXBvcnQgKiBhcyBhZGRzdWJtdWxkaXZfZ3B1IGZyb20gJy4vd2ViZ2wvYWRkc3VibXVsZGl2X2dwdSc7XG5pbXBvcnQge09wZXJhbmRUeXBlfSBmcm9tICcuL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUnO1xuaW1wb3J0ICogYXMgYXJnbWF4ZXF1YWxzX2dwdSBmcm9tICcuL3dlYmdsL2FyZ21heGVxdWFsc19ncHUnO1xuaW1wb3J0ICogYXMgYXJnbWlubWF4X2dwdSBmcm9tICcuL3dlYmdsL2FyZ21pbm1heF9ncHUnO1xuaW1wb3J0ICogYXMgYXZnX3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvYXZnX3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIGJhdGNobm9ybV9ncHUgZnJvbSAnLi93ZWJnbC9iYXRjaG5vcm1fZ3B1JztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX2dwdSBmcm9tICcuL3dlYmdsL2NvbmNhdDNkX2dwdSc7XG5pbXBvcnQgKiBhcyBjb252X2JhY2twcm9wX2dwdSBmcm9tICcuL3dlYmdsL2NvbnZfYmFja3Byb3BfZ3B1JztcbmltcG9ydCAqIGFzIGNvbnZfZ3B1IGZyb20gJy4vd2ViZ2wvY29udl9ncHUnO1xuaW1wb3J0ICogYXMgY29weV9ncHUgZnJvbSAnLi93ZWJnbC9jb3B5X2dwdSc7XG5pbXBvcnQgKiBhcyBleHBfZ3B1IGZyb20gJy4vd2ViZ2wvZXhwX2dwdSc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIGdwZ3B1X3V0aWwgZnJvbSAnLi93ZWJnbC9ncGdwdV91dGlsJztcbmltcG9ydCAqIGFzIGxvZ19ncHUgZnJvbSAnLi93ZWJnbC9sb2dfZ3B1JztcbmltcG9ydCAqIGFzIGxvZ3N1bWV4cF9ncHUgZnJvbSAnLi93ZWJnbC9sb2dzdW1leHBfZ3B1JztcbmltcG9ydCAqIGFzIG1heF9wb29sX2JhY2twcm9wX2dwdSBmcm9tICcuL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdSc7XG5pbXBvcnQgKiBhcyBtYXhfcG9vbF9ncHUgZnJvbSAnLi93ZWJnbC9tYXhfcG9vbF9ncHUnO1xuaW1wb3J0ICogYXMgbWluX3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvbWluX3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIG1pbm1heF9ncHUgZnJvbSAnLi93ZWJnbC9taW5tYXhfZ3B1JztcbmltcG9ydCAqIGFzIG11bG1hdF9ncHUgZnJvbSAnLi93ZWJnbC9tdWxtYXRfZ3B1JztcbmltcG9ydCAqIGFzIG5lZ19ncHUgZnJvbSAnLi93ZWJnbC9uZWdfZ3B1JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvcG9vbF9ncHUnO1xuaW1wb3J0ICogYXMgcmVkdWNlc3VtX2dwdSBmcm9tICcuL3dlYmdsL3JlZHVjZXN1bV9ncHUnO1xuaW1wb3J0ICogYXMgcmVsdV9ncHUgZnJvbSAnLi93ZWJnbC9yZWx1X2dwdSc7XG5pbXBvcnQgKiBhcyByZXNoYXBlX2dwdSBmcm9tICcuL3dlYmdsL3Jlc2hhcGVfZ3B1JztcbmltcG9ydCAqIGFzIHJlc2l6ZV9iaWxpbmVhcl9ncHUgZnJvbSAnLi93ZWJnbC9yZXNpemVfYmlsaW5lYXJfZ3B1JztcbmltcG9ydCAqIGFzIHNoYWRlcl9jb21waWxlciBmcm9tICcuL3dlYmdsL3NoYWRlcl9jb21waWxlcic7XG5pbXBvcnQgKiBhcyBzaWdtb2lkX2dwdSBmcm9tICcuL3dlYmdsL3NpZ21vaWRfZ3B1JztcbmltcG9ydCAqIGFzIHN0ZXBfZ3B1IGZyb20gJy4vd2ViZ2wvc3RlcF9ncHUnO1xuaW1wb3J0IHtUZXh0dXJlTWFuYWdlcn0gZnJvbSAnLi93ZWJnbC90ZXh0dXJlX21hbmFnZXInO1xuaW1wb3J0ICogYXMgdHJpZ19ncHUgZnJvbSAnLi93ZWJnbC90cmlnX2dwdSc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbmNvbnN0IEFSR01BWF9QUk9HID0gJ2FyZ21heCc7XG5jb25zdCBBUkdNQVhfRVFVQUxTX1BST0cgPSAnYXJnbWF4ZXF1YWxzJztcbmNvbnN0IEFSR01JTl9QUk9HID0gJ2FyZ21pbic7XG5cbmNvbnN0IEJBVENITk9STV9QUk9HID0gJ2JhdGNobm9ybSc7XG5cbmNvbnN0IENPUFlfUFJPRyA9ICdjb3B5JztcbmNvbnN0IENPTkNBVF9QUk9HID0gJ2NvbmNhdCc7XG5cbi8vIE1hdHJpeCBhbGdlYnJhLlxuY29uc3QgQUREX1NDQUxFRF9NQVRfUFJPRyA9ICdhZGRzY2FsZWRtYXQnO1xuY29uc3QgTUFUTVVMX1BST0cgPSAnbWF0bXVsJztcblxuLy8gRWxlbWVudC13aXNlIG9wcy5cbmNvbnN0IFJFTFVfUFJPRyA9ICdyZWx1JztcbmNvbnN0IFRBTkhfUFJPRyA9ICd0YW5oJztcbmNvbnN0IFNJTl9QUk9HID0gJ3Npbic7XG5jb25zdCBTSUdNT0lEX1BST0cgPSAnc2lnbW9pZCc7XG5jb25zdCBNQVhfUFJPRyA9ICdtYXgnO1xuY29uc3QgTUlOX1BST0cgPSAnbWluJztcbmNvbnN0IE5FR19QUk9HID0gJ25lZyc7XG5jb25zdCBFWFBfUFJPRyA9ICdleHAnO1xuY29uc3QgTE9HX1BST0cgPSAnbG9nJztcbmNvbnN0IFNVTV9QUk9HID0gJ3N1bSc7XG5jb25zdCBTVEVQX1BST0cgPSAnc3RlcCc7XG5jb25zdCBMT0dTVU1FWFBfUFJPRyA9ICdsb2dzdW1leHAnO1xuY29uc3QgUkVTSEFQRV9QUk9HID0gJ3Jlc2hhcGUnO1xuY29uc3QgQUREX1NVTV9NVUxfRElWX1BST0cgPSAnYWRkc3VtbXVsZGl2JztcblxuLy8gQ29udm9sdXRpb24uXG5jb25zdCBDT05WMkRfUFJPRyA9ICdjb252JztcbmNvbnN0IENPTlYyRF9UUkFOU1BPU0VfUFJPRyA9ICdjb252X3RyYW5zcG9zZSc7XG5jb25zdCBDT05WMkRfREVSV19QUk9HID0gJ2NvbnZfZGVydyc7XG5jb25zdCBDT05WMkRfREVSQl9QUk9HID0gJ2NvbnZfZGVyYic7XG5jb25zdCBNQVhfUE9PTF9QUk9HID0gJ21heHBvb2wnO1xuY29uc3QgTUFYX1BPT0xfUE9TSVRJT05TX1BST0cgPSAnbWF4cG9vbF9wb3NuJztcbmNvbnN0IE1BWF9QT09MX0JBQ0tQUk9QX1BST0cgPSAnbWF4cG9vbF9iYWNrcHJvcCc7XG5jb25zdCBNSU5fUE9PTF9QUk9HID0gJ21pbnBvb2wnO1xuY29uc3QgQVZHX1BPT0xfUFJPRyA9ICdhdmdwb29sJztcblxuY29uc3QgUkVTSVpFX0JJTElORUFSX1BST0cgPSAncmVzaXplYmlsaW4nO1xuXG5mdW5jdGlvbiBtYWtlQ29weVByb2dyYW1OYW1lKFxuICAgIHNvdXJjZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgY29uc3Qgc2hhcGVOYW1lID0gYCR7c291cmNlU2hhcGVSb3dDb2xbMF19XyR7c291cmNlU2hhcGVSb3dDb2xbMV19YDtcbiAgY29uc3Qgc3JjU2l6ZU5hbWUgPSBgJHtzb3VyY2VTaXplUm93Q29sWzBdfV8ke3NvdXJjZVNpemVSb3dDb2xbMV19YDtcbiAgY29uc3QgZHN0U2l6ZU5hbWUgPSBgJHtkZXN0U2l6ZVJvd0NvbFswXX1fJHtkZXN0U2l6ZVJvd0NvbFsxXX1gO1xuICByZXR1cm4gYCR7Q09QWV9QUk9HfV8ke3NoYXBlTmFtZX1fJHtzcmNTaXplTmFtZX1fJHtkc3RTaXplTmFtZX1gO1xufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheU1hdGhHUFUgZXh0ZW5kcyBOREFycmF5TWF0aCB7XG4gIHByaXZhdGUgZ3BncHU6IEdQR1BVQ29udGV4dDtcbiAgcHJpdmF0ZSB0ZXh0dXJlTWFuYWdlcjogVGV4dHVyZU1hbmFnZXI7XG4gIHByaXZhdGUgcHJvZ3JhbUNhY2hlOiB7W2tleTogc3RyaW5nXTogV2ViR0xQcm9ncmFtfSA9IHt9O1xuICBwcml2YXRlIGdwZ3B1Q3JlYXRlZExvY2FsbHk6IGJvb2xlYW47XG5cbiAgY29uc3RydWN0b3IoZ3BncHU/OiBHUEdQVUNvbnRleHQsIHNhZmVNb2RlID0gdHJ1ZSkge1xuICAgIHN1cGVyKHNhZmVNb2RlKTtcbiAgICBpZiAoZ3BncHUgPT0gbnVsbCkge1xuICAgICAgY29uc3QgZ2wgPSBncGdwdV91dGlsLmNyZWF0ZVdlYkdMQ29udGV4dCgpO1xuICAgICAgdGhpcy5ncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoZ2wpO1xuICAgICAgdGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5ID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5ncGdwdSA9IGdwZ3B1O1xuICAgICAgdGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5ID0gZmFsc2U7XG4gICAgfVxuXG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlciA9IG5ldyBUZXh0dXJlTWFuYWdlcih0aGlzLmdwZ3B1KTtcblxuICAgIG5kYXJyYXkuaW5pdGlhbGl6ZUdQVSh0aGlzLmdwZ3B1LCB0aGlzLnRleHR1cmVNYW5hZ2VyKTtcbiAgfVxuXG4gIGdldEdQR1BVQ29udGV4dCgpOiBHUEdQVUNvbnRleHQge1xuICAgIHJldHVybiB0aGlzLmdwZ3B1O1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgbWFrZUNvcHlQcm9ncmFtTmFtZSh0ZXh0dXJlU2hhcGVSQywgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDKSxcbiAgICAgICAgKCkgPT4gY29weV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB0ZXh0dXJlU2hhcGVSQywgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBjb3B5X2dwdS5jb3B5KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkMsIFswLCAwXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkMsIHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDLCBbMCwgMF0sIHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2hhcGVJbnRlcm5hbDxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIGxldCBuZXdUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXTtcblxuICAgIHN3aXRjaCAobmV3U2hhcGUubGVuZ3RoKSB7XG4gICAgICBjYXNlIDA6XG4gICAgICAgIG5ld1RleFNoYXBlID0gWzEsIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgbmV3VGV4U2hhcGUgPSBbbmV3U2hhcGVbMF0sIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgbmV3VGV4U2hhcGUgPSBbbmV3U2hhcGVbMF0sIG5ld1NoYXBlWzFdXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDM6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCBuZXdTaGFwZVsxXSAqIG5ld1NoYXBlWzJdXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBFcnJvcihcbiAgICAgICAgICAgIGBSZXNoYXBlcyBpbnRvICR7bmV3U2hhcGUubGVuZ3RofS1kaW0gbmRhcnJheSBpcyBub3QgeWV0IGAgK1xuICAgICAgICAgICAgYHN1cHBvcnRlZCBvbiBHUFVgKTtcbiAgICB9XG5cbiAgICBjb25zdCBhY3R1YWxUZXhTaGFwZSA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMobmV3VGV4U2hhcGUpO1xuICAgIGxldCBjbG9uZWRBcnJheTogVDE7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFRleFNoYXBlLCBuZXdUZXhTaGFwZSkpIHtcbiAgICAgIGNsb25lZEFycmF5ID0gdGhpcy5yZXNoYXBlVGV4dHVyZShuZGFycmF5LCBuZXdUZXhTaGFwZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNsb25lZEFycmF5ID0gdGhpcy5jbG9uZUludGVybmFsKG5kYXJyYXkpO1xuICAgIH1cbiAgICByZXR1cm4gY2xvbmVkQXJyYXkucmVzaGFwZTxUMj4obmV3U2hhcGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICBjb25zdCByZXN1bHQgPSBOREFycmF5Lm1ha2U8QXJyYXkyRD4oc2l6ZVJvd0NvbCwge1xuICAgICAgdGV4dHVyZTogdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShzaXplUm93Q29sKSxcbiAgICAgIHRleHR1cmVTaGFwZVJDOiBzaXplUm93Q29sXG4gICAgfSk7XG4gICAgdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgaW5wdXQsIGJlZ2luUm93Q29sLCBzaXplUm93Q29sLCByZXN1bHQsIFswLCAwXSwgc2l6ZVJvd0NvbCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELFxuICAgICAgZGVzdEJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb25zdCBzb3VyY2VTaGFwZVJDID0gc291cmNlLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgZGVzdFNoYXBlUkMgPSBkZXN0LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIG1ha2VDb3B5UHJvZ3JhbU5hbWUoc291cmNlU2hhcGVSQywgc291cmNlU2l6ZVJvd0NvbCwgZGVzdFNpemVSb3dDb2wpLFxuICAgICAgICAoKSA9PiBjb3B5X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHNvdXJjZVNoYXBlUkMsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3RTaXplUm93Q29sKSk7XG5cbiAgICBjb3B5X2dwdS5jb3B5KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBzb3VyY2UuZ2V0VGV4dHVyZSgpLCBzb3VyY2VTaGFwZVJDLFxuICAgICAgICBzb3VyY2VCZWdpblJvd0NvbCwgc291cmNlU2l6ZVJvd0NvbCwgZGVzdC5nZXRUZXh0dXJlKCksIGRlc3RTaGFwZVJDLFxuICAgICAgICBkZXN0QmVnaW5Sb3dDb2wsIGRlc3RTaXplUm93Q29sKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb25jYXQzREludGVybmFsKHgxOiBBcnJheTNELCB4MjogQXJyYXkzRCwgYXhpczogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgeDFUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MS5zaGFwZSk7XG4gICAgY29uc3QgeDJUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4Mi5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbFgxVGV4U2hhcGUgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQyh4MVRleFNoYXBlUkMpO1xuICAgIGxldCBjbGVhbnVwWDEgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDFUZXhTaGFwZSwgeDFUZXhTaGFwZVJDKSkge1xuICAgICAgeDEgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgxLCB4MVRleFNoYXBlUkMpO1xuICAgICAgY2xlYW51cFgxID0gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3QgYWN0dWFsWDJUZXhTaGFwZSA9IHgyLmdldFRleHR1cmVTaGFwZVJDKHgyVGV4U2hhcGVSQyk7XG4gICAgbGV0IGNsZWFudXBYMiA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYMlRleFNoYXBlLCB4MlRleFNoYXBlUkMpKSB7XG4gICAgICB4MiA9IHRoaXMucmVzaGFwZVRleHR1cmUoeDIsIHgyVGV4U2hhcGVSQyk7XG4gICAgICBjbGVhbnVwWDIgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlUkNEID1cbiAgICAgICAgY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZSh4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0NPTkNBVF9QUk9HfV8ke3gxLnNoYXBlfV8ke3gyLnNoYXBlfV8ke2F4aXN9YCxcbiAgICAgICAgKCkgPT4gY29uY2F0M2RfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgeDEuc2hhcGUsIHgyLnNoYXBlLCByZXN1bHRTaGFwZVJDRCwgYXhpcykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHJlc3VsdFNoYXBlUkNEKTtcbiAgICBjb25zdCByZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbmNhdDNkX2dwdS5jb25jYXQzRChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeDEuZ2V0VGV4dHVyZSgpLCB4Mi5nZXRUZXh0dXJlKCksIHJlc3VsdFRleCxcbiAgICAgICAgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYMSkge1xuICAgICAgeDEuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGlmIChjbGVhbnVwWDIpIHtcbiAgICAgIHgyLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyUGx1c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYywgYSwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuU0NBTEFSLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcnJheU1pbnVzU2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYSwgYywgYS5zaGFwZSwgT3BlcmFuZFR5cGUuTUFUUklYLCAnLScsIE9wZXJhbmRUeXBlLlNDQUxBUikgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYywgYSwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuU0NBTEFSLCAnLScsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpIHtcbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoIXRoaXMuZG9HUFVTaGFwZXNNYXRjaChhLCBiKSkge1xuICAgICAgYiA9IHRoaXMucmVzaGFwZVRleHR1cmUoYiwgYS5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgQUREX1NDQUxFRF9NQVRfUFJPRywgKCkgPT4gYWRkc2NhbGVkbWF0X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIGFkZHNjYWxlZG1hdF9ncHUuYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCBiLmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCBjMS5nZXRUZXh0dXJlKCksIGMyLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICBpZiAoY2xlYW51cEIpIHtcbiAgICAgIGIuZGlzcG9zZSgpO1xuICAgIH1cbiAgICAvLyBCcmluZyB0aGUgcmVzdWx0IGJhY2sgdG8gdGhlIG9yaWdpbmFsIHNoYXBlLlxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJyonLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgbmVnSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgTkVHX1BST0csICgpID0+IG5lZ19ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBuZWdfZ3B1Lm5lZyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJpdmF0ZSByZXNoYXBlVGV4dHVyZTxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgbmV3VGV4dHVyZVNoYXBlOiBbXG4gICAgbnVtYmVyLCBudW1iZXJcbiAgXSk6IFQge1xuICAgIGNvbnN0IGFUZXhTaGFwZSA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBSRVNIQVBFX1BST0csICgpID0+IHJlc2hhcGVfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUobmV3VGV4dHVyZVNoYXBlKTtcbiAgICByZXNoYXBlX2dwdS5yZXNoYXBlKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYVRleFNoYXBlWzBdLCBhVGV4U2hhcGVbMV0sXG4gICAgICAgIHJlc3VsdFRleHR1cmUsIG5ld1RleHR1cmVTaGFwZVswXSwgbmV3VGV4dHVyZVNoYXBlWzFdKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQzogbmV3VGV4dHVyZVNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF0TXVsSW50ZXJuYWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLFxuICAgICAgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IEFycmF5MkQge1xuICAgIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG4gICAgY29uc3Qgb3V0ZXJTaGFwZUEgPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMF0gOiBhLnNoYXBlWzFdO1xuICAgIGNvbnN0IG91dGVyU2hhcGVCID1cbiAgICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBiLnNoYXBlWzFdIDogYi5zaGFwZVswXTtcbiAgICBjb25zdCBvdXRTaGFwZTogW251bWJlciwgbnVtYmVyXSA9IFtvdXRlclNoYXBlQSwgb3V0ZXJTaGFwZUJdO1xuICAgIGNvbnN0IG91dFRleFNoYXBlID1cbiAgICAgICAgd2ViZ2xfdXRpbC5nZXRUZXh0dXJlU2hhcGVGcm9tTG9naWNhbFNoYXBlKHRoaXMuZ3BncHUuZ2wsIG91dFNoYXBlKTtcbiAgICBjb25zdCBvdXRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShvdXRUZXhTaGFwZSk7XG4gICAgY29uc3Qgb3V0ID0gbmV3IEFycmF5MkQoXG4gICAgICAgIG91dFNoYXBlLCB7dGV4dHVyZTogb3V0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IG91dFRleFNoYXBlfSk7XG5cbiAgICBjb25zdCBrZXkgPSBzaGFkZXJfY29tcGlsZXIubWFrZVNoYWRlcktleShbYSwgYl0sIG91dCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke01BVE1VTF9QUk9HfV8ke2tleX1fJHthT3JpZW50YXRpb259XyR7Yk9yaWVudGF0aW9ufWAsXG4gICAgICAgICgpID0+IG11bG1hdF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXIoXG4gICAgICAgICAgICBhLCBiLCBvdXQsIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG5cbiAgICBtdWxtYXRfZ3B1Lm11bHRpcGx5TWF0cml4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYi5nZXRUZXh0dXJlKCksIG91dFRleHR1cmUsXG4gICAgICAgIG91dFRleFNoYXBlKTtcblxuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYSwgYiwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuTUFUUklYLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOiBBcnJheTJEIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCB5ZXQgaW1wbGVtZW50ZWQhJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYmF0Y2hOb3JtYWxpemF0aW9uM0RJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIG1lYW46IEFycmF5M0R8QXJyYXkxRCwgdmFyaWFuY2U6IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIHZhcmlhbmNlRXBzaWxvbjogbnVtYmVyLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGxldCBjbGVhbnVwTWVhbiA9IGZhbHNlO1xuICAgIGNvbnN0IHByZWZlcnJlZE1lYW5UZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIG1lYW4ucmFuayA9PT0gMSA/IFsxLCBtZWFuLnNpemVdIDogeFRleFNoYXBlO1xuICAgIGxldCBtZWFuVGV4U2hhcGUgPSBtZWFuLmdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZE1lYW5UZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKG1lYW5UZXhTaGFwZSwgcHJlZmVycmVkTWVhblRleFNoYXBlKSkge1xuICAgICAgbWVhbiA9IHRoaXMucmVzaGFwZVRleHR1cmUobWVhbiwgcHJlZmVycmVkTWVhblRleFNoYXBlKTtcbiAgICAgIG1lYW5UZXhTaGFwZSA9IHByZWZlcnJlZE1lYW5UZXhTaGFwZTtcbiAgICAgIGNsZWFudXBNZWFuID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFZhcmlhbmNlID0gZmFsc2U7XG4gICAgY29uc3QgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIHZhcmlhbmNlLnJhbmsgPT09IDEgPyBbMSwgdmFyaWFuY2Uuc2l6ZV0gOiB4VGV4U2hhcGU7XG4gICAgbGV0IHZhcmlhbmNlVGV4U2hhcGUgPSB2YXJpYW5jZS5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRNZWFuVGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbCh2YXJpYW5jZVRleFNoYXBlLCBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlKSkge1xuICAgICAgdmFyaWFuY2UgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHZhcmlhbmNlLCBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlKTtcbiAgICAgIHZhcmlhbmNlVGV4U2hhcGUgPSBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlO1xuICAgICAgY2xlYW51cFZhcmlhbmNlID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgc2NhbGVUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXXxudWxsID0gbnVsbDtcbiAgICBsZXQgY2xlYW51cFNjYWxlID0gZmFsc2U7XG4gICAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IHByZWZlcnJlZFNjYWxlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICAgIHNjYWxlLnJhbmsgPT09IDEgPyBbMSwgc2NhbGUuc2l6ZV0gOiB4VGV4U2hhcGU7XG5cbiAgICAgIHNjYWxlVGV4U2hhcGUgPSBzY2FsZS5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRTY2FsZVRleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChzY2FsZVRleFNoYXBlLCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlKSkge1xuICAgICAgICBzY2FsZSA9IHRoaXMucmVzaGFwZVRleHR1cmUoc2NhbGUsIHByZWZlcnJlZFNjYWxlVGV4U2hhcGUpO1xuICAgICAgICBzY2FsZVRleFNoYXBlID0gcHJlZmVycmVkU2NhbGVUZXhTaGFwZTtcbiAgICAgICAgY2xlYW51cFNjYWxlID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsZXQgb2Zmc2V0VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl18bnVsbCA9IG51bGw7XG4gICAgbGV0IGNsZWFudXBPZmZzZXQgPSBmYWxzZTtcbiAgICBpZiAob2Zmc2V0ICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IHByZWZlcnJlZE9mZnNldFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgICBvZmZzZXQucmFuayA9PT0gMSA/IFsxLCBvZmZzZXQuc2l6ZV0gOiB4VGV4U2hhcGU7XG5cbiAgICAgIG9mZnNldFRleFNoYXBlID0gb2Zmc2V0LmdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZE9mZnNldFRleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChvZmZzZXRUZXhTaGFwZSwgcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGUpKSB7XG4gICAgICAgIG9mZnNldCA9IHRoaXMucmVzaGFwZVRleHR1cmUob2Zmc2V0LCBwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZSk7XG4gICAgICAgIG9mZnNldFRleFNoYXBlID0gcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGU7XG4gICAgICAgIGNsZWFudXBPZmZzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID0geC5nZXRUZXh0dXJlU2hhcGVSQygpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0JBVENITk9STV9QUk9HfV8ke3hUZXhTaGFwZX1fJHttZWFuVGV4U2hhcGV9XyR7dmFyaWFuY2VUZXhTaGFwZX1fYCArXG4gICAgICAgICAgICBgJHtzY2FsZVRleFNoYXBlIX1fJHtvZmZzZXRUZXhTaGFwZSF9XyR7dmFyaWFuY2VFcHNpbG9ufWAsXG4gICAgICAgICgpID0+IGJhdGNobm9ybV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB4VGV4U2hhcGUsIG1lYW5UZXhTaGFwZSwgdmFyaWFuY2VUZXhTaGFwZSwgb2Zmc2V0VGV4U2hhcGUsXG4gICAgICAgICAgICBzY2FsZVRleFNoYXBlLCB2YXJpYW5jZUVwc2lsb24pKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGJhdGNobm9ybV9ncHUuYmF0Y2hOb3JtYWxpemF0aW9uKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgeFRleFNoYXBlLCBtZWFuLmdldFRleHR1cmUoKSxcbiAgICAgICAgbWVhblRleFNoYXBlLCB2YXJpYW5jZS5nZXRUZXh0dXJlKCksIHZhcmlhbmNlVGV4U2hhcGUsXG4gICAgICAgIG9mZnNldCAhPSBudWxsID8gb2Zmc2V0LmdldFRleHR1cmUoKSA6IG51bGwsXG4gICAgICAgIG9mZnNldCAhPSBudWxsID8gb2Zmc2V0VGV4U2hhcGUgOiBudWxsLFxuICAgICAgICBzY2FsZSAhPSBudWxsID8gc2NhbGUuZ2V0VGV4dHVyZSgpIDogbnVsbCxcbiAgICAgICAgc2NhbGUgIT0gbnVsbCA/IHNjYWxlVGV4U2hhcGUgOiBudWxsLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cE1lYW4pIHtcbiAgICAgIG1lYW4uZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFZhcmlhbmNlKSB7XG4gICAgICB2YXJpYW5jZS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwU2NhbGUpIHtcbiAgICAgIHNjYWxlIS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwT2Zmc2V0KSB7XG4gICAgICBvZmZzZXQhLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICB4LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCB5ZXQgaW1wbGVtZW50ZWQhJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3VtSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7U1VNX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IHJlZHVjZXN1bV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIHJlZHVjZXN1bV9ncHUucmVkdWNlU3VtKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0FSR01JTl9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBhcmdtaW5tYXhfZ3B1LmdldEFyZ01pbkZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBhcmdtaW5tYXhfZ3B1LmFyZ01pbk1heChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNQVhfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWlubWF4X2dwdS5nZXRBcmdNYXhGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWlubWF4X2dwdS5hcmdNaW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWF4RXF1YWxzSW50ZXJuYWwoeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogU2NhbGFyIHtcbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCwgZG8gYSBwaHlzaWNhbCByZXNoYXBlIHNvIHRoZXkgZG8uXG4gICAgY29uc3QgYWN0dWFsWDFUZXhTaGFwZSA9IHgxLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgYWN0dWFsWDJUZXhTaGFwZSA9IHgyLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgbGV0IGNsZWFudXBYMiA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYMVRleFNoYXBlLCBhY3R1YWxYMlRleFNoYXBlKSkge1xuICAgICAgeDIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgyLCBhY3R1YWxYMVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYMiA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0FSR01BWF9FUVVBTFNfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWF4ZXF1YWxzX2dwdS5nZXRBcmdNYXhFcXVhbHNGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBhcmdtYXhlcXVhbHNfZ3B1LmFyZ01heEVxdWFscyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeDEuZ2V0VGV4dHVyZSgpLCB4Mi5nZXRUZXh0dXJlKCksIG51bVJvd3MsXG4gICAgICAgIG51bUNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgaWYgKGNsZWFudXBYMikge1xuICAgICAgeDIuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdG9wS0ludGVybmFsKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6XG4gICAgICB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCd0b3BLIEdQVSBub3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke01JTl9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBtaW5tYXhfZ3B1LmdldE1pbkZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBtaW5tYXhfZ3B1Lm1pbk1heChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNQVhfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbWlubWF4X2dwdS5nZXRNYXhGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgbWlubWF4X2dwdS5taW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZGl2aWRlSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy8nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTpcbiAgICAgIFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJy8nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTpcbiAgICAgIFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgICAgICAgIGEsIGMsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy8nLCBPcGVyYW5kVHlwZS5TQ0FMQVIpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy0nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nU3VtRXhwSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0xPR1NVTUVYUF9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBsb2dzdW1leHBfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdCA9XG4gICAgICAgIG5ldyBTY2FsYXIoe3RleHR1cmU6IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKX0pO1xuXG4gICAgcmVkdWNlc3VtX2dwdS5yZWR1Y2VTdW0oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHQuZ2V0VGV4dHVyZSgpKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgRVhQX1BST0csICgpID0+IGV4cF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBleHBfZ3B1LmV4cChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBsb2dJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBMT0dfUFJPRywgKCkgPT4gbG9nX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcbiAgICBsb2dfZ3B1LmxvZyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgUkVMVV9QUk9HLCAoKSA9PiByZWx1X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHJlbHVfZ3B1LnJlbHUoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2lnbW9pZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNJR01PSURfUFJPRywgKCkgPT4gc2lnbW9pZF9ncHUuZ2V0U2lnbW9pZEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgc2lnbW9pZF9ncHUuc2lnbW9pZChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0YW5oSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgVEFOSF9QUk9HLCAoKSA9PiB0cmlnX2dwdS5nZXRUYW5oRnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICB0cmlnX2dwdS50YW5oKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNJTl9QUk9HLCAoKSA9PiB0cmlnX2dwdS5nZXRTaW5GcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHRyaWdfZ3B1LnNpbihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzdGVwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgU1RFUF9QUk9HLCAoKSA9PiBzdGVwX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHN0ZXBfZ3B1LnN0ZXAoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBmaWVsZFNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX1BST0csIHguc2hhcGUsIG91dHB1dERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSwgYmlhc2VzICE9IG51bGxcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgb3V0cHV0RGVwdGgsIGZpZWxkU2l6ZSwgc3RyaWRlLCB6ZXJvUGFkLCBiaWFzZXMgIT0gbnVsbCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmaWVsZFNpemUpO1xuICAgIGNvbnN0IGJpYXNUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGgpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFcgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxXVGV4U2hhcGUgPSB3ZWlnaHRzLmdldFRleHR1cmVTaGFwZVJDKHdUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFdUZXhTaGFwZSwgd1RleFNoYXBlKSkge1xuICAgICAgd2VpZ2h0cyA9IHRoaXMucmVzaGFwZVRleHR1cmUod2VpZ2h0cywgd1RleFNoYXBlKTtcbiAgICAgIGNsZWFudXBXID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IGFjdHVhbEJUZXhTaGFwZSA9IGJpYXNlcy5nZXRUZXh0dXJlU2hhcGVSQyhiaWFzVGV4U2hhcGUpO1xuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbEJUZXhTaGFwZSwgYmlhc1RleFNoYXBlKSkge1xuICAgICAgICBiaWFzZXMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGJpYXNlcywgYmlhc1RleFNoYXBlKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICB4LnNoYXBlLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2dwdS5jb252b2x2ZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHdlaWdodHMuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXRUZXh0dXJlKCkgOiBudWxsLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwQiAmJiBiaWFzZXMgIT0gbnVsbCkge1xuICAgICAgYmlhc2VzLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgaW5wdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3Qgd1RleFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICBjb25zdCB5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZHkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkeSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZHksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgZHcgPSB0aGlzLmNvbnYyZERlcldlaWdodHMoeCwgZHksIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgZGIgPSB0aGlzLmNvbnYyZERlckJpYXMoZHkpO1xuICAgIGNvbnN0IGR4ID0gdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgICAgZHksIHdlaWdodHMsIG51bGwgLyoqIGJpYXNlcyAqLywgc3RyaWRlLCBwYWQpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBXKSB7XG4gICAgICB3ZWlnaHRzLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBZKSB7XG4gICAgICBkeS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHJldHVybiB7ZHgsIGRiLCBkd307XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3Qgb3JpZ0lucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG9yaWdPdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcblxuICAgIGNvbnN0IHByb2dLZXkgPSBbXG4gICAgICBDT05WMkRfVFJBTlNQT1NFX1BST0csIHguc2hhcGUsIGZpZWxkU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIG9yaWdTdHJpZGUsXG4gICAgICBvcmlnUGFkLCBiaWFzZXMgIT0gbnVsbFxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKHByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBjb252X2JhY2twcm9wX2dwdS5nZXRGcmFnbWVudFNoYWRlckNvbnZUcmFuc3Bvc2VTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZmllbGRTaXplLCBvcmlnSW5wdXREZXB0aCwgb3JpZ1N0cmlkZSwgb3JpZ1BhZCxcbiAgICAgICAgICBiaWFzZXMgIT0gbnVsbCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKFxuICAgICAgICBvcmlnSW5wdXREZXB0aCwgb3JpZ091dHB1dERlcHRoLCBmaWVsZFNpemUpO1xuICAgIGNvbnN0IGJpYXNUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3JpZ0lucHV0RGVwdGgpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFcgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxXVGV4U2hhcGUgPSB3ZWlnaHRzLmdldFRleHR1cmVTaGFwZVJDKHdUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFdUZXhTaGFwZSwgd1RleFNoYXBlKSkge1xuICAgICAgd2VpZ2h0cyA9IHRoaXMucmVzaGFwZVRleHR1cmUod2VpZ2h0cywgd1RleFNoYXBlKTtcbiAgICAgIGNsZWFudXBXID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IGFjdHVhbEJpYXNUZXhTaGFwZSA9IGJpYXNlcy5nZXRUZXh0dXJlU2hhcGVSQyhiaWFzVGV4U2hhcGUpO1xuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbEJpYXNUZXhTaGFwZSwgYmlhc1RleFNoYXBlKSkge1xuICAgICAgICBiaWFzZXMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGJpYXNlcywgYmlhc1RleFNoYXBlKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEZpZ3VyZSBvdXQgdGhlIG91dHB1dCBzaGFwZSBieSBkaWxhdGluZyB0aGUgaW5wdXQuXG4gICAgY29uc3QgZGlsYXRlZFJDID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEaWxhdGVkUkMoW3guc2hhcGVbMF0sIHguc2hhcGVbMV1dLCBvcmlnU3RyaWRlKTtcbiAgICBjb25zdCBwYWQgPSBmaWVsZFNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCByZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW2RpbGF0ZWRSQ1swXSwgZGlsYXRlZFJDWzFdLCBvcmlnT3V0cHV0RGVwdGhdLCBmaWVsZFNpemUsXG4gICAgICAgIG9yaWdJbnB1dERlcHRoLCAxLCBwYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2JhY2twcm9wX2dwdS5jb252VHJhbnNwb3NlKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgd2VpZ2h0cy5nZXRUZXh0dXJlKCksXG4gICAgICAgIGJpYXNlcyAhPSBudWxsID8gYmlhc2VzLmdldFRleHR1cmUoKSA6IG51bGwsIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBXKSB7XG4gICAgICB3ZWlnaHRzLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiaWFzZXMhLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBjb252MmREZXJXZWlnaHRzKFxuICAgICAgeDogQXJyYXkzRCwgZFk6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXk0RCB7XG4gICAgY29uc3QgaW5wdXREZXB0aCA9IHguc2hhcGVbMl07XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSBkWS5zaGFwZVsyXTtcbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX0RFUldfUFJPRywgeC5zaGFwZSwgZlNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJEZXJXZWlnaHRzU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3QgeVNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gICAgY29uc3QgeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh5U2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFkgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxZVGV4U2hhcGUgPSBkWS5nZXRUZXh0dXJlU2hhcGVSQyh5VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxZVGV4U2hhcGUsIHlUZXhTaGFwZSkpIHtcbiAgICAgIGRZID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkWSwgeVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBZID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2JhY2twcm9wX2dwdS5kZXJXZWlnaHRzKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgZFkuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZFkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlaWdodHNTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1NoYXBlNEQoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5NEQ+KFxuICAgICAgICB3ZWlnaHRzU2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgY29udjJkRGVyQmlhcyhkWTogQXJyYXkzRCk6IEFycmF5MUQge1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgcHJvZ0tleSA9IFtDT05WMkRfREVSQl9QUk9HLCBkWS5zaGFwZV0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKHByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBjb252X2JhY2twcm9wX2dwdS5nZXRGcmFnbWVudFNoYWRlckRlckJpYXNTb3VyY2UoZFkuc2hhcGUpO1xuICAgIH0pO1xuICAgIGNvbnN0IHlUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZFkuc2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBsZXQgY2xlYW51cFkgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxZVGV4U2hhcGUgPSBkWS5nZXRUZXh0dXJlU2hhcGVSQyh5VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxZVGV4U2hhcGUsIHlUZXhTaGFwZSkpIHtcbiAgICAgIGRZID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkWSwgeVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBZID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGgpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuZGVyQmlhcyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgZFkuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZFkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkxRD4oXG4gICAgICAgIFtvdXRwdXREZXB0aF0sIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBwb29sKFxuICAgICAgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoeC5zaGFwZSwgZlNpemUsIHguc2hhcGVbMl0sIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHBvb2xSZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHBvb2xfZ3B1LnBvb2xDb21tb24oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCBwb29sUmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIHJlc3VsdFNoYXBlLCB7dGV4dHVyZTogcG9vbFJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9vbFByb2dLZXkgPVxuICAgICAgICBbTUFYX1BPT0xfUFJPRywgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWF4UG9vbFByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWF4X3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFNvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMucG9vbChtYXhQb29sUHJvZ3JhbSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5Qb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBtaW5Qb29sUHJvZ0tleSA9XG4gICAgICAgIFtNSU5fUE9PTF9QUk9HLCB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWRdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBtaW5Qb29sUHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0obWluUG9vbFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBtaW5fcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJNaW5Qb29sU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5wb29sKG1pblBvb2xQcm9ncmFtLCB4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGF2Z1Bvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGF2Z1Bvb2xQcm9nS2V5ID1cbiAgICAgICAgW0FWR19QT09MX1BST0csIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZF0uam9pbignXycpO1xuICAgIGNvbnN0IGF2Z1Bvb2xQcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShhdmdQb29sUHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGF2Z19wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlckF2Z1Bvb2xTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLnBvb2woYXZnUG9vbFByb2dyYW0sIHgsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoXG4gICAgICBkeTogQXJyYXkzRCwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Byb2dLZXkgPSBbXG4gICAgICBNQVhfUE9PTF9QT1NJVElPTlNfUFJPRywgeC5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBtYXhQb29sUG9zaXRpb25zUHJvZ3JhbSA9XG4gICAgICAgIHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0obWF4UG9vbFBvc2l0aW9uc1Byb2dLZXksICgpID0+IHtcbiAgICAgICAgICByZXR1cm4gbWF4X3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFBvc2l0aW9uc1NvdXJjZShcbiAgICAgICAgICAgICAgeC5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgICAgICB9KTtcblxuICAgIGNvbnN0IG1heFBvb2xSZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZlNpemUsIHguc2hhcGVbMl0sIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgIGNvbnN0IG1heFBvb2xSZXN1bHRUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QobWF4UG9vbFJlc3VsdFNoYXBlKTtcbiAgICBjb25zdCBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4ID1cbiAgICAgICAgdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShtYXhQb29sUmVzdWx0VGV4U2hhcGUpO1xuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBtYXhfcG9vbF9ncHUubWF4UG9vbENvbW1vbihcbiAgICAgICAgdGhpcy5ncGdwdSwgbWF4UG9vbFBvc2l0aW9uc1Byb2dyYW0sIHguZ2V0VGV4dHVyZSgpLFxuICAgICAgICBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4LCBtYXhQb29sUmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29uc3QgbWF4UG9vbEJhY2twcm9wUHJvZ0tleSA9IFtcbiAgICAgIE1BWF9QT09MX0JBQ0tQUk9QX1BST0csIGR5LnNoYXBlLCBmU2l6ZSwgb3JpZ1N0cmlkZSwgb3JpZ1BhZFxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xCYWNrcHJvcFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBtYXhfcG9vbF9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQmFja3Byb3AoXG4gICAgICAgICAgZHkuc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IGR5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsRHlUZXhTaGFwZSA9IGR5LmdldFRleHR1cmVTaGFwZVJDKGR5VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwRHkgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsRHlUZXhTaGFwZSwgZHlUZXhTaGFwZSkpIHtcbiAgICAgIGR5ID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkeSwgZHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwRHkgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGRpbGF0ZWREeVJDID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEaWxhdGVkUkMoW2R5LnNoYXBlWzBdLCBkeS5zaGFwZVsxXV0sIG9yaWdTdHJpZGUpO1xuICAgIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgcmVzdWx0U2hhcGVSQ0QgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFtkaWxhdGVkRHlSQ1swXSwgZGlsYXRlZER5UkNbMV0sIGR5LnNoYXBlWzJdXSwgZlNpemUsIGR5LnNoYXBlWzJdLCAxLFxuICAgICAgICBwYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZVJDRCk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBtYXhfcG9vbF9iYWNrcHJvcF9ncHUubWF4UG9vbEJhY2twcm9wKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBkeS5nZXRUZXh0dXJlKCksIG1heFBvb2xQb3NpdGlvbnNSZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBEeSkge1xuICAgICAgZHkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlci5yZWxlYXNlVGV4dHVyZShcbiAgICAgICAgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCwgbWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIHJlc3VsdFNoYXBlUkNELCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRCB7XG4gICAgY29uc3QgcHJvZ3JhbUtleSA9XG4gICAgICAgIFtSRVNJWkVfQklMSU5FQVJfUFJPRywgeC5zaGFwZSwgbmV3U2hhcGUyRCwgYWxpZ25Db3JuZXJzXS5qb2luKCdfJyk7XG5cbiAgICBjb25zdCBuZXdTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgW25ld1NoYXBlMkRbMF0sIG5ld1NoYXBlMkRbMV0sIHguc2hhcGVbMl1dO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChuZXdTaGFwZVJDRCk7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgcHJvZ3JhbUtleSxcbiAgICAgICAgKCkgPT4gcmVzaXplX2JpbGluZWFyX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHguc2hhcGUsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVycykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgcmVzaXplX2JpbGluZWFyX2dwdS5yZXNpemVCaWxpbmVhcihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHJlc3VsdFRleHR1cmUsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIG5ld1NoYXBlUkNELCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcml2YXRlIGdldEFuZFNhdmVQcm9ncmFtKHByb2dyYW1LZXk6IHN0cmluZywgZ2V0U2hhZGVyU291cmNlOiAoKSA9PiBzdHJpbmcpOlxuICAgICAgV2ViR0xQcm9ncmFtIHtcbiAgICBpZiAoIShwcm9ncmFtS2V5IGluIHRoaXMucHJvZ3JhbUNhY2hlKSkge1xuICAgICAgdGhpcy5wcm9ncmFtQ2FjaGVbcHJvZ3JhbUtleV0gPVxuICAgICAgICAgIHRoaXMuZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRTaGFkZXJTb3VyY2UoKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnByb2dyYW1DYWNoZVtwcm9ncmFtS2V5XTtcbiAgfVxuXG4gIHByaXZhdGUgYWRkU3ViTXVsRGl2KFxuICAgICAgYTogTkRBcnJheSwgYjogTkRBcnJheSwgcmVzdWx0U2hhcGU6IG51bWJlcltdLFxuICAgICAgb3BlcmFuZEE6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmFuZFR5cGUsXG4gICAgICBvcFR5cGU6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmF0aW9uLFxuICAgICAgb3BlcmFuZEI6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmFuZFR5cGUpOiBOREFycmF5IHtcbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcblxuICAgIGNvbnN0IGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVI7XG4gICAgbGV0IGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVI7XG5cbiAgICBsZXQgbG9naWNhbEJUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXTtcblxuICAgIGlmIChvcGVyYW5kQSA9PT0gT3BlcmFuZFR5cGUuTUFUUklYICYmIG9wZXJhbmRCID09PSBPcGVyYW5kVHlwZS5NQVRSSVgpIHtcbiAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSk7XG5cbiAgICAgIGlmIChhLmluR1BVKCkpIHtcbiAgICAgICAgLy8gUHJlZmVyIEIgdG8gaGF2ZSB0aGUgc2hhcGUgb2YgQS5cbiAgICAgICAgYi5nZXRUZXh0dXJlU2hhcGVSQyhhLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgICAgfSBlbHNlIGlmIChiLmluR1BVKCkpIHtcbiAgICAgICAgLy8gUHJlZmVyIEEgdG8gaGF2ZSB0aGUgc2hhcGUgb2YgQi5cbiAgICAgICAgYS5nZXRUZXh0dXJlU2hhcGVSQyhiLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBhVGV4U2hhcGUgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgICBjb25zdCBiVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgICBsb2dpY2FsQlRleFNoYXBlID0gYlRleFNoYXBlO1xuXG4gICAgICBpZiAoYS5yYW5rID09PSAxKSB7XG4gICAgICAgIC8vIFdoZW4gZGVhbGluZyB3aXRoIHZlY3RvcnMsIHdlIGNhbiBzYW1wbGUgaW4gdHJhbnNwb3NlZCB3YXkgd2l0aG91dFxuICAgICAgICAvLyB0aGUgbmVlZCB0byBkbyBwaHlzaWNhbCByZXNoYXBlLlxuICAgICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYlRleFNoYXBlLCBhVGV4U2hhcGUpKSB7XG4gICAgICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRDtcbiAgICAgICAgICBsb2dpY2FsQlRleFNoYXBlID0gW2JUZXhTaGFwZVsxXSwgYlRleFNoYXBlWzBdXTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYVRleFNoYXBlLCBsb2dpY2FsQlRleFNoYXBlKSkge1xuICAgICAgICBiID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiLCBhVGV4U2hhcGUpO1xuICAgICAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuICAgICAgICBsb2dpY2FsQlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgICBjbGVhbnVwQiA9IHRydWU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ2ljYWxCVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgfVxuXG4gICAgY29uc3QgYVRleFNoYXBlID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGJUZXhTaGFwZSA9IGIuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW1LZXkgPSBbXG4gICAgICBBRERfU1VNX01VTF9ESVZfUFJPRywgb3BlcmFuZEEsIGFPcmllbnRhdGlvbiwgb3BUeXBlLCBvcGVyYW5kQixcbiAgICAgIGJPcmllbnRhdGlvblxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBwcm9ncmFtS2V5LFxuICAgICAgICAoKSA9PiBhZGRzdWJtdWxkaXZfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgb3BlcmFuZEEsIGFPcmllbnRhdGlvbiwgb3BUeXBlLCBvcGVyYW5kQiwgYk9yaWVudGF0aW9uKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlU2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbXG4gICAgICBNYXRoLm1heChhVGV4U2hhcGVbMF0sIGxvZ2ljYWxCVGV4U2hhcGVbMF0pLFxuICAgICAgTWF0aC5tYXgoYVRleFNoYXBlWzFdLCBsb2dpY2FsQlRleFNoYXBlWzFdKVxuICAgIF07XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID1cbiAgICAgICAgdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXh0dXJlU2hhcGUpO1xuXG4gICAgYWRkc3VibXVsZGl2X2dwdS5hZGRTdWJNdWxEaXYoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCBhVGV4U2hhcGUsIGIuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiVGV4U2hhcGUsIHJlc3VsdFRleHR1cmUsIHJlc3VsdFRleHR1cmVTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cEIpIHtcbiAgICAgIGIuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2UoXG4gICAgICAgIHJlc3VsdFNoYXBlLFxuICAgICAgICB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleHR1cmVTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBkb0dQVVNoYXBlc01hdGNoKGE6IE5EQXJyYXksIGI6IE5EQXJyYXkpOiBib29sZWFuIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUpO1xuICAgIGlmIChhLmluR1BVKCkpIHtcbiAgICAgIC8vIFByZWZlciBCIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEEuXG4gICAgICBiLmdldFRleHR1cmVTaGFwZVJDKGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgfSBlbHNlIGlmIChiLmluR1BVKCkpIHtcbiAgICAgIC8vIFByZWZlciBBIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEIuXG4gICAgICBhLmdldFRleHR1cmVTaGFwZVJDKGIuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgfVxuICAgIHJldHVybiB1dGlsLmFycmF5c0VxdWFsKGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSwgYi5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgfVxuXG4gIGdldFRleHR1cmVNYW5hZ2VyKCk6IFRleHR1cmVNYW5hZ2VyIHtcbiAgICByZXR1cm4gdGhpcy50ZXh0dXJlTWFuYWdlcjtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgZm9yIChjb25zdCBwcm9ncmFtS2V5IGluIHRoaXMucHJvZ3JhbUNhY2hlKSB7XG4gICAgICBpZiAodGhpcy5wcm9ncmFtQ2FjaGUuaGFzT3duUHJvcGVydHkocHJvZ3JhbUtleSkpIHtcbiAgICAgICAgdGhpcy5ncGdwdS5kZWxldGVQcm9ncmFtKHRoaXMucHJvZ3JhbUNhY2hlW3Byb2dyYW1LZXldKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlci5kaXNwb3NlKCk7XG5cbiAgICBpZiAodGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5KSB7XG4gICAgICB0aGlzLmdwZ3B1LmRpc3Bvc2UoKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge1RleHR1cmVNYW5hZ2VyfSBmcm9tICcuL3dlYmdsL3RleHR1cmVfbWFuYWdlcic7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbi8vIFRoZXNlIGdsb2JhbCB2YXJpYWJsZXMgbmVlZCB0byBiZSBpbml0aWFsaXplZCB0byBudWxsIHNvIHRoYXQgY2xvc3VyZSBrbm93c1xuLy8gbm90IHRvIHNlYWwgdGhlbS5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IEdQR1BVOiBHUEdQVUNvbnRleHQgPSBudWxsITtcbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IFRFWFRVUkVfTUFOQUdFUjogVGV4dHVyZU1hbmFnZXIgPSBudWxsITtcblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTkRBcnJheURhdGEge1xuICB2YWx1ZXM/OiBGbG9hdDMyQXJyYXk7XG4gIHRleHR1cmU/OiBXZWJHTFRleHR1cmU7XG4gIC8qKiBbcm93cywgY29sdW1uc10gc2hhcGUgb2YgdGhlIHRleHR1cmUuICovXG4gIHRleHR1cmVTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXTtcbn1cblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplR1BVKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHRleHR1cmVNYW5hZ2VyOiBUZXh0dXJlTWFuYWdlcikge1xuICBHUEdQVSA9IGdwZ3B1O1xuICBURVhUVVJFX01BTkFHRVIgPSB0ZXh0dXJlTWFuYWdlcjtcbn1cblxuZnVuY3Rpb24gdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCkge1xuICBpZiAoR1BHUFUgPT0gbnVsbCB8fCBURVhUVVJFX01BTkFHRVIgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignR1BVIG5vdCBpbnRpYWxpemVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBOREFycmF5IHtcbiAgLyoqIFRoZSBzaGFwZSBvZiB0aGUgbmRhcnJheS4gKi9cbiAgc2hhcGU6IG51bWJlcltdO1xuICAvKiogTnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBuZGFycmF5LiAqL1xuICBzaXplOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE51bWJlciBvZiBlbGVtZW50cyB0byBza2lwIGluIGVhY2ggZGltZW5zaW9uIHdoZW4gaW5kZXhpbmcuIFNlZVxuICAgKiBodHRwczovL2RvY3Muc2NpcHkub3JnL2RvYy9udW1weS9yZWZlcmVuY2UvZ2VuZXJhdGVkL251bXB5Lm5kYXJyYXkuc3RyaWRlcy5odG1sXG4gICAqL1xuICBwcm90ZWN0ZWQgc3RyaWRlczogbnVtYmVyW107XG5cbiAgcHJpdmF0ZSBkYXRhOiBOREFycmF5RGF0YTtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3Ioc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIC8vIFNhbml0eSBjaGVja3MuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudmFsdWVzICE9IG51bGwgfHwgZGF0YS50ZXh0dXJlICE9IG51bGwsXG4gICAgICAgICdFaXRoZXIgYHZhbHVlc2Agb3IgYHRleHR1cmVgIG11c3QgYmUgZGVmaW5lZCcpO1xuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudGV4dHVyZSA9PSBudWxsIHx8IChkYXRhLnRleHR1cmVTaGFwZVJDICE9IG51bGwpLFxuICAgICAgICAnYHRleHR1cmVTaGFwZWAgbXVzdCBiZSBkZWZpbmVkIHdoZW4gYHRleHR1cmVgIGlzIGRlZmluZWQnKTtcblxuICAgIHRoaXMuc2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShzaGFwZSk7XG5cbiAgICBpZiAoZGF0YS52YWx1ZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy5zaXplID09PSBkYXRhLnZhbHVlcy5sZW5ndGgsXG4gICAgICAgICAgJ0NvbnN0cnVjdGluZyBuZGFycmF5IG9mIHNoYXBlICgnICsgdGhpcy5zaXplICsgJykgc2hvdWxkIG1hdGNoIHRoZScgK1xuICAgICAgICAgICAgICAnIGxlbmd0aCBvZiB2YWx1ZXMgKCcgKyBkYXRhLnZhbHVlcy5sZW5ndGggKyAnKScpO1xuICAgIH1cblxuICAgIHRoaXMuc2hhcGUgPSBzaGFwZTtcbiAgICB0aGlzLmRhdGEgPSBkYXRhO1xuICAgIGNvbnN0IGRpbSA9IHRoaXMuc2hhcGUubGVuZ3RoO1xuXG4gICAgaWYgKGRpbSA8IDIpIHtcbiAgICAgIHRoaXMuc3RyaWRlcyA9IFtdO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBMYXN0IGRpbWVuc2lvbiBoYXMgaW1wbGljaXQgc3RyaWRlIG9mIDEsIHRodXMgaGF2aW5nIEQtMSAoaW5zdGVhZCBvZiBEKVxuICAgICAgLy8gc3RyaWRlcy5cbiAgICAgIHRoaXMuc3RyaWRlcyA9IG5ldyBBcnJheShkaW0gLSAxKTtcbiAgICAgIHRoaXMuc3RyaWRlc1tkaW0gLSAyXSA9IHRoaXMuc2hhcGVbZGltIC0gMV07XG4gICAgICBmb3IgKGxldCBpID0gZGltIC0gMzsgaSA+PSAwOyAtLWkpIHtcbiAgICAgICAgdGhpcy5zdHJpZGVzW2ldID0gdGhpcy5zdHJpZGVzW2kgKyAxXSAqIHRoaXMuc2hhcGVbaSArIDFdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSBvZiB6ZXJvcyB3aXRoIHRoZSBzcGVjaWZpZWQgc2hhcGUuICovXG4gIHN0YXRpYyB6ZXJvczxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KHNoYXBlLCB7dmFsdWVzfSk7XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgb2YgemVyb3Mgd2l0aCB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgemVyb3NMaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3MoYW5vdGhlci5zaGFwZSkgYXMgVDtcbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSB3aXRoIHRoZSBzYW1lIHZhbHVlcy9zaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuICovXG4gIHN0YXRpYyBsaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gYW5vdGhlci5nZXRWYWx1ZXMoKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGFub3RoZXIuc2hhcGUsIHt2YWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ha2VzIGEgbmV3IG5kYXJyYXkgd2l0aCB0aGUgcHJvdmlkZWQgc2hhcGUgYW5kIHZhbHVlcy4gVmFsdWVzIHNob3VsZCBiZSBpblxuICAgKiBhIGZsYXQgYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgbWFrZTxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSk6IFQge1xuICAgIHN3aXRjaCAoc2hhcGUubGVuZ3RoKSB7XG4gICAgICBjYXNlIDA6XG4gICAgICAgIHJldHVybiBuZXcgU2NhbGFyKGRhdGEpIGFzIFQ7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTFEKGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IEFycmF5MkQoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgY2FzZSAzOlxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXkzRChzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgNDpcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTREKFxuICAgICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgICAgICAgICAgICBzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IE5EQXJyYXkoc2hhcGUsIGRhdGEpIGFzIGFueTtcbiAgICB9XG4gIH1cblxuICAvKiogUmVzaGFwZXMgdGhlIGN1cnJlbnQgbmRhcnJheSBpbnRvIHRoZSBwcm92aWRlZCBzaGFwZS4gKi9cbiAgcmVzaGFwZTxUIGV4dGVuZHMgTkRBcnJheT4obmV3U2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgaWYgKHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgbmV3U2hhcGUpKSB7XG4gICAgICAvLyBOby1vcC5cbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHJldHVybiB0aGlzIGFzIGFueTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5zaXplID09PSB1dGlsLnNpemVGcm9tU2hhcGUobmV3U2hhcGUpLFxuICAgICAgICAnbmV3IHNoYXBlIGFuZCBvbGQgc2hhcGUgbXVzdCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBlbGVtZW50cy4nKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHRoaXMuZGF0YSk7XG4gIH1cblxuICBhc1NjYWxhcigpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KHRoaXMuc2l6ZSA9PT0gMSwgJ1RoZSBhcnJheSBtdXN0IGhhdmUgb25seSAxIGVsZW1lbnQuJyk7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxTY2FsYXI+KFtdKTtcbiAgfVxuXG4gIGFzMUQoKTogQXJyYXkxRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTFEPihbdGhpcy5zaXplXSk7XG4gIH1cblxuICBhczJEKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogQXJyYXkyRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTJEPihbcm93cywgY29sdW1uc10pO1xuICB9XG5cbiAgYXMzRChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgZGVwdGg6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkzRD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoXSk7XG4gIH1cblxuICBhczREKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBkZXB0aDogbnVtYmVyLCBkZXB0aDI6IG51bWJlcik6IEFycmF5NEQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXk0RD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoLCBkZXB0aDJdKTtcbiAgfVxuXG4gIGdldCByYW5rKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc2hhcGUubGVuZ3RoO1xuICB9XG5cbiAgZ2V0KC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpW2luZGV4XTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCAuLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIHRoaXMuc2V0KHRoaXMuZ2V0KC4uLmxvY3MpICsgdmFsdWUsIC4uLmxvY3MpO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHRoaXMuZ2V0VmFsdWVzKClbaW5kZXhdID0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IG51bWJlcltdKTogbnVtYmVyIHtcbiAgICBsZXQgaW5kZXggPSBsb2NzW2xvY3MubGVuZ3RoIC0gMV07XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsb2NzLmxlbmd0aCAtIDE7ICsraSkge1xuICAgICAgaW5kZXggKz0gdGhpcy5zdHJpZGVzW2ldICogbG9jc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4O1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogbnVtYmVyW10ge1xuICAgIGNvbnN0IGxvY3M6IG51bWJlcltdID0gbmV3IEFycmF5KHRoaXMuc2hhcGUubGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBsb2NzW2ldID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlc1tpXSk7XG4gICAgICBpbmRleCAtPSBsb2NzW2ldICogdGhpcy5zdHJpZGVzW2ldO1xuICAgIH1cbiAgICBsb2NzW2xvY3MubGVuZ3RoIC0gMV0gPSBpbmRleDtcbiAgICByZXR1cm4gbG9jcztcbiAgfVxuXG4gIGZpbGwodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKCkuZmlsbCh2YWx1ZSk7XG4gIH1cblxuICBnZXREYXRhKCk6IE5EQXJyYXlEYXRhIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhO1xuICB9XG5cbiAgZ2V0VmFsdWVzKCk6IEZsb2F0MzJBcnJheSB7XG4gICAgaWYgKHRoaXMuZGF0YS52YWx1ZXMgPT0gbnVsbCkge1xuICAgICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgICB0aGlzLmRhdGEudmFsdWVzID0gR1BHUFUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyFbMF0sXG4gICAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDIVsxXSk7XG4gICAgICB0aGlzLmRpc3Bvc2VUZXh0dXJlKCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRhdGEudmFsdWVzO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGxvYWRUb0dQVShwcmVmZXJyZWRUZXhTaGFwZT86IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgICB0aHJvd0lmR1BVTm90SW5pdGlhbGl6ZWQoKTtcbiAgICB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMgPSB3ZWJnbF91dGlsLmdldFRleHR1cmVTaGFwZUZyb21Mb2dpY2FsU2hhcGUoXG4gICAgICAgIEdQR1BVLmdsLCB0aGlzLnNoYXBlLCBwcmVmZXJyZWRUZXhTaGFwZSk7XG4gICAgdGhpcy5kYXRhLnRleHR1cmUgPVxuICAgICAgICBURVhUVVJFX01BTkFHRVIuYWNxdWlyZVRleHR1cmUodGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDKTtcblxuICAgIEdQR1BVLnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDWzFdLCB0aGlzLmRhdGEudmFsdWVzISk7XG5cbiAgICB0aGlzLmRhdGEudmFsdWVzID0gbnVsbCE7XG4gIH1cblxuICBnZXRUZXh0dXJlKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogV2ViR0xUZXh0dXJlIHtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgPT0gbnVsbCkge1xuICAgICAgdGhpcy51cGxvYWRUb0dQVShwcmVmZXJyZWRTaGFwZVJDKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlITtcbiAgfVxuXG4gIGdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyA9PSBudWxsKSB7XG4gICAgICB0aGlzLnVwbG9hZFRvR1BVKHByZWZlcnJlZFNoYXBlUkMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDITtcbiAgfVxuXG4gIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5kYXRhLnZhbHVlcyA9IG51bGwhO1xuICAgIHRoaXMuc2hhcGUgPSBudWxsITtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgdGhpcy5kaXNwb3NlVGV4dHVyZSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZGlzcG9zZVRleHR1cmUoKSB7XG4gICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgVEVYVFVSRV9NQU5BR0VSLnJlbGVhc2VUZXh0dXJlKFxuICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyEpO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlID0gbnVsbCE7XG4gICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDID0gbnVsbCE7XG4gIH1cblxuICBpbkdQVSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbDtcbiAgfVxuXG4gIGVxdWFscyh0OiBOREFycmF5KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgdC5zaGFwZSkgJiZcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLmdldFZhbHVlcygpLCB0LmdldFZhbHVlcygpKTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIHJhbmRGdW5jdGlvbjogKCkgPT4gbnVtYmVyKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIHZhbHVlc1tpXSA9IHJhbmRGdW5jdGlvbigpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oc2hhcGUsIHt2YWx1ZXN9KTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kTm9ybWFsPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2KSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFRydW5jYXRlZE5vcm1hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2LCB0cnVlKSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFVuaWZvcm08VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSwgYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgICByZXR1cm4gTkRBcnJheS5yYW5kPFQ+KHNoYXBlLCAoKSA9PiB1dGlsLnJhbmRVbmlmb3JtKGEsIGIpKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2NhbGFyIGV4dGVuZHMgTkRBcnJheSB7XG4gIGNvbnN0cnVjdG9yKGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgaWYgKGRhdGEudGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICBkYXRhLnRleHR1cmVTaGFwZVJDID0gWzEsIDFdO1xuICAgIH1cbiAgICBzdXBlcihbXSwgZGF0YSk7XG4gIH1cblxuICBzdGF0aWMgbmV3KHZhbHVlOiBudW1iZXIpIHtcbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KFt2YWx1ZV0pfSk7XG4gIH1cblxuICBzdGF0aWMgWkVSTyA9IFNjYWxhci5uZXcoMCk7XG4gIHN0YXRpYyBPTkUgPSBTY2FsYXIubmV3KDEpO1xuICBzdGF0aWMgVFdPID0gU2NhbGFyLm5ldygyKTtcbiAgc3RhdGljIE5FR19PTkUgPSBTY2FsYXIubmV3KC0xKTtcblxuICBnZXQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVswXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVswXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpWzBdICs9IHZhbHVlO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTFEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyXTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIGNvbnN0IHNoYXBlID0gKGRhdGEudmFsdWVzICE9IG51bGwpID9cbiAgICAgICAgW2RhdGEudmFsdWVzLmxlbmd0aF0gOlxuICAgICAgICBbdXRpbC5zaXplRnJvbVNoYXBlKGRhdGEudGV4dHVyZVNoYXBlUkMhKV07XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICB9XG5cbiAgc3RhdGljIG5ldyh2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGluZmVycmVkU2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAgIGBFcnJvciBjb25zdHJ1Y3RpbmcgQXJyYXkxRC4gU2hhcGUgb2YgdmFsdWVzICR7aW5mZXJyZWRTaGFwZX0gaXMgYCArXG4gICAgICAgICAgICAgIGBub3QgMSBkaW1lbnNpb25hbC5gKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTFEKHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVtpXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW2ldID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVtpXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jOiBbbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIGxvY1swXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXJdIHtcbiAgICByZXR1cm4gW2luZGV4XTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlcl0pOiBBcnJheTFEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTFEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5MkQgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3Ioc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAyLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAyJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcoXG4gICAgICBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdmFsdWVzOiBGbG9hdDMyQXJyYXl8bnVtYmVyW118bnVtYmVyW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5MkQuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTJEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgal0gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIGxvY3NbMV07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgICByZXR1cm4gW01hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTApLCBpbmRleCAlIHRoaXMuc3RyaWRlMF07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTJEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5M0QgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAzLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAzJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gIH1cblxuICBzdGF0aWMgbmV3KFxuICAgICAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5M0QuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTNEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsga10gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0pOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnN0cmlkZTAgKiBsb2NzWzBdICsgdGhpcy5zdHJpZGUxICogbG9jc1sxXSArIGxvY3NbMl07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICAgIGNvbnN0IGkgPSBNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUwKTtcbiAgICBpbmRleCAtPSBpICogdGhpcy5zdHJpZGUwO1xuICAgIHJldHVybiBbaSwgTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSksIGluZGV4ICUgdGhpcy5zdHJpZGUxXTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkzRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTREIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTI6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSA0LCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCA0Jyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gICAgdGhpcy5zdHJpZGUyID0gdGhpcy5zdHJpZGVzWzJdO1xuICB9XG5cbiAgc3RhdGljIG5ldyhcbiAgICAgIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXVtdKSB7XG4gICAgaWYgKCEodmFsdWVzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSkge1xuICAgICAgY29uc3QgaW5mZXJyZWRTaGFwZSA9IHV0aWwuaW5mZXJTaGFwZSh2YWx1ZXMpO1xuICAgICAgaWYgKGluZmVycmVkU2hhcGUubGVuZ3RoID4gMSkge1xuICAgICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKFxuICAgICAgICAgICAgc2hhcGUsIGluZmVycmVkU2hhcGUsXG4gICAgICAgICAgICBgRXJyb3Igd2hlbiBjb25zdHJ1Y3RpbmcgQXJyYXk0RC4gU2hhcGUgb2YgdmFsdWVzIGAgK1xuICAgICAgICAgICAgICAgIGAke2luZmVycmVkU2hhcGV9IGRvZXMgbm90IG1hdGNoIHRoZSBwcm92aWRlZCBzaGFwZSBgICtcbiAgICAgICAgICAgICAgICBgJHtzaGFwZX0uIGApO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbmV3IEFycmF5NEQoc2hhcGUsIHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVxuICAgICAgICBbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyB0aGlzLnN0cmlkZTIgKiBrICsgbF0gKz0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIHRoaXMuc3RyaWRlMSAqIGxvY3NbMV0gK1xuICAgICAgICB0aGlzLnN0cmlkZTIgKiBsb2NzWzJdICsgbG9jc1szXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBpID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMCk7XG4gICAgaW5kZXggLT0gaSAqIHRoaXMuc3RyaWRlMDtcbiAgICBjb25zdCBqID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSk7XG4gICAgaW5kZXggLT0gaiAqIHRoaXMuc3RyaWRlMTtcbiAgICByZXR1cm4gW2ksIGosIE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTIpLCBpbmRleCAlIHRoaXMuc3RyaWRlMl07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXk0RCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXk0RD4oc2hhcGUpO1xuICB9XG59XG5cbnR5cGUgQXJyYXlEYXRhID0gRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW118bnVtYmVyW11bXVtdfG51bWJlcltdW11bXVtdO1xuXG5mdW5jdGlvbiB0b1R5cGVkQXJyYXkoYTogQXJyYXlEYXRhKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIChhIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSA/IGEgOiBuZXcgRmxvYXQzMkFycmF5KHV0aWwuZmxhdHRlbihhKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEFTY2FsYXI7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QlNjYWxhcjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZUZXhlbCA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgYSA9IHRleHR1cmUyRChtYXRyaXhBLCByZXN1bHRVVikucjtcbiAgICAgIGZsb2F0IGIgPSB0ZXh0dXJlMkQobWF0cml4QiwgcmVzdWx0VVYpLnI7XG4gICAgICBmbG9hdCBhU2NhbGFyID0gdGV4dHVyZTJEKG1hdHJpeEFTY2FsYXIsIGhhbGZUZXhlbCkucjtcbiAgICAgIGZsb2F0IGJTY2FsYXIgPSB0ZXh0dXJlMkQobWF0cml4QlNjYWxhciwgaGFsZlRleGVsKS5yO1xuICAgICAgdmVjMiBhYlNjYWxlZCA9IHZlYzIoYSwgYikgKiB2ZWMyKGFTY2FsYXIsIGJTY2FsYXIpO1xuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChhYlNjYWxlZC54ICsgYWJTY2FsZWQueSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhZGRTY2FsZWRNYXRyaWNlcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBhZGRTY2FsZWRNYXRyaWNlc1Byb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICBhOiBXZWJHTFRleHR1cmUsIGI6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgYVNjYWxhcjogV2ViR0xUZXh0dXJlLCBiU2NhbGFyOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShhZGRTY2FsZWRNYXRyaWNlc1Byb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhU2NhbGFyLCAnbWF0cml4QVNjYWxhcicsIDIpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYlNjYWxhciwgJ21hdHJpeEJTY2FsYXInLCAzKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZEFkZFNjYWxlZE1hdHJpY2VzRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBiOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIGFTY2FsYXI6IG51bWJlciwgYlNjYWxhcjogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgY29uc3QgYVRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IGJUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBhU2NhbGFyVGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSgxLCAxKTtcbiAgY29uc3QgYlNjYWxhclRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGNvbnN0IHJlc3VsdFRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG5cbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXgsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYlRleCwgcm93cywgY29sdW1ucywgYik7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhU2NhbGFyVGV4LCAxLCAxLCBuZXcgRmxvYXQzMkFycmF5KFthU2NhbGFyXSkpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYlNjYWxhclRleCwgMSwgMSwgbmV3IEZsb2F0MzJBcnJheShbYlNjYWxhcl0pKTtcblxuICBhZGRTY2FsZWRNYXRyaWNlcyhcbiAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhVGV4LCBiVGV4LCByb3dzLCBjb2x1bW5zLCBhU2NhbGFyVGV4LCBiU2NhbGFyVGV4LFxuICAgICAgcmVzdWx0VGV4KTtcblxuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleCwgcm93cywgY29sdW1ucyk7XG5cbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXgpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFTY2FsYXJUZXgpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGJTY2FsYXJUZXgpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vbWF0aCc7XG5cbmltcG9ydCAqIGFzIGJpbmFyeW9wX2dwdSBmcm9tICcuL2JpbmFyeW9wX2dwdSc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IHR5cGUgT3BlcmF0aW9uID0gJysnIHwgJy0nIHwgJyonIHwgJy8nO1xuXG5leHBvcnQgZW51bSBPcGVyYW5kVHlwZSB7XG4gIE1BVFJJWCxcbiAgU0NBTEFSXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBhVHlwZTogT3BlcmFuZFR5cGUsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sIG9wOiBPcGVyYXRpb24sXG4gICAgYlR5cGU6IE9wZXJhbmRUeXBlLCBiT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogc3RyaW5nIHtcbiAgY29uc3QgYVVWID0gb3BlcmFuZFRvU2hhZGVyU25pcHBldChhVHlwZSwgYU9yaWVudGF0aW9uKTtcbiAgY29uc3QgYlVWID0gb3BlcmFuZFRvU2hhZGVyU25pcHBldChiVHlwZSwgYk9yaWVudGF0aW9uKTtcbiAgY29uc3QgcmVzdWx0T3AgPSBgZ2xfRnJhZ0NvbG9yID0gdmVjNChhICR7b3B9IGIsIDAsIDAsIDApO2A7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoYVVWLCBiVVYsIHJlc3VsdE9wKTtcbn1cblxuZnVuY3Rpb24gb3BlcmFuZFRvU2hhZGVyU25pcHBldChcbiAgICBvcGVyYW5kOiBPcGVyYW5kVHlwZSwgb3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogc3RyaW5nIHtcbiAgc3dpdGNoIChvcGVyYW5kKSB7XG4gICAgY2FzZSBPcGVyYW5kVHlwZS5NQVRSSVg6XG4gICAgICByZXR1cm4gJ3Jlc3VsdFVWJyArXG4gICAgICAgICAgKG9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSID8gJy5zdCcgOiAnLnRzJyk7XG4gICAgY2FzZSBPcGVyYW5kVHlwZS5TQ0FMQVI6XG4gICAgICByZXR1cm4gJ3ZlYzIoMC41LCAwLjUpJztcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmtub3duIG9wZXJhbmQgdHlwZScpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhZGRTdWJNdWxEaXYoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBXZWJHTFRleHR1cmUsXG4gICAgYlNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LmJpbmFyeU9wKFxuICAgICAgZ3BncHUsIHByb2dyYW0sIGEsIGFTaGFwZVJvd0NvbCwgYiwgYlNoYXBlUm93Q29sLCByZXN1bHQsXG4gICAgICByZXN1bHRTaGFwZVJvd0NvbCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJQbHVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICcrJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeE1pbnVzU2NhbGFyRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBhU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIGI6IG51bWJlcixcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5NQVRSSVgsIGFPcmllbnRhdGlvbiwgJy0nLCBPcGVyYW5kVHlwZS5TQ0FMQVIsXG4gICAgICBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgYSwgYVNoYXBlLCBuZXcgRmxvYXQzMkFycmF5KFtiXSksIFsxLCAxXSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNjYWxhck1pbnVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICctJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNjYWxhclRpbWVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICcqJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFRpbWVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBiOiBGbG9hdDMyQXJyYXksIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuTUFUUklYLCBhT3JpZW50YXRpb24sICcqJywgT3BlcmFuZFR5cGUuTUFUUklYLCBiT3JpZW50YXRpb24pO1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LnVwbG9hZEJpbmFyeU9wRG93bmxvYWQoYSwgc2hhcGUsIGIsIHNoYXBlLCBzcmMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTWF0cml4UGx1c01hdHJpeERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCBzaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBzcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgIE9wZXJhbmRUeXBlLk1BVFJJWCwgYU9yaWVudGF0aW9uLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCwgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKGEsIHNoYXBlLCBiLCBzaGFwZSwgc3JjKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgYXJnbWlubWF4X2dwdSBmcm9tICcuL2FyZ21pbm1heF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEI7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGFyZ01heEEgPSBnZXRBcmdNaW5NYXgobWF0cml4QSk7XG4gICAgICBmbG9hdCBhcmdNYXhCID0gZ2V0QXJnTWluTWF4KG1hdHJpeEIpO1xuICAgICAgZmxvYXQgdmFsdWU7XG4gICAgICBpZiAoaXNOYU4oYXJnTWF4QSkpIHtcbiAgICAgICAgdmFsdWUgPSBhcmdNYXhBO1xuICAgICAgfSBlbHNlIGlmIChpc05hTihhcmdNYXhCKSkge1xuICAgICAgICB2YWx1ZSA9IGFyZ01heEI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IGZsb2F0KGFyZ01heEEgPT0gYXJnTWF4Qik7XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01heEVxdWFsc0ZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIFtcbiAgICBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCksXG4gICAgYXJnbWlubWF4X2dwdS5nZXRGcmFnbWVudFNoYWRlckdldEFyZ01pbk1heFNvdXJjZSgnPicsIHJvd3MsIGNvbHVtbnMpLFxuICAgIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpXG4gIF0uam9pbignXFxuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcmdNYXhFcXVhbHMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWF4RXF1YWxzUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYjogV2ViR0xUZXh0dXJlLCBudW1Sb3dzOiBudW1iZXIsIG51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1heEVxdWFsc1Byb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZ2V0QXJnTWluTWF4KG1hdHJpeEEpLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZnVuY3Rpb24gZ2V0QXJnTWluTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGNvbXBPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIFtcbiAgICBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCksXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoY29tcE9wLCByb3dzLCBjb2x1bW5zKSxcbiAgICBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKVxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXJnTWluRnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gZ2V0QXJnTWluTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucywgJzwnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICc+Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldEFyZ01pbk1heFNvdXJjZShcbiAgICBjb21wT3A6IHN0cmluZywgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpIHtcbiAgcmV0dXJuIGBcbiAgICBjb25zdCB2ZWMyIGRpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgZmxvYXQgZ2V0QXJnTWluTWF4KGluIHNhbXBsZXIyRCBtYXRyaXgpIHtcbiAgICAgIHZlYzIgYmVzdENSID0gdmVjMigwLCAwKTtcbiAgICAgIGZsb2F0IGJlc3RWYWx1ZSA9IHRleHR1cmUyRChtYXRyaXgsIGJlc3RDUikucjtcblxuICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgZGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgZGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIGNyID0gdmVjMihjLCByKTtcbiAgICAgICAgICB2ZWMyIHV2ID0gKGNyICsgaGFsZkNSKSAvIGRpbUNSO1xuICAgICAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeCwgdXYpLnI7XG4gICAgICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodmFsdWUgJHtjb21wT3B9IGJlc3RWYWx1ZSkge1xuICAgICAgICAgICAgYmVzdFZhbHVlID0gdmFsdWU7XG4gICAgICAgICAgICBiZXN0Q1IgPSBjcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBiZXN0Q1IueCArIChiZXN0Q1IueSAqIGRpbUNSLngpO1xuICAgIH1cbiAgYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFyZ01pbk1heChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBtaW5NYXhQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obWluTWF4UHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vcG9vbF9ncHUnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJBdmdQb29sU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdhdmcnLCBmYWxzZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhdmdQb29sKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgeFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0sIG1lYW5UZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIHZhcmlhbmNlVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSxcbiAgICBvZmZzZXRUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsXG4gICAgc2NhbGVUZXhTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXXxudWxsLCB2YXJpYW5jZUVwc2lsb24gPSAwLjAwMSk6IHN0cmluZyB7XG4gIGxldCBvZmZzZXRTYW1wbGVyU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldENvb3Jkc1NuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFVWU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0VmFsdWVTbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRPcGVyYXRpb25TbmlwcGV0ID0gJzAuMCc7XG5cbiAgbGV0IHNjYWxlU2FtcGxlclNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlU2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlQ29vcmRzU25pcHBldCA9ICcnO1xuICBsZXQgc2NhbGVVVlNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlVmFsdWVTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZU9wZXJhdGlvblNuaXBwZXQgPSAnJztcblxuICBpZiAob2Zmc2V0VGV4U2hhcGVSQyAhPSBudWxsKSB7XG4gICAgb2Zmc2V0U2FtcGxlclNuaXBwZXQgPSAndW5pZm9ybSBzYW1wbGVyMkQgb2Zmc2V0Oyc7XG4gICAgb2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSBgY29uc3QgdmVjMiBvZmZzZXRTaGFwZUNSID0gdmVjMihcbiAgICAgICAgICAgICR7b2Zmc2V0VGV4U2hhcGVSQ1sxXX0sICR7b2Zmc2V0VGV4U2hhcGVSQ1swXX0pO2A7XG4gICAgb2Zmc2V0Q29vcmRzU25pcHBldCA9ICd2ZWMyIG9mZnNldENvb3Jkc0NSID0gbW9kKHlUZXhDUiwgb2Zmc2V0U2hhcGVDUik7JztcbiAgICBvZmZzZXRVVlNuaXBwZXQgPVxuICAgICAgICAndmVjMiBvZmZzZXRVViA9IChvZmZzZXRDb29yZHNDUiArIGhhbGZDUikgLyBvZmZzZXRTaGFwZUNSOyc7XG4gICAgb2Zmc2V0VmFsdWVTbmlwcGV0ID0gJ2Zsb2F0IG9mZnNldFZhbHVlID0gdGV4dHVyZTJEKG9mZnNldCwgb2Zmc2V0VVYpLnI7JztcbiAgICBvZmZzZXRPcGVyYXRpb25TbmlwcGV0ID0gJ29mZnNldFZhbHVlJztcbiAgfVxuXG4gIGlmIChzY2FsZVRleFNoYXBlUkMgIT0gbnVsbCkge1xuICAgIHNjYWxlU2FtcGxlclNuaXBwZXQgPSAndW5pZm9ybSBzYW1wbGVyMkQgc2NhbGU7JztcbiAgICBzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gYGNvbnN0IHZlYzIgc2NhbGVTaGFwZUNSID0gdmVjMihcbiAgICAgICAgICAgICR7c2NhbGVUZXhTaGFwZVJDWzFdfSwgJHtzY2FsZVRleFNoYXBlUkNbMF19KTtgO1xuICAgIHNjYWxlQ29vcmRzU25pcHBldCA9ICd2ZWMyIHNjYWxlQ29vcmRzQ1IgPSBtb2QoeVRleENSLCBzY2FsZVNoYXBlQ1IpOyc7XG4gICAgc2NhbGVVVlNuaXBwZXQgPSAndmVjMiBzY2FsZVVWID0gKHNjYWxlQ29vcmRzQ1IgKyBoYWxmQ1IpIC8gc2NhbGVTaGFwZUNSOyc7XG4gICAgc2NhbGVWYWx1ZVNuaXBwZXQgPSAnZmxvYXQgc2NhbGVWYWx1ZSA9IHRleHR1cmUyRChzY2FsZSwgc2NhbGVVVikucjsnO1xuICAgIHNjYWxlT3BlcmF0aW9uU25pcHBldCA9ICdpbnYgKj0gc2NhbGVWYWx1ZTsnO1xuICB9XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtZWFuO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHZhcmlhbmNlO1xuICAgICR7b2Zmc2V0U2FtcGxlclNuaXBwZXR9XG4gICAgJHtzY2FsZVNhbXBsZXJTbmlwcGV0fVxuXG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIG1lYW5TaGFwZUNSID0gdmVjMigke21lYW5UZXhTaGFwZVJDWzFdfSwgJHttZWFuVGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgdmFyaWFuY2VTaGFwZUNSID0gdmVjMihcbiAgICAgICAgJHt2YXJpYW5jZVRleFNoYXBlUkNbMV19LCAke3ZhcmlhbmNlVGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgJHtvZmZzZXRTaGFwZUluaXRpYWxpemF0aW9uU25pcHBldH1cbiAgICAke3NjYWxlU2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXR9XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IGZsb2F0IHZhcmlhbmNlRXBzaWxvbiA9ICR7dmFyaWFuY2VFcHNpbG9ufTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgdmVjMiBtZWFuQ29vcmRzQ1IgPSBtb2QoeVRleENSLCBtZWFuU2hhcGVDUik7XG4gICAgICB2ZWMyIHZhcmlhbmNlQ29vcmRzQ1IgPSBtb2QoeVRleENSLCB2YXJpYW5jZVNoYXBlQ1IpO1xuICAgICAgJHtvZmZzZXRDb29yZHNTbmlwcGV0fVxuICAgICAgJHtzY2FsZUNvb3Jkc1NuaXBwZXR9XG5cbiAgICAgIHZlYzIgbWVhblVWID0gKG1lYW5Db29yZHNDUiArIGhhbGZDUikgLyBtZWFuU2hhcGVDUjtcbiAgICAgIHZlYzIgdmFyaWFuY2VVViA9ICh2YXJpYW5jZUNvb3Jkc0NSICsgaGFsZkNSKSAvIHZhcmlhbmNlU2hhcGVDUjtcbiAgICAgICR7b2Zmc2V0VVZTbmlwcGV0fVxuICAgICAgJHtzY2FsZVVWU25pcHBldH1cblxuICAgICAgZmxvYXQgeFZhbHVlID0gdGV4dHVyZTJEKHgsIHJlc3VsdFVWKS5yO1xuICAgICAgZmxvYXQgbWVhblZhbHVlID0gdGV4dHVyZTJEKG1lYW4sIG1lYW5VVikucjtcbiAgICAgIGZsb2F0IHZhcmlhbmNlVmFsdWUgPSB0ZXh0dXJlMkQodmFyaWFuY2UsIHZhcmlhbmNlVVYpLnI7XG4gICAgICAke29mZnNldFZhbHVlU25pcHBldH1cbiAgICAgICR7c2NhbGVWYWx1ZVNuaXBwZXR9XG5cbiAgICAgIGZsb2F0IGludiA9IDEuMCAvIHNxcnQodmFyaWFuY2VWYWx1ZSArIHZhcmlhbmNlRXBzaWxvbik7XG4gICAgICAke3NjYWxlT3BlcmF0aW9uU25pcHBldH1cbiAgICAgIGZsb2F0IHhUaW1lc0ludiA9IHhWYWx1ZSAqIGludjtcbiAgICAgIGZsb2F0IG1lYW5UaW1lc0ludldpdGhPZmZzZXQgPSAke29mZnNldE9wZXJhdGlvblNuaXBwZXR9XG4gICAgICAgICAgLSBtZWFuVmFsdWUgKiBpbnY7XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoeFRpbWVzSW52ICsgbWVhblRpbWVzSW52V2l0aE9mZnNldCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiYXRjaE5vcm1hbGl6YXRpb24oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgeFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBtZWFuOiBXZWJHTFRleHR1cmUsXG4gICAgbWVhblNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCB2YXJpYW5jZTogV2ViR0xUZXh0dXJlLFxuICAgIHZhcmlhbmNlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIG9mZnNldDogV2ViR0xUZXh0dXJlfG51bGwsXG4gICAgb2Zmc2V0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl18bnVsbCwgc2NhbGU6IFdlYkdMVGV4dHVyZXxudWxsLFxuICAgIHNjYWxlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl18bnVsbCwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShtZWFuLCAnbWVhbicsIDEpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUodmFyaWFuY2UsICd2YXJpYW5jZScsIDIpO1xuICBsZXQgbmV4dEluZGV4ID0gMztcbiAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKG9mZnNldCwgJ29mZnNldCcsIG5leHRJbmRleCk7XG4gICAgbmV4dEluZGV4Kys7XG4gIH1cbiAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc2NhbGUsICdzY2FsZScsIG5leHRJbmRleCk7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgYVJlc3VsdFVWOiBzdHJpbmcsIGJSZXN1bHRVVjogc3RyaW5nLCBvcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGEgPSB0ZXh0dXJlMkQobWF0cml4QSwgJHthUmVzdWx0VVZ9KS5yO1xuICAgICAgZmxvYXQgYiA9IHRleHR1cmUyRChtYXRyaXhCLCAke2JSZXN1bHRVVn0pLnI7XG4gICAgICAke29wfVxuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluYXJ5T3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBXZWJHTFRleHR1cmUsXG4gICAgYlNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBGbG9hdDMyQXJyYXksXG4gICAgYlNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBmcmFnbWVudFNoYWRlclNvdXJjZTogc3RyaW5nKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlKTtcblxuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoYVNoYXBlWzBdLCBhU2hhcGVbMV0pO1xuICBjb25zdCBiVGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoYlNoYXBlWzBdLCBiU2hhcGVbMV0pO1xuXG4gIGNvbnN0IHJlc3VsdFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgIFtNYXRoLm1heChhU2hhcGVbMF0sIGJTaGFwZVswXSksIE1hdGgubWF4KGFTaGFwZVsxXSwgYlNoYXBlWzFdKV07XG5cbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocmVzdWx0U2hhcGVbMF0sIHJlc3VsdFNoYXBlWzFdKTtcblxuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIGFTaGFwZVswXSwgYVNoYXBlWzFdLCBhKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJUZXh0dXJlLCBiU2hhcGVbMF0sIGJTaGFwZVsxXSwgYik7XG5cbiAgYmluYXJ5T3AoXG4gICAgICBncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIGFTaGFwZSwgYlRleHR1cmUsIGJTaGFwZSwgcmVzdWx0VGV4dHVyZSxcbiAgICAgIHJlc3VsdFNoYXBlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgIHJlc3VsdFRleHR1cmUsIHJlc3VsdFNoYXBlWzBdLCByZXN1bHRTaGFwZVsxXSk7XG5cbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYlRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHJlc3VsdFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICB4MVNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHgyU2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICByZXN1bHRTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBheGlzOiBudW1iZXIpOiBzdHJpbmcge1xuICBjb25zdCB4MVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHgxU2hhcGVSQ0QpO1xuICBjb25zdCB4MlRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHgyU2hhcGVSQ0QpO1xuXG4gIGNvbnN0IHlBeGVzID0gWyd5UicsICd5QycsICd5RCddO1xuICBjb25zdCBjb25jYXRBeGlzID0geUF4ZXNbYXhpc107XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDI7XG5cbiAgICBjb25zdCB2ZWMyIHgxU2hhcGVDUiA9IHZlYzIoJHt4MVRleFNoYXBlUkNbMV19LCAke3gxVGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgeDJTaGFwZUNSID0gdmVjMigke3gyVGV4U2hhcGVSQ1sxXX0uMCwgJHt4MlRleFNoYXBlUkNbMF19LjApO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgeUQpLlxuICAgICAgZmxvYXQgeVIgPSB5VGV4Q1IueTtcbiAgICAgIGZsb2F0IHlDID0gZmxvb3IoeVRleENSLnggLyAke3Jlc3VsdFNoYXBlUkNEWzJdfS4wKTtcbiAgICAgIGZsb2F0IHlEID0gbW9kKHlUZXhDUi54LCAke3Jlc3VsdFNoYXBlUkNEWzJdfS4wKTtcblxuICAgICAgZmxvYXQgdmFsdWUgPSAwLjA7XG5cbiAgICAgIGlmICgke2NvbmNhdEF4aXN9IDwgJHt4MVNoYXBlUkNEW2F4aXNdfS4wKSB7XG4gICAgICAgIC8vIE1hcCB5UiwgeUMsIHlEIGJhY2sgdG8geDEgY29vcmRpbmF0ZXMuXG4gICAgICAgIHZlYzIgeDFDUiA9IHZlYzIoeUMgKiAke3gxU2hhcGVSQ0RbMl19LjAgKyB5RCwgeVIpO1xuICAgICAgICB2ZWMyIHgxVVYgPSAoeDFDUiArIGhhbGZDUikgLyB4MVNoYXBlQ1I7XG4gICAgICAgIHZhbHVlID0gdGV4dHVyZTJEKHgxLCB4MVVWKS5yO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgJHtjb25jYXRBeGlzfSA9ICR7Y29uY2F0QXhpc30gLSAke3gxU2hhcGVSQ0RbYXhpc119LjA7XG5cbiAgICAgICAgLy8gTWFwIHlSLCB5QywgeUQgYmFjayB0byB4MiBjb29yZGluYXRlcy5cbiAgICAgICAgdmVjMiB4MkNSID0gdmVjMih5QyAqICR7eDJTaGFwZVJDRFsyXX0uMCArIHlELCB5Uik7XG4gICAgICAgIHZlYzIgeDJVViA9ICh4MkNSICsgaGFsZkNSKSAvIHgyU2hhcGVDUjtcbiAgICAgICAgdmFsdWUgPSB0ZXh0dXJlMkQoeDIsIHgyVVYpLnI7XG4gICAgICB9XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsdWUsIDAuMCwgMC4wLCAwLjApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29uY2F0M0QoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4MTogV2ViR0xUZXh0dXJlLFxuICAgIHgyOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByZXN1bHRTaGFwZVJDWzBdLCByZXN1bHRTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgxLCAneDEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgyLCAneDInLCAxKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbnZfZ3B1IGZyb20gJy4vY29udl9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckRlcldlaWdodHNTb3VyY2UoXG4gICAgeFNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlcixcbiAgICBvdXRwdXREZXB0aDogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZDogbnVtYmVyKSB7XG4gIGNvbnN0IGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkID1cbiAgICAgIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTtcbiAgY29uc3QgaW5wdXREZXB0aCA9IHhTaGFwZVJvd0NvbERlcHRoWzJdO1xuXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSb3dDb2xEZXB0aCk7XG5cbiAgY29uc3QgeVNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgeFNoYXBlUm93Q29sRGVwdGgsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgY29uc3QgeU51bVJvd3MgPSB5U2hhcGVbMF07XG4gIGNvbnN0IHlOdW1Db2xzID0geVNoYXBlWzFdO1xuICBjb25zdCB5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeVNoYXBlKTtcblxuICBjb25zdCBmU2l6ZVRpbWVzSW5wdXREZXB0aCA9IGZTaXplICogaW5wdXREZXB0aDtcblxuICBjb25zdCBwcm9sb2d1ZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBkeTtcbiAgYDtcblxuICByZXR1cm4gcHJvbG9ndWUgKyAnXFxuJyArIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkICsgJ1xcbicgK1xuICAgICAgYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHt5VGV4U2hhcGVSQ1sxXX0sICR7eVRleFNoYXBlUkNbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgd1RleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHdUZXhSLCB3VGV4QykgdG8gNEQgKHdSLCB3QywgZDEsIGQyKS5cbiAgICAgIGZsb2F0IHdSID0gZmxvb3Iod1RleENSLnkgLyAke2ZTaXplVGltZXNJbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IHdUZXhSTGVmdG92ZXIgPSB3VGV4Q1IueSAtIHdSICogJHtmU2l6ZVRpbWVzSW5wdXREZXB0aH0uMDtcbiAgICAgIGZsb2F0IHdDID0gZmxvb3Iod1RleFJMZWZ0b3ZlciAvICR7aW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMSA9IG1vZCh3VGV4UkxlZnRvdmVyLCAke2lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSB3VGV4Q1IueDtcblxuICAgICAgLy8gQ29udm9sdmUgeCg/LCA/LCBkMSkgd2l0aCBkeSg6LCA6LCBkMikgdG8gZ2V0IGR3KHdSLCB3QywgZDEsIGQyKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgeVIgPSAwLjA7IHlSIDwgJHt5TnVtUm93c30uMDsgeVIgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IHhSID0gd1IgKyB5UiAqICR7c3RyaWRlfS4wIC0gJHt6ZXJvUGFkfS4wO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuICAgICAgICBmbG9hdCB5VGV4UiA9IHlSO1xuICAgICAgICBmb3IgKGZsb2F0IHlDID0gMC4wOyB5QyA8ICR7eU51bUNvbHN9LjA7IHlDICs9IDEuMCkge1xuICAgICAgICAgIGZsb2F0IHhDID0gd0MgKyB5QyAqICR7c3RyaWRlfS4wIC0gJHt6ZXJvUGFkfS4wO1xuXG4gICAgICAgICAgLy8gTWFwIGZyb20gM0QgKHhSLCB4QywgZDEpIHRvIDJEICh4VGV4UiwgeFRleEMpLlxuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh5UiwgeUMsIGQyKSB0byAyRCAoeVRleFIsIHlUZXhDKS5cbiAgICAgICAgICB2ZWMyIHh5VGV4QyA9IHZlYzIoeEMsIHlDKSAqIHZlYzIoJHtpbnB1dERlcHRofS4wLCAke291dHB1dERlcHRofS4wKSArXG4gICAgICAgICAgICAgICAgICAgICAgICB2ZWMyKGQxLCBkMik7XG4gICAgICAgICAgZmxvYXQgeFRleEMgPSB4eVRleEMueDtcbiAgICAgICAgICBmbG9hdCB5VGV4QyA9IHh5VGV4Qy55O1xuXG4gICAgICAgICAgLy8gUmVhZCBkeSh5UiwgeUMsIGQyKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMih5VGV4QywgeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgLy8gUmVhZCB4KHhSLCB4QywgZDEpIChwb3RlbnRpYWxseSB6ZXJvLXBhZGRlZCkuXG4gICAgICAgICAgZmxvYXQgeFZhbHVlID1cbiAgICAgICAgICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKHgsIHhTaGFwZUNSLCB2ZWMyKHhUZXhDLCB4VGV4UikpO1xuXG4gICAgICAgICAgZG90UHJvZCArPSAoeFZhbHVlICogZHlWYWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckNvbnZUcmFuc3Bvc2VTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIG9yaWdJbnB1dERlcHRoOiBudW1iZXIsXG4gICAgb3JpZ1N0cmlkZTogbnVtYmVyLCBvcmlnUGFkOiBudW1iZXIsIGhhc0JpYXM6IGJvb2xlYW4pIHtcbiAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgY29uc3QgW3hSb3dzLCB4Q29scywgb3JpZ091dHB1dERlcHRoXSA9IHhTaGFwZVJDRDtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcbiAgY29uc3Qgd1RleFNoYXBlUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUob3JpZ0lucHV0RGVwdGgsIG9yaWdPdXRwdXREZXB0aCwgZlNpemUpO1xuXG4gIGNvbnN0IGdldEJpYXNWYWx1ZSA9IGhhc0JpYXMgP1xuICAgICAgY29udl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJHZXRCaWFzVmFsdWVTb3VyY2Uob3JpZ0lucHV0RGVwdGgpIDpcbiAgICAgICcnO1xuICBjb25zdCBiaWFzUHJvbG9ndWUgPSBoYXNCaWFzID8gJ3VuaWZvcm0gc2FtcGxlcjJEIGJpYXNlczsnIDogJyc7XG4gIGNvbnN0IGJpYXNPcGVyYXRpb24gPSBoYXNCaWFzID8gJ2RvdFByb2QgKz0gZ2V0Qmlhc1ZhbHVlKGJpYXNlcywgZDIpOycgOiAnJztcblxuICBjb25zdCBwcm9sb2d1ZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB3ZWlnaHRzO1xuICAgICR7Ymlhc1Byb2xvZ3VlfVxuICAgIGA7XG5cbiAgcmV0dXJuIHByb2xvZ3VlICsgJ1xcbicgKyBnZXRCaWFzVmFsdWUgKyAnXFxuJyArXG4gICAgICBgXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIHhTaGFwZUNSID0gdmVjMigke3hUZXhTaGFwZVJDWzFdfSwgJHt4VGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgd1NoYXBlQ1IgPSB2ZWMyKCR7d1RleFNoYXBlUkNbMV19LCAke3dUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQyKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtvcmlnSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMiA9IG1vZCh5VGV4Q1IueCwgJHtvcmlnSW5wdXREZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgeFJDQ29ybmVyID0gdmVjMih5UiwgeUMpIC0gdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggdyg6LCA6LCBkMiwgZDEpIHRvIGdldCB5KHlSLCB5QywgZDIpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcblxuICAgICAgICBmbG9hdCB4UiA9ICh4UkNvcm5lciArIHdSKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgLy8gVE9ETyhzbWlsa292KTogU3BsaWNlIHRoaXMgd2l0aCBhbm90aGVyIHZlcnNpb24gd2hlcmUgeW91IGNhbGxcbiAgICAgICAgLy8gZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoKS4gSGVyZSBhbmQgYmVsb3cuXG4gICAgICAgIGlmICh4UiA8IDAuMCB8fCB4UiA+PSAke3hSb3dzfS4wIHx8IGZyYWN0KHhSKSA+IDAuMCkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgZmxvYXQgd1JQZXJtID0gJHtmU2l6ZX0uMCAtIDEuMCAtIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG5cbiAgICAgICAgICBmbG9hdCB4QyA9ICh4Q0Nvcm5lciArIHdDKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgICBpZiAoeEMgPCAwLjAgfHwgeEMgPj0gJHt4Q29sc30uMCB8fCBmcmFjdCh4QykgPiAwLjApIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IHdDUGVybSA9ICR7ZlNpemV9LjAgLSAxLjAgLSB3QztcbiAgICAgICAgICBmbG9hdCB3VGV4UiA9IHdSUGVybSAqICR7ZlNpemV9LjAgKiAke29yaWdJbnB1dERlcHRofS4wICtcbiAgICAgICAgICAgICAgICAgICAgICAgIHdDUGVybSAqICR7b3JpZ0lucHV0RGVwdGh9LjAgKyBkMjtcblxuICAgICAgICAgIGZvciAoZmxvYXQgZDEgPSAwLjA7IGQxIDwgJHtvcmlnT3V0cHV0RGVwdGh9LjA7IGQxICs9IDEuMCkge1xuICAgICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7b3JpZ091dHB1dERlcHRofS4wICsgZDE7XG4gICAgICAgICAgICBmbG9hdCB3VGV4QyA9IGQxO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHgoeFIsIHhDLCBkMSkuXG4gICAgICAgICAgICB2ZWMyIHhVViA9ICh2ZWMyKHhUZXhDLCB4VGV4UikgKyBoYWxmQ1IpIC8geFNoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB4VmFsdWUgPSB0ZXh0dXJlMkQoeCwgeFVWKS5yO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHcod1JQZXJtLCB3Q1Blcm0sIGQyLCBkMSkuXG4gICAgICAgICAgICB2ZWMyIHdVViA9ICh2ZWMyKHdUZXhDLCB3VGV4UikgKyBoYWxmQ1IpIC8gd1NoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB3VmFsdWUgPSB0ZXh0dXJlMkQod2VpZ2h0cywgd1VWKS5yO1xuXG4gICAgICAgICAgICBkb3RQcm9kICs9IHhWYWx1ZSAqIHdWYWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgICR7Ymlhc09wZXJhdGlvbn1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckRlckJpYXNTb3VyY2UoXG4gICAgZHlTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKSB7XG4gIGNvbnN0IGR5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZHlTaGFwZVJDRCk7XG4gIGNvbnN0IFt5TnVtUm93cywgeU51bUNvbHMsIG91dHB1dERlcHRoXSA9IGR5U2hhcGVSQ0Q7XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgZHlTaGFwZUNSID0gdmVjMigke2R5VGV4U2hhcGVSQ1sxXX0sICR7ZHlUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIGJpYXNUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIFRoZSBiaWFzIHRleHR1cmUgUkMgc2hhcGUgaXMgWzEsIGQyXS5cbiAgICAgIGZsb2F0IGQyID0gYmlhc1RleENSLng7XG5cbiAgICAgIGZsb2F0IGRlckJpYXMgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHlSID0gMC4wOyB5UiA8ICR7eU51bVJvd3N9LjA7IHlSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB5VGV4UiA9IHlSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgeUMgPSAwLjA7IHlDIDwgJHt5TnVtQ29sc30uMDsgeUMgKz0gMS4wKSB7XG4gICAgICAgICAgLy8gTWFwIGZyb20gM0QgKHlSLCB5QywgZDIpIHRvIDJEICh5VGV4UiwgeVRleEMpLlxuICAgICAgICAgIGZsb2F0IHlUZXhDID0geUMgKiAke291dHB1dERlcHRofS4wICsgZDI7XG5cbiAgICAgICAgICAvLyBSZWFkIGR5KHlSLCB5QywgZDIpLlxuICAgICAgICAgIHZlYzIgZHlVViA9ICh2ZWMyKHlUZXhDLCB5VGV4UikgKyBoYWxmQ1IpIC8gZHlTaGFwZUNSO1xuICAgICAgICAgIGZsb2F0IGR5VmFsdWUgPSB0ZXh0dXJlMkQoZHksIGR5VVYpLnI7XG5cbiAgICAgICAgICBkZXJCaWFzICs9IGR5VmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZGVyQmlhcywgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZXJCaWFzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgZHlUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGR5VGV4LCAnZHknLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlcldlaWdodHMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgZHlUZXg6IFdlYkdMVGV4dHVyZSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHhUZXgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb252VHJhbnNwb3NlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeFRleDogV2ViR0xUZXh0dXJlLFxuICAgIHdlaWdodHNUZXg6IFdlYkdMVGV4dHVyZSwgYmlhc2VzVGV4OiBXZWJHTFRleHR1cmV8bnVsbCxcbiAgICByZXN1bHRUZXg6IFdlYkdMVGV4dHVyZSwgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHhUZXgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh3ZWlnaHRzVGV4LCAnd2VpZ2h0cycsIDEpO1xuICBpZiAoYmlhc2VzVGV4ICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYmlhc2VzVGV4LCAnYmlhc2VzJywgMik7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB3ZWlnaHRzO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGJpYXNlcztcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZChpbiBzYW1wbGVyMkQgbWF0cml4LCB2ZWMyIG1hdHJpeFNoYXBlQ1IsXG4gICAgICAgIHZlYzIgcmVxdWVzdGVkQ1IpIHtcbiAgICAgIHZlYzIgdXYgPSAocmVxdWVzdGVkQ1IgKyB2ZWMyKDAuNSwgMC41KSkgLyBtYXRyaXhTaGFwZUNSO1xuICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4LCB1dikucjtcbiAgICAgIGJvb2wgbGVzc1RoYW5aZXJvID0gYW55KGxlc3NUaGFuKHV2LCB2ZWMyKDAsIDApKSk7XG4gICAgICBib29sIGdyZWF0ZXJUaGFuT25lID0gYW55KGdyZWF0ZXJUaGFuKHV2LCB2ZWMyKDEsIDEpKSk7XG4gICAgICBib29sIG91dHNpZGUgPSBsZXNzVGhhblplcm8gfHwgZ3JlYXRlclRoYW5PbmU7XG4gICAgICByZXR1cm4gbWl4KHZhbHVlLCAwLjAsIGZsb2F0KG91dHNpZGUpKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyQ29udm9sdmVTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIG91dHB1dERlcHRoOiBudW1iZXIsXG4gICAgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyLCBoYXNCaWFzOiBib29sZWFuKSB7XG4gIGNvbnN0IFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdID0geFNoYXBlUkNEO1xuXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSQ0QpO1xuICBjb25zdCB3VGV4U2hhcGVSQyA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuXG4gIHJldHVybiBgXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIHhTaGFwZUNSID0gdmVjMigke3hUZXhTaGFwZVJDWzFdfSwgJHt4VGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgd1NoYXBlQ1IgPSB2ZWMyKCR7d1RleFNoYXBlUkNbMV19LCAke3dUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQyKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtvdXRwdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMiA9IG1vZCh5VGV4Q1IueCwgJHtvdXRwdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCB3VGV4QyA9IGQyO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAqIHZlYzIoJHtzdHJpZGV9LCAke3N0cmlkZX0pIC1cbiAgICAgICAgICB2ZWMyKCR7cGFkfS4wLCAke3BhZH0uMCk7XG4gICAgICBmbG9hdCB4UkNvcm5lciA9IHhSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgeENDb3JuZXIgPSB4UkNDb3JuZXIueTtcblxuICAgICAgLy8gQ29udm9sdmUgeCg/LCA/LCBkMSkgd2l0aCB3KDosIDosIGQxLCBkMikgdG8gZ2V0IHkoeVIsIHlDLCBkMikuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHhSQ29ybmVyICsgd1I7XG4gICAgICAgIGZsb2F0IHhUZXhSID0geFI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHhDQ29ybmVyICsgd0M7XG5cbiAgICAgICAgICBmb3IgKGZsb2F0IGQxID0gMC4wOyBkMSA8ICR7aW5wdXREZXB0aH0uMDsgZDEgKz0gMS4wKSB7XG4gICAgICAgICAgICBmbG9hdCB4VGV4QyA9IHhDICogJHtpbnB1dERlcHRofS4wICsgZDE7XG4gICAgICAgICAgICBmbG9hdCB3VGV4UiA9IHdSICogJHtmU2l6ZSAqIGlucHV0RGVwdGh9LjAgK1xuICAgICAgICAgICAgICAgIHdDICogJHtpbnB1dERlcHRofS4wICsgZDE7XG5cbiAgICAgICAgICAgIGZsb2F0IHhWYWx1ZSA9XG4gICAgICAgICAgICAgICAgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoeCwgeFNoYXBlQ1IsIHZlYzIoeFRleEMsIHhUZXhSKSk7XG5cbiAgICAgICAgICAgIC8vIFJlYWQgdyh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICAgICAgICB2ZWMyIHdVViA9ICh2ZWMyKHdUZXhDLCB3VGV4UikgKyBoYWxmQ1IpIC8gd1NoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB3VmFsdWUgPSB0ZXh0dXJlMkQod2VpZ2h0cywgd1VWKS5yO1xuXG4gICAgICAgICAgICBkb3RQcm9kICs9IHhWYWx1ZSAqIHdWYWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICgke2hhc0JpYXN9KSB7XG4gICAgICAgIGRvdFByb2QgKz0gZ2V0Qmlhc1ZhbHVlKGJpYXNlcywgZDIpO1xuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChkb3RQcm9kLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKG91dHB1dERlcHRoOiBudW1iZXIpOlxuICAgIHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgZ2V0Qmlhc1ZhbHVlKGluIHNhbXBsZXIyRCBiaWFzLCBmbG9hdCBiaWFzQykge1xuICAgICAgY29uc3QgdmVjMiBiaWFzU2hhcGVDUiA9IHZlYzIoJHtvdXRwdXREZXB0aH0sIDEpO1xuICAgICAgdmVjMiBiaWFzQ1IgPSB2ZWMyKG1vZChiaWFzQywgJHtvdXRwdXREZXB0aH0uMCksIDApO1xuICAgICAgdmVjMiBiaWFzVVYgPSAoYmlhc0NSICsgdmVjMigwLjUsIDAuNSkpIC8gYmlhc1NoYXBlQ1I7XG4gICAgICByZXR1cm4gdGV4dHVyZTJEKGJpYXMsIGJpYXNVVikucjtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIGFTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHJlc3VsdERlcHRoOiBudW1iZXIsXG4gICAgZmllbGRTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCB6ZXJvUGFkOiBudW1iZXIsXG4gICAgaGFzQmlhczogYm9vbGVhbik6IHN0cmluZyB7XG4gIGNvbnN0IGFTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoYVNoYXBlUm93Q29sRGVwdGgpO1xuXG4gIGNvbnN0IHdlaWdodFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0gPSBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShcbiAgICAgIGFTaGFwZVJvd0NvbERlcHRoWzJdLCByZXN1bHREZXB0aCwgZmllbGRTaXplKTtcblxuICBjb25zdCBwcm9sb2d1ZSA9IGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTtcbiAgY29uc3QgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQgPVxuICAgICAgZ2V0RnJhZ21lbnRTaGFkZXJHZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZFNvdXJjZSgpO1xuICBjb25zdCBjb252b2x2ZSA9IGdldEZyYWdtZW50U2hhZGVyQ29udm9sdmVTb3VyY2UoXG4gICAgICBhU2hhcGVSb3dDb2xEZXB0aCwgZmllbGRTaXplLCByZXN1bHREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkLCBoYXNCaWFzKTtcbiAgY29uc3QgZ2V0Qmlhc1ZhbHVlID0gZ2V0RnJhZ21lbnRTaGFkZXJHZXRCaWFzVmFsdWVTb3VyY2UocmVzdWx0RGVwdGgpO1xuXG4gIHJldHVybiBbXG4gICAgcHJvbG9ndWUsXG4gICAgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQsXG4gICAgZ2V0Qmlhc1ZhbHVlLFxuICAgIGNvbnZvbHZlLFxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29udm9sdmUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgd2VpZ2h0czogV2ViR0xUZXh0dXJlLCBiaWFzZXM6IFdlYkdMVGV4dHVyZXxudWxsLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ3gnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHdlaWdodHMsICd3ZWlnaHRzJywgMSk7XG4gIGlmIChiaWFzZXMgIT0gbnVsbCkge1xuICAgIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiaWFzZXMsICdiaWFzZXMnLCAyKTtcbiAgfVxuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBkZXN0U2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHNvdXJjZTtcbiAgICB1bmlmb3JtIHZlYzIgc291cmNlU3RhcnRDUjtcbiAgICB1bmlmb3JtIHZlYzIgZGVzdFN0YXJ0Q1I7XG5cbiAgICBjb25zdCB2ZWMyIHNvdXJjZVNoYXBlQ1IgPVxuICAgICAgdmVjMigke3NvdXJjZVNoYXBlUm93Q29sWzFdfSwgJHtzb3VyY2VTaGFwZVJvd0NvbFswXX0pO1xuICAgIGNvbnN0IHZlYzIgc291cmNlU2l6ZUNSID1cbiAgICAgIHZlYzIoJHtzb3VyY2VTaXplUm93Q29sWzFdfSwgJHtzb3VyY2VTaXplUm93Q29sWzBdfSk7XG4gICAgY29uc3QgdmVjMiBkZXN0U2l6ZUNSID1cbiAgICAgIHZlYzIoJHtkZXN0U2l6ZVJvd0NvbFsxXX0sICR7ZGVzdFNpemVSb3dDb2xbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgZGVzdE9mZnNldENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KSAtIGRlc3RTdGFydENSO1xuICAgICAgZmxvYXQgZGVzdE9mZnNldEZsYXQgPSAoZGVzdE9mZnNldENSLnkgKiBkZXN0U2l6ZUNSLngpICsgZGVzdE9mZnNldENSLng7XG4gICAgICB2ZWMyIHNvdXJjZU9mZnNldENSID0gdmVjMihtb2QoZGVzdE9mZnNldEZsYXQsIHNvdXJjZVNpemVDUi54KSxcbiAgICAgICAgZmxvb3IoZGVzdE9mZnNldEZsYXQgLyBzb3VyY2VTaXplQ1IueCkpO1xuICAgICAgdmVjMiBzb3VyY2VDUiA9IHNvdXJjZVN0YXJ0Q1IgKyBzb3VyY2VPZmZzZXRDUjtcbiAgICAgIHZlYzIgc291cmNlVVYgPSAoc291cmNlQ1IgKyB2ZWMyKDAuNSwgMC41KSkgLyBzb3VyY2VTaGFwZUNSO1xuICAgICAgZ2xfRnJhZ0NvbG9yID0gdGV4dHVyZTJEKHNvdXJjZSwgc291cmNlVVYpO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29weShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHNvdXJjZTogV2ViR0xUZXh0dXJlLFxuICAgIHNvdXJjZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBzb3VyY2VTdGFydFJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBXZWJHTFRleHR1cmUsXG4gICAgZGVzdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0U3RhcnRSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShkZXN0LCBkZXN0U2hhcGVSb3dDb2xbMF0sIGRlc3RTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgZGVzdFN0YXJ0Um93Q29sWzBdLCBkZXN0U2l6ZVJvd0NvbFswXSwgZGVzdFN0YXJ0Um93Q29sWzFdLFxuICAgICAgZGVzdFNpemVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc291cmNlLCAnc291cmNlJywgMCk7XG4gIGNvbnN0IHNvdXJjZVN0YXJ0Q1JMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ3NvdXJjZVN0YXJ0Q1InKTtcbiAgZ3BncHUuZ2wudW5pZm9ybTJmKFxuICAgICAgc291cmNlU3RhcnRDUkxvYywgc291cmNlU3RhcnRSb3dDb2xbMV0sIHNvdXJjZVN0YXJ0Um93Q29sWzBdKTtcbiAgY29uc3QgZGVzdFN0YXJ0Q1JMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2Rlc3RTdGFydENSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihkZXN0U3RhcnRDUkxvYywgZGVzdFN0YXJ0Um93Q29sWzFdLCBkZXN0U3RhcnRSb3dDb2xbMF0pO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRFeHBVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNChleHAodmFsdWUpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldEV4cFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleHAoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgZXhwUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIGV4cFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRFeHBEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRFeHBVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBncGdwdV91dGlsIGZyb20gJy4vZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyB0ZXhfdXRpbCBmcm9tICcuL3RleF91dGlsJztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi93ZWJnbF91dGlsJztcblxuaW1wb3J0IHtXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9ufSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgY2xhc3MgR1BHUFVDb250ZXh0IHtcbiAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgdGV4dHVyZUZsb2F0RXh0ZW5zaW9uOiB7fTtcbiAgY29sb3JCdWZmZXJGbG9hdEV4dGVuc2lvbjoge307XG4gIGxvc2VDb250ZXh0RXh0ZW5zaW9uOiBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICB2ZXJ0ZXhCdWZmZXI6IFdlYkdMQnVmZmVyO1xuICBpbmRleEJ1ZmZlcjogV2ViR0xCdWZmZXI7XG4gIGZyYW1lYnVmZmVyOiBXZWJHTEZyYW1lYnVmZmVyO1xuICBvdXRwdXRUZXh0dXJlOiBXZWJHTFRleHR1cmV8bnVsbCA9IG51bGw7XG4gIHByb2dyYW06IFdlYkdMUHJvZ3JhbXxudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBkaXNwb3NlZCA9IGZhbHNlO1xuICBwcml2YXRlIGF1dG9EZWJ1Z1ZhbGlkYXRlID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoZ2w/OiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgICBpZiAoZ2wgIT0gbnVsbCkge1xuICAgICAgdGhpcy5nbCA9IGdsO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmdsID0gZ3BncHVfdXRpbC5jcmVhdGVXZWJHTENvbnRleHQoKTtcbiAgICB9XG5cbiAgICAvLyBXZWJHTCAyLjAgZW5hYmxlcyB0ZXh0dXJlIGZsb2F0cyB3aXRob3V0IGFuIGV4dGVuc2lvbi5cbiAgICBpZiAoIXdlYmdsX3V0aWwuaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICAgIHRoaXMudGV4dHVyZUZsb2F0RXh0ZW5zaW9uID1cbiAgICAgICAgICB3ZWJnbF91dGlsLmdldEV4dGVuc2lvbk9yVGhyb3codGhpcy5nbCwgJ09FU190ZXh0dXJlX2Zsb2F0Jyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY29sb3JCdWZmZXJGbG9hdEV4dGVuc2lvbiA9XG4gICAgICAgICAgd2ViZ2xfdXRpbC5nZXRFeHRlbnNpb25PclRocm93KHRoaXMuZ2wsICdFWFRfY29sb3JfYnVmZmVyX2Zsb2F0Jyk7XG4gICAgfVxuXG4gICAgdGhpcy5sb3NlQ29udGV4dEV4dGVuc2lvbiA9XG4gICAgICAgIHdlYmdsX3V0aWwuZ2V0RXh0ZW5zaW9uT3JUaHJvdyh0aGlzLmdsLCAnV0VCR0xfbG9zZV9jb250ZXh0JykgYXNcbiAgICAgICAgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgICB0aGlzLnZlcnRleEJ1ZmZlciA9IGdwZ3B1X3V0aWwuY3JlYXRlVmVydGV4QnVmZmVyKHRoaXMuZ2wpO1xuICAgIHRoaXMuaW5kZXhCdWZmZXIgPSBncGdwdV91dGlsLmNyZWF0ZUluZGV4QnVmZmVyKHRoaXMuZ2wpO1xuICAgIHRoaXMuZnJhbWVidWZmZXIgPSB3ZWJnbF91dGlsLmNyZWF0ZUZyYW1lYnVmZmVyKHRoaXMuZ2wpO1xuICB9XG5cbiAgcHVibGljIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBpZiAodGhpcy5wcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAnRGlzcG9zaW5nIGEgR1BHUFVDb250ZXh0IHRoYXQgc3RpbGwgaGFzIGEgYm91bmQgV2ViR0xQcm9ncmFtLicgK1xuICAgICAgICAgICcgVGhpcyBpcyBwcm9iYWJseSBhIHJlc291cmNlIGxlYWssIGRlbGV0ZSB0aGUgcHJvZ3JhbSB3aXRoICcgK1xuICAgICAgICAgICdHUEdQVUNvbnRleHQuZGVsZXRlUHJvZ3JhbSBiZWZvcmUgZGlzcG9zaW5nLicpO1xuICAgIH1cbiAgICBpZiAodGhpcy5vdXRwdXRUZXh0dXJlICE9IG51bGwpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAnRGlzcG9zaW5nIGEgR1BHUFVDb250ZXh0IHRoYXQgc3RpbGwgaGFzIGEgYm91bmQgb3V0cHV0IG1hdHJpeCAnICtcbiAgICAgICAgICAndGV4dHVyZS4gIFRoaXMgaXMgcHJvYmFibHkgYSByZXNvdXJjZSBsZWFrLCBkZWxldGUgdGhlIG91dHB1dCAnICtcbiAgICAgICAgICAnbWF0cml4IHRleHR1cmUgd2l0aCBHUEdQVUNvbnRleHQuZGVsZXRlTWF0cml4VGV4dHVyZSBiZWZvcmUgJyArXG4gICAgICAgICAgJ2Rpc3Bvc2luZy4nKTtcbiAgICB9XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5maW5pc2goKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVGcmFtZWJ1ZmZlcih0aGlzLmZyYW1lYnVmZmVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBudWxsKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZUJ1ZmZlcih0aGlzLnZlcnRleEJ1ZmZlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgICBnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVCdWZmZXIodGhpcy5pbmRleEJ1ZmZlcikpO1xuICAgIHRoaXMubG9zZUNvbnRleHRFeHRlbnNpb24ubG9zZUNvbnRleHQoKTtcbiAgICB0aGlzLmRpc3Bvc2VkID0gdHJ1ZTtcbiAgfVxuXG4gIHB1YmxpYyBlbmFibGVBdXRvbWF0aWNEZWJ1Z1ZhbGlkYXRpb24oZW5hYmxlZDogYm9vbGVhbikge1xuICAgIHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUgPSBlbmFibGVkO1xuICAgIHdlYmdsX3V0aWwuZW5hYmxlRGVidWdXZWJHTEVycm9yQ2hlY2tpbmcoZW5hYmxlZCk7XG4gIH1cblxuICBwdWJsaWMgY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC5jcmVhdGVNYXRyaXhUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpO1xuICB9XG5cbiAgcHVibGljIHVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICAgIHBpeGVsczogSW1hZ2VEYXRhfEhUTUxJbWFnZUVsZW1lbnR8SFRNTENhbnZhc0VsZW1lbnR8SFRNTFZpZGVvRWxlbWVudCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgZ3BncHVfdXRpbC51cGxvYWRQaXhlbERhdGFUb1RleHR1cmUodGhpcy5nbCwgdGV4dHVyZSwgcGl4ZWxzKTtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTpcbiAgICAgIFdlYkdMVGV4dHVyZSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpO1xuICB9XG5cbiAgcHVibGljIGRlbGV0ZU1hdHJpeFRleHR1cmUodGV4dHVyZTogV2ViR0xUZXh0dXJlKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBpZiAodGhpcy5vdXRwdXRUZXh0dXJlID09PSB0ZXh0dXJlKSB7XG4gICAgICB3ZWJnbF91dGlsLnVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcih0aGlzLmdsLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICAgIHRoaXMub3V0cHV0VGV4dHVyZSA9IG51bGw7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuZGVsZXRlVGV4dHVyZSh0ZXh0dXJlKSk7XG4gIH1cblxuICBwdWJsaWMgdXBsb2FkTWF0cml4VG9UZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICAgIG1hdHJpeDogRmxvYXQzMkFycmF5KSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBudW1DaGFubmVscyA9IDE7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwudXBsb2FkTWF0cml4VG9UZXh0dXJlKFxuICAgICAgICB0aGlzLmdsLCB0ZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBtYXRyaXgsIG51bUNoYW5uZWxzKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHJldHVybiBncGdwdV91dGlsLnVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShcbiAgICAgICAgdGhpcy5nbCwgdGV4dHVyZSwgcm93cywgY29sdW1ucywgbWF0cml4KTtcbiAgfVxuXG4gIHB1YmxpYyBkb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICAgIHRleHR1cmUsXG4gICAgICAgICgpID0+XG4gICAgICAgICAgICBncGdwdV91dGlsLmRvd25sb2FkTWF0cml4RnJvbU91dHB1dFRleHR1cmUodGhpcy5nbCwgcm93cywgY29sdW1ucykpO1xuICB9XG5cbiAgcHVibGljIGRvd25sb2FkTWF0cml4RnJvbVBhY2tlZFRleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5kb3dubG9hZE1hdHJpeERyaXZlcihcbiAgICAgICAgdGV4dHVyZSxcbiAgICAgICAgKCkgPT4gZ3BncHVfdXRpbC5kb3dubG9hZE1hdHJpeEZyb21QYWNrZWRPdXRwdXRUZXh0dXJlKFxuICAgICAgICAgICAgdGhpcy5nbCwgcm93cywgY29sdW1ucykpO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTb3VyY2U6IHN0cmluZyk6IFdlYkdMUHJvZ3JhbSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgY29uc3QgZnJhZ21lbnRTaGFkZXI6IFdlYkdMU2hhZGVyID1cbiAgICAgICAgd2ViZ2xfdXRpbC5jcmVhdGVGcmFnbWVudFNoYWRlcihnbCwgZnJhZ21lbnRTaGFkZXJTb3VyY2UpO1xuICAgIGNvbnN0IHZlcnRleFNoYWRlcjogV2ViR0xTaGFkZXIgPSBncGdwdV91dGlsLmNyZWF0ZVZlcnRleFNoYWRlcihnbCk7XG4gICAgY29uc3QgcHJvZ3JhbTogV2ViR0xQcm9ncmFtID0gd2ViZ2xfdXRpbC5jcmVhdGVQcm9ncmFtKGdsKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hdHRhY2hTaGFkZXIocHJvZ3JhbSwgZnJhZ21lbnRTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmxpbmtQcm9ncmFtKGdsLCBwcm9ncmFtKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZVByb2dyYW0oZ2wsIHByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGV0YWNoU2hhZGVyKHByb2dyYW0sIHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVTaGFkZXIodmVydGV4U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRldGFjaFNoYWRlcihwcm9ncmFtLCBmcmFnbWVudFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVTaGFkZXIoZnJhZ21lbnRTaGFkZXIpKTtcbiAgICByZXR1cm4gcHJvZ3JhbTtcbiAgfVxuXG4gIHB1YmxpYyBkZWxldGVQcm9ncmFtKHByb2dyYW06IFdlYkdMUHJvZ3JhbSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHByb2dyYW0gPT09IHRoaXMucHJvZ3JhbSkge1xuICAgICAgdGhpcy5wcm9ncmFtID0gbnVsbDtcbiAgICB9XG4gICAgaWYgKHByb2dyYW0gIT0gbnVsbCkge1xuICAgICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5kZWxldGVQcm9ncmFtKHByb2dyYW0pKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgc2V0UHJvZ3JhbShwcm9ncmFtOiBXZWJHTFByb2dyYW18bnVsbCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy5wcm9ncmFtID0gcHJvZ3JhbTtcbiAgICBpZiAoKHRoaXMucHJvZ3JhbSAhPSBudWxsKSAmJiB0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlUHJvZ3JhbSh0aGlzLmdsLCB0aGlzLnByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLnVzZVByb2dyYW0ocHJvZ3JhbSkpO1xuICB9XG5cbiAgcHVibGljIGdldFVuaWZvcm1Mb2NhdGlvbih1bmlmb3JtTmFtZTogc3RyaW5nKTogV2ViR0xVbmlmb3JtTG9jYXRpb24ge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy50aHJvd0lmTm9Qcm9ncmFtKCk7XG4gICAgcmV0dXJuIHdlYmdsX3V0aWwuZ2V0UHJvZ3JhbVVuaWZvcm1Mb2NhdGlvbk9yVGhyb3coXG4gICAgICAgIHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSEsIHVuaWZvcm1OYW1lKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRJbnB1dE1hdHJpeFRleHR1cmUoXG4gICAgICBpbnB1dE1hdHJpeFRleHR1cmU6IFdlYkdMVGV4dHVyZSwgdW5pZm9ybU5hbWU6IHN0cmluZyxcbiAgICAgIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIHdlYmdsX3V0aWwuYmluZFRleHR1cmVUb1Byb2dyYW1Vbmlmb3JtU2FtcGxlcihcbiAgICAgICAgdGhpcy5nbCwgdGhpcy5wcm9ncmFtISwgaW5wdXRNYXRyaXhUZXh0dXJlLCB1bmlmb3JtTmFtZSwgdGV4dHVyZVVuaXQpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICBvdXRwdXRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKG91dHB1dE1hdHJpeFRleHR1cmUsIGNvbHVtbnMsIHJvd3MpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dFBhY2tlZE1hdHJpeFRleHR1cmUoXG4gICAgICBvdXRwdXRQYWNrZWRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgICB0aGlzLnNldE91dHB1dE1hdHJpeFRleHR1cmVEcml2ZXIob3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZSwgd2lkdGgsIGhlaWdodCk7XG4gIH1cblxuICBwdWJsaWMgc2V0T3V0cHV0TWF0cml4V3JpdGVSZWdpb24oXG4gICAgICBzdGFydFJvdzogbnVtYmVyLCBudW1Sb3dzOiBudW1iZXIsIHN0YXJ0Q29sdW1uOiBudW1iZXIsXG4gICAgICBudW1Db2x1bW5zOiBudW1iZXIpIHtcbiAgICB0aGlzLnNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uRHJpdmVyKFxuICAgICAgICBzdGFydENvbHVtbiwgc3RhcnRSb3csIG51bUNvbHVtbnMsIG51bVJvd3MpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dFBhY2tlZE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgc3RhcnRSb3c6IG51bWJlciwgbnVtUm93czogbnVtYmVyLCBzdGFydENvbHVtbjogbnVtYmVyLFxuICAgICAgbnVtQ29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdzZXRPdXRwdXRQYWNrZWRNYXRyaXhXcml0ZVJlZ2lvbiBub3QgaW1wbGVtZW50ZWQuJyk7XG4gIH1cblxuICBwdWJsaWMgZGVidWdWYWxpZGF0ZSgpIHtcbiAgICBpZiAodGhpcy5wcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVQcm9ncmFtKHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwudmFsaWRhdGVGcmFtZWJ1ZmZlcih0aGlzLmdsKTtcbiAgfVxuXG4gIHB1YmxpYyBleGVjdXRlUHJvZ3JhbSgpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICBncGdwdV91dGlsLmJpbmRWZXJ0ZXhQcm9ncmFtQXR0cmlidXRlU3RyZWFtcyhcbiAgICAgICAgZ2wsIHRoaXMucHJvZ3JhbSEsIHRoaXMudmVydGV4QnVmZmVyKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgdGhpcy5kZWJ1Z1ZhbGlkYXRlKCk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgICBnbCwgKCkgPT4gZ2wuZHJhd0VsZW1lbnRzKGdsLlRSSUFOR0xFUywgNiwgZ2wuVU5TSUdORURfU0hPUlQsIDApKTtcbiAgfVxuXG4gIHB1YmxpYyBibG9ja1VudGlsQWxsUHJvZ3JhbXNDb21wbGV0ZWQoKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLmZpbmlzaCgpKTtcbiAgfVxuXG4gIHByaXZhdGUgZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgICBkb3dubG9hZEFuZERlY29kZTogKCkgPT4gRmxvYXQzMkFycmF5KTogRmxvYXQzMkFycmF5IHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHdlYmdsX3V0aWwuYmluZENvbG9yVGV4dHVyZVRvRnJhbWVidWZmZXIoXG4gICAgICAgIHRoaXMuZ2wsIHRleHR1cmUsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGRvd25sb2FkQW5kRGVjb2RlKCk7XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICAgIHRoaXMuZ2wsIHRoaXMub3V0cHV0VGV4dHVyZSwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIodGhpcy5nbCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHdlYmdsX3V0aWwudW5iaW5kQ29sb3JUZXh0dXJlRnJvbUZyYW1lYnVmZmVyKHRoaXMuZ2wsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKFxuICAgICAgb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkOiBXZWJHTFRleHR1cmUsIHdpZHRoOiBudW1iZXIsXG4gICAgICBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIHdlYmdsX3V0aWwuYmluZENvbG9yVGV4dHVyZVRvRnJhbWVidWZmZXIoXG4gICAgICAgIGdsLCBvdXRwdXRNYXRyaXhUZXh0dXJlTWF5YmVQYWNrZWQsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIGlmICh0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIoZ2wpO1xuICAgIH1cbiAgICB0aGlzLm91dHB1dFRleHR1cmUgPSBvdXRwdXRNYXRyaXhUZXh0dXJlTWF5YmVQYWNrZWQ7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnZpZXdwb3J0KDAsIDAsIHdpZHRoLCBoZWlnaHQpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuc2Npc3NvcigwLCAwLCB3aWR0aCwgaGVpZ2h0KSk7XG4gIH1cblxuICBwcml2YXRlIHNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uRHJpdmVyKFxuICAgICAgeDogbnVtYmVyLCB5OiBudW1iZXIsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgdGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5zY2lzc29yKHgsIHksIHdpZHRoLCBoZWlnaHQpKTtcbiAgfVxuXG4gIHByaXZhdGUgdGhyb3dJZkRpc3Bvc2VkKCkge1xuICAgIGlmICh0aGlzLmRpc3Bvc2VkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F0dGVtcHRlZCB0byB1c2UgZGlzcG9zZWQgR1BHUFVDb250ZXh0LicpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgdGhyb3dJZk5vUHJvZ3JhbSgpIHtcbiAgICBpZiAodGhpcy5wcm9ncmFtID09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTm8gR1BVIHByb2dyYW0gaXMgY3VycmVudGx5IHNldC4nKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdGV4X3V0aWwgZnJvbSAnLi90ZXhfdXRpbCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRXZWJHTENvbnRleHRBdHRyaWJ1dGVzKCk6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMge1xuICByZXR1cm4ge1xuICAgIGFscGhhOiBmYWxzZSxcbiAgICBhbnRpYWxpYXM6IGZhbHNlLFxuICAgIHByZW11bHRpcGxpZWRBbHBoYTogZmFsc2UsXG4gICAgcHJlc2VydmVEcmF3aW5nQnVmZmVyOiBmYWxzZSxcbiAgICBkZXB0aDogZmFsc2UsXG4gICAgc3RlbmNpbDogZmFsc2UsXG4gICAgZmFpbElmTWFqb3JQZXJmb3JtYW5jZUNhdmVhdDogdHJ1ZVxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xDb250ZXh0KGNhbnZhcz86IEhUTUxDYW52YXNFbGVtZW50KSB7XG4gIGNvbnN0IGF0dHJpYnV0ZXMgPSBnZXRXZWJHTENvbnRleHRBdHRyaWJ1dGVzKCk7XG4gIGxldCBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICBpZiAoY2FudmFzICE9IG51bGwpIHtcbiAgICBnbCA9IHdlYmdsX3V0aWwuY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0RnJvbUNhbnZhcyhjYW52YXMsIGF0dHJpYnV0ZXMpO1xuICB9IGVsc2Uge1xuICAgIGdsID0gd2ViZ2xfdXRpbC5jcmVhdGVXZWJHTFJlbmRlcmluZ0NvbnRleHQoYXR0cmlidXRlcyk7XG4gIH1cbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuREVQVEhfVEVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5TVEVOQ0lMX1RFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuQkxFTkQpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuRElUSEVSKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLlBPTFlHT05fT0ZGU0VUX0ZJTEwpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuU0FNUExFX0NPVkVSQUdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGUoZ2wuU0NJU1NPUl9URVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGUoZ2wuQ1VMTF9GQUNFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jdWxsRmFjZShnbC5CQUNLKSk7XG4gIHJldHVybiBnbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVZlcnRleFNoYWRlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xTaGFkZXIge1xuICBjb25zdCB2ZXJ0ZXhTaGFkZXJTb3VyY2UgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIGF0dHJpYnV0ZSB2ZWMzIGNsaXBTcGFjZVBvcztcbiAgICBhdHRyaWJ1dGUgdmVjMiB1djtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBnbF9Qb3NpdGlvbiA9IHZlYzQoY2xpcFNwYWNlUG9zLCAxKTtcbiAgICAgIHJlc3VsdFVWID0gdXY7XG4gICAgfWA7XG4gIHJldHVybiB3ZWJnbF91dGlsLmNyZWF0ZVZlcnRleFNoYWRlcihnbCwgdmVydGV4U2hhZGVyU291cmNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVZlcnRleEJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xCdWZmZXIge1xuICAvLyBbeCB5IHogdSB2XSAqIFt1cHBlci1sZWZ0LCBsb3dlci1sZWZ0LCB1cHBlci1yaWdodCwgbG93ZXItcmlnaHRdXG4gIGNvbnN0IHZlcnRleEFycmF5ID0gbmV3IEZsb2F0MzJBcnJheShcbiAgICAgIFstMSwgMSwgMCwgMCwgMSwgLTEsIC0xLCAwLCAwLCAwLCAxLCAxLCAwLCAxLCAxLCAxLCAtMSwgMCwgMSwgMF0pO1xuICByZXR1cm4gd2ViZ2xfdXRpbC5jcmVhdGVTdGF0aWNWZXJ0ZXhCdWZmZXIoZ2wsIHZlcnRleEFycmF5KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUluZGV4QnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEJ1ZmZlciB7XG4gIC8vIE9wZW5HTCAoYW5kIFdlYkdMKSBoYXZlIFwiQ0NXID09IGZyb250XCIgd2luZGluZ1xuICBjb25zdCB0cmlhbmdsZVZlcnRleEluZGljZXMgPSBuZXcgVWludDE2QXJyYXkoWzAsIDEsIDIsIDIsIDEsIDNdKTtcbiAgcmV0dXJuIHdlYmdsX3V0aWwuY3JlYXRlU3RhdGljSW5kZXhCdWZmZXIoZ2wsIHRyaWFuZ2xlVmVydGV4SW5kaWNlcyk7XG59XG5cbmZ1bmN0aW9uIGdldFRleHR1cmVJbnRlcm5hbEZvcm1hdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBudW1DaGFubmVsczogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKHdlYmdsX3V0aWwuaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICBpZiAobnVtQ2hhbm5lbHMgPT09IDQpIHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHJldHVybiAoZ2wgYXMgYW55KS5SR0JBMzJGO1xuICAgIH1cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgcmV0dXJuIChnbCBhcyBhbnkpLlIzMkY7XG4gIH1cbiAgcmV0dXJuIGdsLlJHQkE7XG59XG5cbmZ1bmN0aW9uIGdldFRleHR1cmVGb3JtYXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbnVtQ2hhbm5lbHM6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpICYmIG51bUNoYW5uZWxzID09PSAxKSB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIHJldHVybiAoZ2wgYXMgYW55KS5SRUQ7XG4gIH1cbiAgcmV0dXJuIGdsLlJHQkE7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIsXG4gICAgbnVtQ2hhbm5lbHM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIHdlYmdsX3V0aWwudmFsaWRhdGVUZXh0dXJlU2l6ZShnbCwgd2lkdGgsIGhlaWdodCk7XG4gIGNvbnN0IHRleHR1cmUgPSB3ZWJnbF91dGlsLmNyZWF0ZVRleHR1cmUoZ2wpO1xuXG4gIGNvbnN0IHRleDJkID0gZ2wuVEVYVFVSRV8yRDtcbiAgY29uc3QgaW50ZXJuYWxGb3JtYXQgPSBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgY29uc3QgZm9ybWF0ID0gZ2V0VGV4dHVyZUZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUodGV4MmQsIHRleHR1cmUpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9XUkFQX1MsIGdsLkNMQU1QX1RPX0VER0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9XUkFQX1QsIGdsLkNMQU1QX1RPX0VER0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBnbC5ORUFSRVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgZ2wuTkVBUkVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudGV4SW1hZ2UyRChcbiAgICAgICAgICB0ZXgyZCwgMCwgaW50ZXJuYWxGb3JtYXQsIHdpZHRoLCBoZWlnaHQsIDAsIGZvcm1hdCwgZ2wuRkxPQVQsIG51bGwpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbiAgcmV0dXJuIHRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVNYXRyaXhUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgIHRleF91dGlsLmdldFVucGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gMTtcbiAgcmV0dXJuIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoZ2wsIHdpZHRoLCBoZWlnaHQsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUNvbG9yTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRDb2xvck1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgIHRleF91dGlsLmdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVmVydGV4UHJvZ3JhbUF0dHJpYnV0ZVN0cmVhbXMoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLFxuICAgIHZlcnRleEJ1ZmZlcjogV2ViR0xCdWZmZXIpIHtcbiAgY29uc3QgcG9zT2Zmc2V0ID0gMDsgICAgICAgICAgICAgICAvLyB4IGlzIHRoZSBmaXJzdCBidWZmZXIgZWxlbWVudFxuICBjb25zdCB1dk9mZnNldCA9IDMgKiA0OyAgICAgICAgICAgIC8vIHV2IGNvbWVzIGFmdGVyIFt4IHkgel1cbiAgY29uc3Qgc3RyaWRlID0gKDMgKiA0KSArICgyICogNCk7ICAvLyB4eXogKyB1diwgZWFjaCBlbnRyeSBpcyA0LWJ5dGUgZmxvYXQuXG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCB2ZXJ0ZXhCdWZmZXIpKTtcbiAgd2ViZ2xfdXRpbC5iaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgICAgZ2wsIHByb2dyYW0sICdjbGlwU3BhY2VQb3MnLCB2ZXJ0ZXhCdWZmZXIsIDMsIHN0cmlkZSwgcG9zT2Zmc2V0KTtcbiAgdHJ5IHtcbiAgICB3ZWJnbF91dGlsLmJpbmRWZXJ0ZXhCdWZmZXJUb1Byb2dyYW1BdHRyaWJ1dGUoXG4gICAgICAgIGdsLCBwcm9ncmFtLCAndXYnLCB2ZXJ0ZXhCdWZmZXIsIDIsIHN0cmlkZSwgdXZPZmZzZXQpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gUHJvZ3JhbXMgd2l0aCAxeDEgb3V0cHV0IHRleHR1cmVzIGRvbid0IHVzZSB0aGUgdXYgYXR0cmlidXRlLlxuICAgIC8vIFRoaXMgY2FuIGNhdXNlIHRoZSBzaGFkZXIgbGlua2VyIHRvIGRlYWQtc3RyaXAgaXQsIHNvIHdlIHNob3VsZG4ndFxuICAgIC8vIGNvbXBsYWluIG9yIGZhaWwgaWYgaXQncyBub3QgcHJlc2VudC5cbiAgICBpZiAoIWUuaGFzT3duUHJvcGVydHkoJ25hbWVkVmVydGV4QXR0cmlidXRlTm90Rm91bmQnKSkge1xuICAgICAgdGhyb3cgZTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgcGl4ZWxzOiBJbWFnZURhdGF8SFRNTEltYWdlRWxlbWVudHxIVE1MQ2FudmFzRWxlbWVudHxIVE1MVmlkZW9FbGVtZW50KSB7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgY29uc3QgaW50ZXJuYWxGb3JtYXQgPSBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnRleEltYWdlMkQoXG4gICAgICAgICAgZ2wuVEVYVFVSRV8yRCwgMCwgaW50ZXJuYWxGb3JtYXQsIGdsLlJHQkEsIGdsLkZMT0FULCBwaXhlbHMpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZnVuY3Rpb24gdXBsb2FkRGF0YVRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHdpZHRoOiBudW1iZXIsXG4gICAgaGVpZ2h0OiBudW1iZXIsIGRhdGE6IEZsb2F0MzJBcnJheSwgbnVtQ2hhbm5lbHM6IG51bWJlcikge1xuICBjb25zdCB0ZXh0dXJlRm9ybWF0ID0gZ2V0VGV4dHVyZUZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuXG4gIHdlYmdsX3V0aWwudmFsaWRhdGVUZXh0dXJlU2l6ZShnbCwgd2lkdGgsIGhlaWdodCk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC50ZXhTdWJJbWFnZTJEKFxuICAgICAgICAgIGdsLlRFWFRVUkVfMkQsIDAsIDAsIDAsIHdpZHRoLCBoZWlnaHQsIHRleHR1cmVGb3JtYXQsIGdsLkZMT0FULFxuICAgICAgICAgIGRhdGEpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlcixcbiAgICBjb2x1bW5zOiBudW1iZXIsIG1hdHJpeDogRmxvYXQzMkFycmF5LCBudW1DaGFubmVsczogbnVtYmVyKSB7XG4gIGNvbnN0IFt3LCBoXSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuXG4gIGNvbnN0IGNoYW5uZWxzUGVyVGV4dHVyZSA9XG4gICAgICBudW1DaGFubmVscyA9PT0gMSA/IHdlYmdsX3V0aWwuZ2V0Q2hhbm5lbHNQZXJUZXh0dXJlKCkgOiBudW1DaGFubmVscztcbiAgY29uc3QgdW5wYWNrZWRBcnJheSA9XG4gICAgICBuZXcgRmxvYXQzMkFycmF5KHRleF91dGlsLmdldFVucGFja2VkQXJyYXlTaXplRnJvbU1hdHJpeFNpemUoXG4gICAgICAgICAgbWF0cml4Lmxlbmd0aCwgY2hhbm5lbHNQZXJUZXh0dXJlKSk7XG4gIHRleF91dGlsLmVuY29kZU1hdHJpeFRvVW5wYWNrZWRBcnJheShcbiAgICAgIG1hdHJpeCwgdW5wYWNrZWRBcnJheSwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcblxuICB1cGxvYWREYXRhVG9UZXh0dXJlKGdsLCB0ZXh0dXJlLCB3LCBoLCB1bnBhY2tlZEFycmF5LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCBtYXRyaXg6IEZsb2F0MzJBcnJheSkge1xuICBjb25zdCBbdywgaF0gPSB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcGFja2VkUkdCQSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRSR0JBQXJyYXlTaXplRnJvbU1hdHJpeFNoYXBlKHJvd3MsIGNvbHVtbnMpKTtcbiAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9QYWNrZWRSR0JBKG1hdHJpeCwgcm93cywgY29sdW1ucywgcGFja2VkUkdCQSk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgdXBsb2FkRGF0YVRvVGV4dHVyZShnbCwgdGV4dHVyZSwgdywgaCwgcGFja2VkUkdCQSwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZG93bmxvYWRNYXRyaXhGcm9tT3V0cHV0VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IFt3LCBoXSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuXG4gIGNvbnN0IGNoYW5uZWxzUGVyVGV4dHVyZSA9IDQ7XG4gIGNvbnN0IHVucGFja2VkQXJyYXkgPVxuICAgICAgbmV3IEZsb2F0MzJBcnJheSh0ZXhfdXRpbC5nZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgICAgICAgIHJvd3MgKiBjb2x1bW5zLCBjaGFubmVsc1BlclRleHR1cmUpKTtcbiAgY29uc3QgdGV4dHVyZUZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG5cbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wucmVhZFBpeGVscygwLCAwLCB3LCBoLCBnbC5SR0JBLCBnbC5GTE9BVCwgdW5wYWNrZWRBcnJheSkpO1xuXG4gIGNvbnN0IG1hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkocm93cyAqIGNvbHVtbnMpO1xuICB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICAgIHVucGFja2VkQXJyYXksIG1hdHJpeCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcbiAgcmV0dXJuIG1hdHJpeDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRvd25sb2FkTWF0cml4RnJvbVBhY2tlZE91dHB1dFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBbdywgaF0gPSB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcGFja2VkUkdCQSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRSR0JBQXJyYXlTaXplRnJvbU1hdHJpeFNoYXBlKHJvd3MsIGNvbHVtbnMpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wucmVhZFBpeGVscygwLCAwLCB3LCBoLCBnbC5SR0JBLCBnbC5GTE9BVCwgcGFja2VkUkdCQSkpO1xuICBjb25zdCBtYXRyaXggPSBuZXcgRmxvYXQzMkFycmF5KHJvd3MgKiBjb2x1bW5zKTtcbiAgcmV0dXJuIHRleF91dGlsLmRlY29kZU1hdHJpeEZyb21QYWNrZWRSR0JBKHBhY2tlZFJHQkEsIHJvd3MsIGNvbHVtbnMsIG1hdHJpeCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldExvZ1VuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuICdnbF9GcmFnQ29sb3IgPSB2ZWM0KGxvZyh2YWx1ZSksIDAsIDAsIDApOyc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0TG9nVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvZyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBsb2dQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgbG9nUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZExvZ0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldExvZ1VuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGFNYXggPSB0ZXh0dXJlMkQobWF0cml4QSwgaGFsZkNSIC8gYURpbUNSKS5yO1xuICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgYURpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGFEaW1DUi54OyBjICs9IDEuMCkge1xuICAgICAgICAgIHZlYzIgdXYgPSAodmVjMihjLCByKSArIGhhbGZDUikgLyBhRGltQ1I7XG4gICAgICAgICAgZmxvYXQgYUN1ciA9IHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICAgICAgICBhTWF4ID0gbWF4KGFNYXgsIGFDdXIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGZsb2F0IGV4cFN1bSA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGFDdXIgPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgICAgZXhwU3VtICs9IGV4cChhQ3VyIC0gYU1heCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChhTWF4ICsgbG9nKGV4cFN1bSksIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbG9nU3VtRXhwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGxvZ1N1bUV4cFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obG9nU3VtRXhwUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTG9nU3VtRXhwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtID0gZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zKSk7XG4gIGNvbnN0IGFUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4dHVyZSwgcm93cywgY29sdW1ucywgYSk7XG4gIGxvZ1N1bUV4cChncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleHR1cmUsIDEsIDEpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0WzBdO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQmFja3Byb3AoXG4gICAgZHlTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgb3JpZ1BhZDogbnVtYmVyKSB7XG4gIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gZHlTaGFwZVJDRFsyXTtcbiAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgY29uc3QgW2R5Um93cywgZHlDb2xzLCBkZXB0aF0gPSBkeVNoYXBlUkNEO1xuXG4gIGNvbnN0IGR5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZHlTaGFwZVJDRCk7XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF4UG9zO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHtkeVRleFNoYXBlUkNbMV19LCAke2R5VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBkeFRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKGR4VGV4UiwgZHhUZXhDKSB0byAzRCAoZHhSLCBkeEMsIGQpLlxuICAgICAgZmxvYXQgZHhSID0gZHhUZXhDUi55O1xuICAgICAgZmxvYXQgZHhDID0gZmxvb3IoZHhUZXhDUi54IC8gJHtvcmlnSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkID0gbW9kKGR4VGV4Q1IueCwgJHtvcmlnSW5wdXREZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgZHlSQ0Nvcm5lciA9IHZlYzIoZHhSLCBkeEMpIC0gdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgZHlSQ29ybmVyID0gZHlSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgZHlDQ29ybmVyID0gZHlSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBDb252b2x2ZSBkeSg/LCA/LCBkKSB3aXRoIHBvcyBtYXNrKDosIDosIGQpIHRvIGdldCBkeCh5UiwgZHhDLCBkKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG5cbiAgICAgICAgZmxvYXQgZHlSID0gKGR5UkNvcm5lciArIHdSKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgLy8gVE9ETyhuc3Rob3JhdCk6IFNwbGljZSB0aGlzIHdpdGggYW5vdGhlciB2ZXJzaW9uIHdoZXJlIHlvdSBjYWxsXG4gICAgICAgIC8vIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKCkuIEhlcmUgYW5kIGJlbG93LlxuICAgICAgICBpZiAoZHlSIDwgMC4wIHx8IGR5UiA+PSAke2R5Um93c30uMCB8fCBmcmFjdChkeVIpID4gMC4wKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBmbG9hdCBkeVRleFIgPSBkeVI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcblxuICAgICAgICAgIGZsb2F0IGR5QyA9IChkeUNDb3JuZXIgKyB3QykgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgICAgaWYgKGR5QyA8IDAuMCB8fCBkeUMgPj0gJHtkeUNvbHN9LjAgfHwgZnJhY3QoZHlDKSA+IDAuMCkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZmxvYXQgZHlUZXhDID0gZHlDICogJHtkZXB0aH0uMCArIGQ7XG5cbiAgICAgICAgICAvLyBSZWFkIGR5KGR5UiwgZHlDLCBkKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMihkeVRleEMsIGR5VGV4UikgKyBoYWxmQ1IpIC8gZHlTaGFwZUNSO1xuICAgICAgICAgIGZsb2F0IGR5VmFsdWUgPSB0ZXh0dXJlMkQoZHksIGR5VVYpLnI7XG5cbiAgICAgICAgICAvLyBSZWFkIG1heFBvcyhkeVIsIGR5QywgZCkuXG4gICAgICAgICAgZmxvYXQgbWF4UG9zVmFsdWUgPVxuICAgICAgICAgICAgICAke2ZTaXplICogZlNpemUgLSAxfS4wIC0gdGV4dHVyZTJEKG1heFBvcywgZHlVVikucjtcblxuICAgICAgICAgIC8vIEdldCB0aGUgY3VycmVudCB2YWx1ZSwgY2hlY2sgaXQgYWdhaW5zdCB0aGUgdmFsdWUgZnJvbSB0aGVcbiAgICAgICAgICAvLyBwb3NpdGlvbiBtYXRyaXguXG4gICAgICAgICAgZmxvYXQgY3VyUG9zVmFsdWUgPSB3UiAqICR7ZlNpemV9LjAgKyB3QztcbiAgICAgICAgICBmbG9hdCBtYXNrID0gZmxvYXQobWF4UG9zVmFsdWUgPT0gY3VyUG9zVmFsdWUgPyAxLjAgOiAwLjApO1xuXG4gICAgICAgICAgZG90UHJvZCArPSBkeVZhbHVlICogbWFzaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChkb3RQcm9kLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1heFBvb2xCYWNrcHJvcChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGR5VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgbWF4UG9zaXRpb25zVGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShtYXhQb3NpdGlvbnNUZXgsICdtYXhQb3MnLCAxKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3Bvb2xfZ3B1JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFBvc2l0aW9uc1NvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCB0cnVlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCBmYWxzZSk7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIsIGNvbXB1dGVNYXhQb3NpdGlvbnM6IGJvb2xlYW4pIHtcbiAgcmV0dXJuIHBvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnbWF4JywgY29tcHV0ZU1heFBvc2l0aW9ucyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXhQb29sQ29tbW9uKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgcG9vbF9ncHUgZnJvbSAnLi9wb29sX2dwdSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1pblBvb2xTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBwb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlclBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21pbicsIGZhbHNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1pblBvb2woXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIHBvb2xfZ3B1LnBvb2xDb21tb24oZ3BncHUsIHByb2dyYW0sIHgsIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2wpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGNvbXBPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgb3V0cHV0Q29sdW1uUm93O1xuXG4gICAgY29uc3QgdmVjMiBhRGltQ1IgPSB2ZWMyKCR7Y29sdW1uc30uMCwgJHtyb3dzfS4wKTtcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgJHtJU19OQU5fU0hBREVSX0ZVTkN9XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXhBLCBoYWxmQ1IgLyBhRGltQ1IpLnI7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiBjciA9IHZlYzIoYywgcik7XG4gICAgICAgICAgdmVjMiB1diA9IChjciArIGhhbGZDUikgLyBhRGltQ1I7XG4gICAgICAgICAgZmxvYXQgY2FuZGlkYXRlID0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTihjYW5kaWRhdGUpKSB7XG4gICAgICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGNhbmRpZGF0ZSwgMCwgMCwgMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIHZhbHVlID0gJHtjb21wT3B9KHZhbHVlLCBjYW5kaWRhdGUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE1pbkZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICdtaW4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE1heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICdtYXgnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1pbk1heChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBtaW5NYXhQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1pbk1heFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vbWF0aCc7XG5pbXBvcnQge0FycmF5MkR9IGZyb20gJy4uL25kYXJyYXknO1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHNoYWRlcl9jb21waWxlciBmcm9tICcuL3NoYWRlcl9jb21waWxlcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlcihcbiAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBvdXQ6IEFycmF5MkQsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sXG4gICAgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF0pO1xuICBjb25zdCBhU25pcHBldCA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/ICdhUm93LCBpJyA6ICdpLCBhUm93JztcbiAgY29uc3QgYlNuaXBwZXQgPVxuICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyAnaSwgYkNvbCcgOiAnYkNvbCwgaSc7XG5cbiAgY29uc3QgaW5wdXRzID0gW3tuYW1lOiAnbWF0cml4QScsIGFycmF5OiBhfSwge25hbWU6ICdtYXRyaXhCJywgYXJyYXk6IGJ9XTtcbiAgY29uc3QgdXNlckNvZGUgPSBgXG4gICAgY29uc3QgZmxvYXQgc2hhcmVkRGltID0gJHtzaGFyZWREaW19LjA7XG5cbiAgICBmbG9hdCBkb3RBUm93QkNvbChmbG9hdCBhUm93LCBmbG9hdCBiQ29sKSB7XG4gICAgICBmbG9hdCByZXN1bHQgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IGkgPSAwLjA7IGkgPCBzaGFyZWREaW07IGkgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IGEgPSBnZXRNYXRyaXhBKCR7YVNuaXBwZXR9KTtcbiAgICAgICAgZmxvYXQgYiA9IGdldE1hdHJpeEIoJHtiU25pcHBldH0pO1xuICAgICAgICByZXN1bHQgKz0gKGEgKiBiKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiByZXNSQyA9IGdldE91dHB1dENvb3JkcygpO1xuICAgICAgc2V0T3V0cHV0KGRvdEFSb3dCQ29sKHJlc1JDLngsIHJlc1JDLnkpKTtcbiAgICB9XG4gIGA7XG4gIHJldHVybiBzaGFkZXJfY29tcGlsZXIubWFrZVNoYWRlcihpbnB1dHMsIG91dCwgdXNlckNvZGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbXVsdGlwbHlNYXRyaXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbXVsdGlwbHlQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBiOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgb3V0VGV4U2hhcGVbMF0sIG91dFRleFNoYXBlWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtdWx0aXBseVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldE5lZ1VuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuICdnbF9GcmFnQ29sb3IgPSB2ZWM0KC12YWx1ZSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXROZWdVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbmVnKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBwcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTmVnRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0TmVnVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCB7SVNfTkFOX1NIQURFUl9GVU5DfSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlciwgcG9vbFR5cGU6ICdtYXgnfCdtaW4nfCdhdmcnLCBjb21wdXRlUG9zaXRpb25zOiBib29sZWFuKSB7XG4gIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycgJiYgY29tcHV0ZVBvc2l0aW9ucykge1xuICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGNvbXB1dGUgcG9zaXRpb25zIGZvciBhdmVyYWdlIHBvb2wuJyk7XG4gIH1cblxuICBjb25zdCBkZXB0aCA9IHhTaGFwZVJDRFsyXTtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcblxuICBsZXQgcmV0dXJuVmFsdWUgPSAnbWluTWF4VmFsdWUnO1xuICBpZiAoY29tcHV0ZVBvc2l0aW9ucykge1xuICAgIHJldHVyblZhbHVlID0gJ21pbk1heFBvc2l0aW9uJztcbiAgfSBlbHNlIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycpIHtcbiAgICByZXR1cm5WYWx1ZSA9ICdhdmdWYWx1ZSc7XG4gIH1cblxuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB4O1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7ZGVwdGh9LjApO1xuICAgICAgZmxvYXQgZCA9IG1vZCh5VGV4Q1IueCwgJHtkZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgeFJDQ29ybmVyID0gdmVjMih5UiwgeUMpICogdmVjMigke3N0cmlkZX0sICR7c3RyaWRlfSkgLVxuICAgICAgICAgIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IHhSQ29ybmVyID0geFJDQ29ybmVyLng7XG4gICAgICBmbG9hdCB4Q0Nvcm5lciA9IHhSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBtYXgvbWluIHgoPywgPywgZCkgdG8gZ2V0IHkoeVIsIHlDLCBkKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkXG4gICAgICBmbG9hdCBtaW5NYXhWYWx1ZSA9IDAuMDtcbiAgICAgIGZsb2F0IG1pbk1heFZhbHVlRm91bmQgPSAwLjA7XG4gICAgICBmbG9hdCBtaW5NYXhQb3NpdGlvbiA9IDAuMDtcbiAgICAgIGZsb2F0IGF2Z1ZhbHVlID0gMC4wO1xuXG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHhSQ29ybmVyICsgd1I7XG4gICAgICAgIGZsb2F0IHhUZXhSID0geFI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHhDQ29ybmVyICsgd0M7XG4gICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7ZGVwdGh9LjAgKyBkO1xuXG4gICAgICAgICAgdmVjMiB0ZXhDUiA9IHZlYzIoeFRleEMsIHhUZXhSKTtcblxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSByZXF1ZXN0ZWQgVVYgaXMgaW52YWxpZC5cbiAgICAgICAgICB2ZWMyIHV2ID0gKHRleENSICsgaGFsZkNSKSAvIHhTaGFwZUNSO1xuICAgICAgICAgIGJvb2wgbGVzc1RoYW5aZXJvID0gYW55KGxlc3NUaGFuKHV2LCB2ZWMyKDAsIDApKSk7XG4gICAgICAgICAgYm9vbCBncmVhdGVyVGhhbk9uZSA9IGFueShncmVhdGVyVGhhbih1diwgdmVjMigxLCAxKSkpO1xuICAgICAgICAgIGJvb2wgb3V0c2lkZSA9IGxlc3NUaGFuWmVybyB8fCBncmVhdGVyVGhhbk9uZTtcbiAgICAgICAgICBpZiAob3V0c2lkZSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQoeCwgdXYpLnI7XG4gICAgICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICgke3Bvb2xUeXBlID09PSAnYXZnJ30pIHtcbiAgICAgICAgICAgIGF2Z1ZhbHVlICs9IHZhbHVlIC8gJHtmU2l6ZSAqIGZTaXplfS4wO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBJZiBhIG1pbiAvIG1heCB2YWx1ZSBoYXMgYWxyZWFkeSBiZWVuIGZvdW5kLCB1c2UgaXQuIElmIG5vdCwgdXNlXG4gICAgICAgICAgICAvLyB0aGUgY3VycmVudCB2YWx1ZS5cbiAgICAgICAgICAgIGZsb2F0IGN1cnJlbnRNaW5NYXhWYWx1ZSA9IG1peChcbiAgICAgICAgICAgICAgICB2YWx1ZSwgbWluTWF4VmFsdWUsIG1pbk1heFZhbHVlRm91bmQpO1xuICAgICAgICAgICAgaWYgKHZhbHVlICR7cG9vbFR5cGUgPT09ICdtaW4nID8gJzw9JyA6ICc+PSd9IGN1cnJlbnRNaW5NYXhWYWx1ZSkge1xuICAgICAgICAgICAgICBtaW5NYXhWYWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgICBtaW5NYXhWYWx1ZUZvdW5kID0gMS4wO1xuICAgICAgICAgICAgICBpZiAoJHtjb21wdXRlUG9zaXRpb25zfSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFBvc2l0aW9uID0gd1IgKiAke2ZTaXplfS4wICsgd0M7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoJHtyZXR1cm5WYWx1ZX0sIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcG9vbENvbW1vbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgsICd4JywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHN1bSA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIHN1bSArPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoc3VtLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlZHVjZVN1bShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZWR1Y2VTdW1Qcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVkdWNlU3VtUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkUmVkdWNlU3VtRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPVxuICAgICAgZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zKSk7XG4gIGNvbnN0IGFUZXh0dXJlOiBXZWJHTFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCByZXN1bHRUZXh0dXJlOiBXZWJHTFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICByZWR1Y2VTdW0oZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCAxLCAxKVswXTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0UmVsdVVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCByZXN1bHQgPSAodmFsdWUgPCAwLjAgPyAwLjAgOiB2YWx1ZSk7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyZXN1bHQsIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFJlbHVVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVsdShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZWx1UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHJlbHVQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkUmVsdURvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFJlbHVVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmVuZGVyUkdCU2hhZGVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGRlc3RpbmF0aW9uV2lkdGg6IG51bWJlcik6IFdlYkdMUHJvZ3JhbSB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgZmxvYXQgZGVzdGluYXRpb25XaWR0aCA9ICR7ZGVzdGluYXRpb25XaWR0aH0uMDtcbiAgICBjb25zdCBmbG9hdCBhID0gMS4wO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgeHIgPSBmbG9vcihyZXN1bHRVVi5zICogZGVzdGluYXRpb25XaWR0aCkgKiAzLjA7XG4gICAgICB2ZWMzIHggPSB4ciArIHZlYzMoMCwgMSwgMik7XG5cbiAgICAgIGZsb2F0IHNvdXJjZVdpZHRoID0gZGVzdGluYXRpb25XaWR0aCAqIDMuMDtcbiAgICAgIHZlYzMgdSA9ICh4ICsgMC41KSAvIHNvdXJjZVdpZHRoO1xuICAgICAgZmxvYXQgdiA9IDEuMCAtIHJlc3VsdFVWLnQ7XG5cbiAgICAgIGZsb2F0IHIgPSB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHVbMF0sIHYpKS5yO1xuICAgICAgZmxvYXQgZyA9IHRleHR1cmUyRChzb3VyY2UsIHZlYzIodVsxXSwgdikpLnI7XG4gICAgICBmbG9hdCBiID0gdGV4dHVyZTJEKHNvdXJjZSwgdmVjMih1WzJdLCB2KSkucjtcblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyLCBnLCBiLCBhKTtcbiAgICB9YDtcblxuICByZXR1cm4gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb0NhbnZhcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZW5kZXJTaGFkZXI6IFdlYkdMUHJvZ3JhbSwgc291cmNlVGV4OiBXZWJHTFRleHR1cmUpIHtcbiAgd2ViZ2xfdXRpbC5iaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihncGdwdS5nbCk7XG4gIHJlbmRlclRvRnJhbWVidWZmZXIoZ3BncHUsIHJlbmRlclNoYWRlciwgc291cmNlVGV4KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvRnJhbWVidWZmZXIoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVuZGVyU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVuZGVyU2hhZGVyKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNvdXJjZVRleCwgJ3NvdXJjZScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHVuaWZvcm0gdmVjMiBpbnB1dERpbUNSO1xuICAgIHVuaWZvcm0gdmVjMiByZXN1bHREaW1DUjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgcmVzdWx0Q1IgPSBmbG9vcihyZXN1bHRVViAqIHJlc3VsdERpbUNSKTtcbiAgICAgIC8vIGluZGV4SW5GbGF0ID0gcm93ICogc3RyaWRlICsgY29sdW1uLCB3aGVyZSBzdHJpZGUgPT0gbnVtT3V0cHV0Q29sdW1uc1xuICAgICAgZmxvYXQgaW5kZXhJbkZsYXQgPSByZXN1bHRDUi55ICogcmVzdWx0RGltQ1IueCArIHJlc3VsdENSLng7XG5cbiAgICAgIHZlYzIgaW5wdXRDUiA9IHZlYzIoXG4gICAgICAgIG1vZChpbmRleEluRmxhdCwgaW5wdXREaW1DUi54KSwgLy8gY29sID0gaW5kZXhJbkZsYXQgJSBudW1JbnB1dENvbHVtbnNcbiAgICAgICAgZmxvb3IoaW5kZXhJbkZsYXQgLyBpbnB1dERpbUNSLngpIC8vIHJvdyA9IGluZGV4SW5GbGF0IC8gbnVtSW5wdXRDb2x1bW5zXG4gICAgICApICsgaGFsZkNSO1xuXG4gICAgICB2ZWMyIGlucHV0VVYgPSBpbnB1dENSIC8gaW5wdXREaW1DUjtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRChtYXRyaXhBLCBpbnB1dFVWKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc2hhcGUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVzaGFwZVByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFOdW1Sb3dzOiBudW1iZXIsIGFOdW1Db2xzOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdE51bVJvd3M6IG51bWJlciwgcmVzdWx0TnVtQ29sczogbnVtYmVyKSB7XG4gIGNvbnN0IGlucHV0U2l6ZSA9IGFOdW1Sb3dzICogYU51bUNvbHM7XG4gIGNvbnN0IG91dHB1dFNpemUgPSByZXN1bHROdW1Db2xzICogcmVzdWx0TnVtUm93cztcbiAgdXRpbC5hc3NlcnQoXG4gICAgICBpbnB1dFNpemUgPT09IG91dHB1dFNpemUsXG4gICAgICBgVGhlIGlucHV0IHNpemUgKCR7aW5wdXRTaXplfSkgYW5kIG91dHB1dCBzaXplICgke291dHB1dFNpemV9KSBgICtcbiAgICAgICAgICBgbXVzdCBtYXRjaGApO1xuXG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByZXN1bHROdW1Sb3dzLCByZXN1bHROdW1Db2xzKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShyZXNoYXBlUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuXG4gIGNvbnN0IGlucHV0RGltQ1JMb2NhdGlvbiA9IGdwZ3B1LmdldFVuaWZvcm1Mb2NhdGlvbignaW5wdXREaW1DUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYoaW5wdXREaW1DUkxvY2F0aW9uLCBhTnVtQ29scywgYU51bVJvd3MpO1xuXG4gIGNvbnN0IHJlc3VsdERpbUNSTG9jYXRpb24gPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ3Jlc3VsdERpbUNSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihyZXN1bHREaW1DUkxvY2F0aW9uLCByZXN1bHROdW1Db2xzLCByZXN1bHROdW1Sb3dzKTtcblxuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBpbnB1dFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgb3V0cHV0RGltZW5zaW9uc1Jvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogc3RyaW5nIHtcbiAgY29uc3QgZGVwdGggPSBpbnB1dFNoYXBlUkNEWzJdO1xuXG4gIGNvbnN0IGlucHV0VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoaW5wdXRTaGFwZVJDRCk7XG5cbiAgY29uc3QgZWZmZWN0aXZlSW5wdXRTaGFwZVJDRCA9IGFsaWduQ29ybmVycyA/XG4gICAgICBbaW5wdXRTaGFwZVJDRFswXSAtIDEsIGlucHV0U2hhcGVSQ0RbMV0gLSAxLCBkZXB0aF0gOlxuICAgICAgaW5wdXRTaGFwZVJDRDtcblxuICBjb25zdCBlZmZlY3RpdmVPdXRwdXRTaGFwZVJDRCA9IGFsaWduQ29ybmVycyA/XG4gICAgICBbb3V0cHV0RGltZW5zaW9uc1Jvd0NvbFswXSAtIDEsIG91dHB1dERpbWVuc2lvbnNSb3dDb2xbMV0gLSAxLCBkZXB0aF0gOlxuICAgICAgW291dHB1dERpbWVuc2lvbnNSb3dDb2xbMF0sIG91dHB1dERpbWVuc2lvbnNSb3dDb2xbMV0sIGRlcHRoXTtcblxuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgY29uc3QgdmVjMiBpbnB1dFNoYXBlQ1IgPSB2ZWMyKCR7aW5wdXRTaGFwZVJDRFsxXX0sICR7aW5wdXRTaGFwZVJDRFswXX0pO1xuICAgIGNvbnN0IHZlYzIgaW5wdXRTaGFwZVRleENSID0gdmVjMihcbiAgICAgICAgJHtpbnB1dFRleFNoYXBlUkNbMV19LCAke2lucHV0VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgY29uc3QgdmVjMiBlZmZlY3RpdmVJbnB1dE92ZXJPdXRwdXRSYXRpb0NSID0gdmVjMihcbiAgICAgICAgJHtlZmZlY3RpdmVJbnB1dFNoYXBlUkNEWzFdIC8gZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0RbMV19LFxuICAgICAgICAke2VmZmVjdGl2ZUlucHV0U2hhcGVSQ0RbMF0gLyBlZmZlY3RpdmVPdXRwdXRTaGFwZVJDRFswXX0pO1xuXG4gICAgZmxvYXQgc2FtcGxlSW5wdXQoZmxvYXQgY29sLCBmbG9hdCByb3csIGZsb2F0IGQpIHtcbiAgICAgIHZlYzIgdXYgPSAodmVjMihjb2wgKiAke2RlcHRofS4wICsgZCwgcm93KSArIGhhbGZDUikgLyBpbnB1dFNoYXBlVGV4Q1I7XG4gICAgICByZXR1cm4gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgIH1cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgZCkuXG4gICAgICB2ZWMyIHlDUiA9IHZlYzIoZmxvb3IoeVRleENSLnggLyAke2RlcHRofS4wKSwgeVRleENSLnkpO1xuICAgICAgZmxvYXQgZCA9IG1vZCh5VGV4Q1IueCwgJHtkZXB0aH0uMCk7XG5cbiAgICAgIC8vIEZyYWN0aW9uYWwgc291cmNlIGluZGV4LlxuICAgICAgdmVjMiBzb3VyY2VGcmFjSW5kZXhDUiA9IHlDUiAqIGVmZmVjdGl2ZUlucHV0T3Zlck91dHB1dFJhdGlvQ1I7XG5cbiAgICAgIC8vIENvbXB1dGUgdGhlIGZvdXIgaW50ZWdlciBpbmRpY2VzLlxuICAgICAgdmVjMiBzb3VyY2VGbG9vckNSID0gZmxvb3Ioc291cmNlRnJhY0luZGV4Q1IpO1xuICAgICAgdmVjMiBzb3VyY2VDZWlsQ1IgPSBtaW4oaW5wdXRTaGFwZUNSIC0gMS4wLCBjZWlsKHNvdXJjZUZyYWNJbmRleENSKSk7XG5cbiAgICAgIGZsb2F0IHRvcExlZnQgPSBzYW1wbGVJbnB1dChzb3VyY2VGbG9vckNSWzBdLCBzb3VyY2VGbG9vckNSWzFdLCBkKTtcbiAgICAgIGZsb2F0IGJvdHRvbUxlZnQgPSBzYW1wbGVJbnB1dChzb3VyY2VGbG9vckNSWzBdLCBzb3VyY2VDZWlsQ1JbMV0sIGQpO1xuICAgICAgZmxvYXQgdG9wUmlnaHQgPSBzYW1wbGVJbnB1dChzb3VyY2VDZWlsQ1JbMF0sIHNvdXJjZUZsb29yQ1JbMV0sIGQpO1xuICAgICAgZmxvYXQgYm90dG9tUmlnaHQgPSBzYW1wbGVJbnB1dChzb3VyY2VDZWlsQ1JbMF0sIHNvdXJjZUNlaWxDUlsxXSwgZCk7XG5cbiAgICAgIHZlYzIgZnJhY0NSID0gc291cmNlRnJhY0luZGV4Q1IgLSBzb3VyY2VGbG9vckNSO1xuXG4gICAgICBmbG9hdCB0b3AgPSB0b3BMZWZ0ICsgKHRvcFJpZ2h0IC0gdG9wTGVmdCkgKiBmcmFjQ1JbMF07XG4gICAgICBmbG9hdCBib3R0b20gPSBib3R0b21MZWZ0ICsgKGJvdHRvbVJpZ2h0IC0gYm90dG9tTGVmdCkgKiBmcmFjQ1JbMF07XG4gICAgICBmbG9hdCBuZXdWYWx1ZSA9IHRvcCArIChib3R0b20gLSB0b3ApICogZnJhY0NSWzFdO1xuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KG5ld1ZhbHVlLCAwLjAsIDAuMCwgMC4wKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc2l6ZUJpbGluZWFyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlc2l6ZUJpbGluZWFyUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVzaXplQmlsaW5lYXJQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9uZGFycmF5JztcblxuZXhwb3J0IHR5cGUgSW5wdXQgPSB7XG4gIG5hbWU6IHN0cmluZzsgYXJyYXk6IE5EQXJyYXk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gbWFrZVNoYWRlcktleShpbnB1dHM6IE5EQXJyYXlbXSwgb3V0cHV0OiBOREFycmF5KTogc3RyaW5nIHtcbiAgY29uc3QgaW5zID0gaW5wdXRzLm1hcCh4ID0+IHguc2hhcGUgKyAnXycgKyB4LmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICByZXR1cm4gaW5zLmpvaW4oJ18nKSArICdfJyArIG91dHB1dC5zaGFwZSArICdfJyArIG91dHB1dC5nZXRUZXh0dXJlU2hhcGVSQygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWFrZVNoYWRlcihcbiAgICBpbnB1dHM6IElucHV0W10sIG91dHB1dDogTkRBcnJheSwgdXNlckNvZGU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGlucHV0UHJlZml4U25pcHBldCA9XG4gICAgICBpbnB1dHMubWFwKHggPT4gYHVuaWZvcm0gc2FtcGxlcjJEICR7eC5uYW1lfTtgKS5qb2luKCdcXG4nKTtcbiAgY29uc3QgaW5wdXRTYW1wbGluZ1NuaXBwZXQgPVxuICAgICAgaW5wdXRzLm1hcCh4ID0+IGdldElucHV0U2FtcGxpbmdTbmlwcGV0KHgpKS5qb2luKCdcXG4nKTtcbiAgY29uc3Qgb3V0VGV4U2hhcGUgPSBvdXRwdXQuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgY29uc3Qgb3V0cHV0U2FtcGxpbmdTbmlwcGV0ID1cbiAgICAgIGdldE91dHB1dFNhbXBsaW5nU25pcHBldChvdXRwdXQuc2hhcGUsIG91dFRleFNoYXBlKTtcbiAgY29uc3Qgc291cmNlID0gW1xuICAgIFNIQURFUl9QUkVGSVgsIGlucHV0UHJlZml4U25pcHBldCwgU0FNUExFXzJEX1NOSVBQRVQsIGlucHV0U2FtcGxpbmdTbmlwcGV0LFxuICAgIG91dHB1dFNhbXBsaW5nU25pcHBldCwgdXNlckNvZGVcbiAgXS5qb2luKCdcXG4nKTtcbiAgcmV0dXJuIHNvdXJjZTtcbn1cblxuZnVuY3Rpb24gZ2V0SW5wdXRTYW1wbGluZ1NuaXBwZXQoaW5wdXQ6IElucHV0KSB7XG4gIGNvbnN0IGFyciA9IGlucHV0LmFycmF5O1xuICBjb25zdCBzaGFwZSA9IGFyci5zaGFwZTtcbiAgY29uc3QgdGV4U2hhcGUgPSBhcnIuZ2V0VGV4dHVyZVNoYXBlUkMoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSk7XG4gIHN3aXRjaCAoc2hhcGUubGVuZ3RoKSB7XG4gICAgY2FzZSAyOlxuICAgICAgcmV0dXJuIGdldFNhbXBsZXIyRChpbnB1dC5uYW1lLCBzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXJdLCB0ZXhTaGFwZSk7XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJHthcnIucmFua30tRCBpbnB1dCBzYW1wbGluZyBpcyBub3QgeWV0IHN1cHBvcnRlZGApO1xuICB9XG59XG5cbmZ1bmN0aW9uIGdldE91dHB1dFNhbXBsaW5nU25pcHBldChcbiAgICBvdXRTaGFwZTogbnVtYmVyW10sIG91dFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgc3dpdGNoIChvdXRTaGFwZS5sZW5ndGgpIHtcbiAgICBjYXNlIDI6XG4gICAgICByZXR1cm4gZ2V0T3V0cHV0MkRDb29yZHMob3V0U2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgb3V0VGV4U2hhcGUpO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYCR7b3V0U2hhcGUubGVuZ3RofS1EIG91dHB1dCBzYW1wbGluZyBpcyBub3QgeWV0IHN1cHBvcnRlZGApO1xuICB9XG59XG5cbmNvbnN0IFNIQURFUl9QUkVGSVggPSBgXG4gIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gIHZvaWQgc2V0T3V0cHV0KGZsb2F0IHZhbCkge1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsLCAwLCAwLCAwKTtcbiAgfVxuYDtcblxuY29uc3QgU0FNUExFXzJEX1NOSVBQRVQgPSBgXG4gIGZsb2F0IHNhbXBsZTJEKHNhbXBsZXIyRCB0ZXh0dXJlLCBmbG9hdCB0ZXhOdW1SLCBmbG9hdCB0ZXhOdW1DLCBmbG9hdCBudW1DLFxuICAgICAgZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICBmbG9hdCBpbmRleCA9IGRvdCh2ZWMyKHJvdywgY29sKSwgdmVjMihudW1DLCAxLjApKTtcbiAgICBmbG9hdCB0ZXhSID0gZmxvb3IoaW5kZXggLyB0ZXhOdW1DKTtcbiAgICBmbG9hdCB0ZXhDID0gbW9kKGluZGV4LCB0ZXhOdW1DKTtcbiAgICB2ZWMyIHV2ID0gKHZlYzIodGV4QywgdGV4UikgKyBoYWxmQ1IpIC8gdmVjMih0ZXhOdW1DLCB0ZXhOdW1SKTtcbiAgICByZXR1cm4gdGV4dHVyZTJEKHRleHR1cmUsIHV2KS5yO1xuICB9XG5gO1xuXG5mdW5jdGlvbiBnZXRPdXRwdXQyRENvb3JkcyhcbiAgICBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgaWYgKHV0aWwuYXJyYXlzRXF1YWwoc2hhcGUsIHRleFNoYXBlKSkge1xuICAgIHJldHVybiBgXG4gICAgICB2ZWMyIGdldE91dHB1dENvb3JkcygpIHtcbiAgICAgICAgcmV0dXJuIGZsb29yKGdsX0ZyYWdDb29yZC55eCk7XG4gICAgICB9XG4gICAgYDtcbiAgfVxuICByZXR1cm4gYFxuICAgIHZlYzIgZ2V0T3V0cHV0Q29vcmRzKCkge1xuICAgICAgdmVjMiByZXNUZXhSQyA9IGZsb29yKGdsX0ZyYWdDb29yZC55eCk7XG4gICAgICBmbG9hdCBpbmRleCA9IGRvdChyZXNUZXhSQywgdmVjMigke3RleFNoYXBlWzFdfS4wLCAxLjApKTtcbiAgICAgIGZsb2F0IHIgPSBmbG9vcihpbmRleCAvICR7c2hhcGVbMV19LjApO1xuICAgICAgZmxvYXQgYyA9IG1vZChpbmRleCwgJHtzaGFwZVsxXX0uMCk7XG4gICAgICByZXR1cm4gdmVjMihyLCBjKTtcbiAgICB9XG4gIGA7XG59XG5cbmZ1bmN0aW9uIGdldFNhbXBsZXIyRChcbiAgICB0ZXhOYW1lOiBzdHJpbmcsIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCB0ZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBmdW5jTmFtZSA9ICdnZXQnICsgdGV4TmFtZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIHRleE5hbWUuc2xpY2UoMSk7XG4gIGNvbnN0IHRSID0gdGV4U2hhcGVbMF07XG4gIGNvbnN0IHRDID0gdGV4U2hhcGVbMV07XG4gIGlmICh1dGlsLmFycmF5c0VxdWFsKHNoYXBlLCB0ZXhTaGFwZSkpIHtcbiAgICByZXR1cm4gYFxuICAgICAgZmxvYXQgJHtmdW5jTmFtZX0oZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGNvbCwgcm93KSArIGhhbGZDUikgLyB2ZWMyKCR7dEN9LjAsICR7dFJ9LjApO1xuICAgICAgICByZXR1cm4gdGV4dHVyZTJEKCR7dGV4TmFtZX0sIHV2KS5yO1xuICAgICAgfVxuICAgIGA7XG4gIH1cbiAgcmV0dXJuIGBcbiAgICBmbG9hdCAke2Z1bmNOYW1lfShmbG9hdCByb3csIGZsb2F0IGNvbCkge1xuICAgICAgcmV0dXJuIHNhbXBsZTJEKCR7dGV4TmFtZX0sICR7dFJ9LjAsICR7dEN9LjAsICR7c2hhcGVbMV19LjAsIHJvdywgY29sKTtcbiAgICB9XG4gIGA7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFNpZ21vaWRVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNCgxLjAgLyAoMS4wICsgZXhwKC0xLjAgKiB2YWx1ZSkpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2lnbW9pZEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRTaWdtb2lkVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpZ21vaWQoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgc2lnbW9pZFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBzaWdtb2lkUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNpZ21vaWREb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChcbiAgICAgIGEsIHJvd3MsIGNvbHVtbnMsIGdldFNpZ21vaWRVbmFyeU9wKCkpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0U3RlcFVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCByZXMgPSB2YWx1ZSA9PSB2YWx1ZSA/ICh2YWx1ZSA+IDAuMCA/IDEuMCA6IDAuMCkgOiB2YWx1ZTtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHJlcywgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U3RlcFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzdGVwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHN0ZXBQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc3RlcFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTdGVwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0U3RlcFVuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbY29sdW1ucywgcm93c107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgIG1hdHJpeFNpemU6IG51bWJlciwgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gbWF0cml4U2l6ZSAqIGNoYW5uZWxzUGVyVGV4dHVyZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldENvbG9yTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtjb2x1bW5zICogNCwgcm93c107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNYXRyaXhTaXplRnJvbVVucGFja2VkQXJyYXlTaXplKFxuICAgIHVucGFja2VkU2l6ZTogbnVtYmVyLCBjaGFubmVsc1BlclRleHR1cmU6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh1bnBhY2tlZFNpemUgJSBjaGFubmVsc1BlclRleHR1cmUgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd1bnBhY2tlZFNpemUgKCcgKyB1bnBhY2tlZFNpemUgKyAnKSBtdXN0IGJlIGEgbXVsdGlwbGUgb2YgJyArXG4gICAgICAgIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIH1cbiAgcmV0dXJuIHVucGFja2VkU2l6ZSAvIGNoYW5uZWxzUGVyVGV4dHVyZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZU1hdHJpeFRvVW5wYWNrZWRBcnJheShcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSwgdW5wYWNrZWRBcnJheTogRmxvYXQzMkFycmF5LFxuICAgIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9XG4gICAgICBnZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKG1hdHJpeC5sZW5ndGgsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIGlmICh1bnBhY2tlZEFycmF5Lmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ3VucGFja2VkQXJyYXkgbGVuZ3RoICgnICsgdW5wYWNrZWRBcnJheS5sZW5ndGggK1xuICAgICAgICAnKSBtdXN0IGJlID49ICcgKyByZXF1aXJlZFNpemUpO1xuICB9XG4gIGxldCBkc3QgPSAwO1xuICBmb3IgKGxldCBzcmMgPSAwOyBzcmMgPCBtYXRyaXgubGVuZ3RoOyArK3NyYykge1xuICAgIHVucGFja2VkQXJyYXlbZHN0XSA9IG1hdHJpeFtzcmNdO1xuICAgIGRzdCArPSBjaGFubmVsc1BlclRleHR1cmU7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU1hdHJpeEZyb21VbnBhY2tlZEFycmF5KFxuICAgIHVucGFja2VkQXJyYXk6IEZsb2F0MzJBcnJheSwgbWF0cml4OiBGbG9hdDMyQXJyYXksXG4gICAgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpIHtcbiAgY29uc3QgcmVxdWlyZWRTaXplID0gZ2V0TWF0cml4U2l6ZUZyb21VbnBhY2tlZEFycmF5U2l6ZShcbiAgICAgIHVucGFja2VkQXJyYXkubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBpZiAobWF0cml4Lmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ21hdHJpeCBsZW5ndGggKCcgKyBtYXRyaXgubGVuZ3RoICsgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgc3JjID0gMDsgc3JjIDwgdW5wYWNrZWRBcnJheS5sZW5ndGg7IHNyYyArPSBjaGFubmVsc1BlclRleHR1cmUpIHtcbiAgICBtYXRyaXhbZHN0KytdID0gdW5wYWNrZWRBcnJheVtzcmNdO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW01hdGguY2VpbChjb2x1bW5zIC8gMiksIE1hdGguY2VpbChyb3dzIC8gMildO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IFt3LCBoXSA9IGdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICByZXR1cm4gdyAqIGggKiA0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZW5jb2RlTWF0cml4VG9QYWNrZWRSR0JBKFxuICAgIG1hdHJpeDogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBwYWNrZWRSR0JBOiBGbG9hdDMyQXJyYXkpIHtcbiAgY29uc3QgcmVxdWlyZWRTaXplID0gZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKTtcbiAgaWYgKHBhY2tlZFJHQkEubGVuZ3RoIDwgcmVxdWlyZWRTaXplKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAncGFja2VkUkdCQSBsZW5ndGggKCcgKyBwYWNrZWRSR0JBLmxlbmd0aCArXG4gICAgICAgICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgLypcbiAgICBVbnBhY2tlZCBtYXRyaXgsIHJvdy1tYWpvciBvcmRlciBpbiBGbG9hdDMyQXJyYXlbMTZdOiAgQSBCIEMgRFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFIEYgRyBIXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEkgSiBLIExcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTSBOIE8gUFxuXG4gICAgUGFja2VkIG1hdHJpeCwgMngyIFJHQkEzMiB0ZXh0dXJlIChtZW1vcnkgdmlldyk6ICAgICAgIEFCRUYgQ0RHSCBJSk1OIEtMT1BcblxuICAgIFBhY2tlZCBtYXRyaXgsIDJ4MiBSR0JBMzIgdGV4dHVyZSAobWF0cml4IHZpZXcpOiAgICAgICBBQnxDRFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFRnxHSFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLSstLVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJSnxLTFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNTnxPUFxuICAgKi9cbiAgY29uc3QgW3RleHR1cmVXaWR0aCwgdGV4dHVyZUhlaWdodF0gPVxuICAgICAgZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG9kZFdpZHRoID0gKGNvbHVtbnMgJSAyKSA9PT0gMTtcbiAgY29uc3Qgb2RkSGVpZ2h0ID0gKHJvd3MgJSAyKSA9PT0gMTtcbiAgY29uc3Qgd2lkdGhJbkZ1bGxCbG9ja3MgPSBNYXRoLmZsb29yKGNvbHVtbnMgLyAyKTtcbiAgY29uc3QgaGVpZ2h0SW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihyb3dzIC8gMik7XG5cbiAgLy8gbG9vcCBvdmVyIGZ1bGwgMngyIGJsb2Nrc1xuICB7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gKG9kZFdpZHRoID8gNCA6IDApO1xuICAgIGNvbnN0IG9uZVJvdyA9IGNvbHVtbnM7XG4gICAgbGV0IGRzdCA9IDA7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgY29uc3QgbWF0cml4U3JjUm93ID0gKGJsb2NrWSAqIDIgKiBjb2x1bW5zKTtcbiAgICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgICBjb25zdCBtYXRyaXhTcmNDb2wgPSBibG9ja1ggKiAyO1xuICAgICAgICBjb25zdCBzcmMgPSBtYXRyaXhTcmNSb3cgKyBtYXRyaXhTcmNDb2w7XG4gICAgICAgIHBhY2tlZFJHQkFbZHN0XSA9IG1hdHJpeFtzcmNdO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDFdID0gbWF0cml4W3NyYyArIDFdO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDJdID0gbWF0cml4W3NyYyArIG9uZVJvd107XG4gICAgICAgIHBhY2tlZFJHQkFbZHN0ICsgM10gPSBtYXRyaXhbc3JjICsgb25lUm93ICsgMV07XG4gICAgICAgIGRzdCArPSA0O1xuICAgICAgfVxuICAgICAgZHN0ICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGRvd24gZmluYWwgb2RkIGNvbHVtblxuICBpZiAob2RkV2lkdGgpIHtcbiAgICBsZXQgc3JjID0gY29sdW1ucyAtIDE7XG4gICAgbGV0IGRzdCA9ICh0ZXh0dXJlV2lkdGggLSAxKSAqIDQ7XG4gICAgY29uc3Qgc3JjU3RyaWRlID0gMiAqIGNvbHVtbnM7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gdGV4dHVyZVdpZHRoICogNDtcbiAgICBmb3IgKGxldCBibG9ja1kgPSAwOyBibG9ja1kgPCBoZWlnaHRJbkZ1bGxCbG9ja3M7ICsrYmxvY2tZKSB7XG4gICAgICBwYWNrZWRSR0JBW2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICAgIHBhY2tlZFJHQkFbZHN0ICsgMl0gPSBtYXRyaXhbc3JjICsgY29sdW1uc107XG4gICAgICBzcmMgKz0gc3JjU3RyaWRlO1xuICAgICAgZHN0ICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGFjcm9zcyBmaW5hbCByb3dcbiAgaWYgKG9kZEhlaWdodCkge1xuICAgIGxldCBzcmMgPSAocm93cyAtIDEpICogY29sdW1ucztcbiAgICBsZXQgZHN0ID0gKHRleHR1cmVIZWlnaHQgLSAxKSAqIHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgZm9yIChsZXQgYmxvY2tYID0gMDsgYmxvY2tYIDwgd2lkdGhJbkZ1bGxCbG9ja3M7ICsrYmxvY2tYKSB7XG4gICAgICBwYWNrZWRSR0JBW2RzdCsrXSA9IG1hdHJpeFtzcmMrK107XG4gICAgICBwYWNrZWRSR0JBW2RzdCsrXSA9IG1hdHJpeFtzcmMrK107XG4gICAgICBkc3QgKz0gMjtcbiAgICB9XG4gIH1cblxuICAvLyBmaWxsIGluIGJvdHRvbS1yaWdodCB0ZXhlbFxuICBpZiAob2RkV2lkdGggJiYgb2RkSGVpZ2h0KSB7XG4gICAgcGFja2VkUkdCQVtwYWNrZWRSR0JBLmxlbmd0aCAtIDRdID0gbWF0cml4W21hdHJpeC5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHJldHVybiBwYWNrZWRSR0JBO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlTWF0cml4RnJvbVBhY2tlZFJHQkEoXG4gICAgcGFja2VkUkdCQTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSk6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IHJvd3MgKiBjb2x1bW5zO1xuICBpZiAocmVxdWlyZWRTaXplIDwgbWF0cml4Lmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ21hdHJpeCBsZW5ndGggKCcgKyBtYXRyaXgubGVuZ3RoICsgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBjb25zdCBvZGRXaWR0aCA9IChjb2x1bW5zICUgMikgPT09IDE7XG4gIGNvbnN0IG9kZEhlaWdodCA9IChyb3dzICUgMikgPT09IDE7XG4gIGNvbnN0IHdpZHRoSW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihjb2x1bW5zIC8gMik7XG4gIGNvbnN0IGhlaWdodEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3Iocm93cyAvIDIpO1xuICBjb25zdCBbdGV4dHVyZVdpZHRoLCB0ZXh0dXJlSGVpZ2h0XSA9XG4gICAgICBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICAvLyBsb29wIG92ZXIgZnVsbCAyeDIgYmxvY2tzXG4gIHtcbiAgICBjb25zdCBzcmNTdHJpZGUgPSBvZGRXaWR0aCA/IDQgOiAwO1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IGNvbHVtbnMgKyAob2RkV2lkdGggPyAxIDogMCk7XG4gICAgbGV0IHNyYyA9IDA7XG4gICAgbGV0IGRzdFJvdzEgPSAwO1xuICAgIGxldCBkc3RSb3cyID0gY29sdW1ucztcbiAgICBmb3IgKGxldCBibG9ja1kgPSAwOyBibG9ja1kgPCBoZWlnaHRJbkZ1bGxCbG9ja3M7ICsrYmxvY2tZKSB7XG4gICAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgICAgbWF0cml4W2RzdFJvdzErK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzErK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzIrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzIrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIH1cbiAgICAgIHNyYyArPSBzcmNTdHJpZGU7XG4gICAgICBkc3RSb3cxICs9IGRzdFN0cmlkZTtcbiAgICAgIGRzdFJvdzIgKz0gZHN0U3RyaWRlO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvb3AgZG93biBmaW5hbCBjb2x1bW5cbiAgaWYgKG9kZFdpZHRoKSB7XG4gICAgbGV0IHNyYyA9ICh0ZXh0dXJlV2lkdGggLSAxKSAqIDQ7XG4gICAgbGV0IGRzdCA9IGNvbHVtbnMgLSAxO1xuICAgIGNvbnN0IHNyY1N0cmlkZSA9IHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gMiAqIGNvbHVtbnM7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgbWF0cml4W2RzdF0gPSBwYWNrZWRSR0JBW3NyY107XG4gICAgICBtYXRyaXhbZHN0ICsgY29sdW1uc10gPSBwYWNrZWRSR0JBW3NyYyArIDJdO1xuICAgICAgc3JjICs9IHNyY1N0cmlkZTtcbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBhY3Jvc3MgZmluYWwgcm93XG4gIGlmIChvZGRIZWlnaHQpIHtcbiAgICBsZXQgc3JjID0gKHRleHR1cmVIZWlnaHQgLSAxKSAqIHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgbGV0IGRzdCA9IChyb3dzIC0gMSkgKiBjb2x1bW5zO1xuICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgbWF0cml4W2RzdCsrXSA9IHBhY2tlZFJHQkFbc3JjKytdO1xuICAgICAgbWF0cml4W2RzdCsrXSA9IHBhY2tlZFJHQkFbc3JjKytdO1xuICAgICAgc3JjICs9IDI7XG4gICAgfVxuICB9XG5cbiAgLy8gZmlsbCBpbiBib3R0b20tcmlnaHQgY2VsbFxuICBpZiAob2RkV2lkdGggJiYgb2RkSGVpZ2h0KSB7XG4gICAgbWF0cml4W21hdHJpeC5sZW5ndGggLSAxXSA9IHBhY2tlZFJHQkFbcGFja2VkUkdCQS5sZW5ndGggLSA0XTtcbiAgfVxuXG4gIHJldHVybiBtYXRyaXg7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgY2xhc3MgVGV4dHVyZU1hbmFnZXIge1xuICBwcml2YXRlIG51bVVzZWRUZXh0dXJlcyA9IDA7XG4gIHByaXZhdGUgbnVtRnJlZVRleHR1cmVzID0gMDtcbiAgcHJpdmF0ZSBmcmVlVGV4dHVyZXM6IHtbc2hhcGU6IHN0cmluZ106IFdlYkdMVGV4dHVyZVtdfSA9IHt9O1xuICBwcml2YXRlIGxvZ0VuYWJsZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSB1c2VkVGV4dHVyZUNvdW50OiB7W3NoYXBlOiBzdHJpbmddOiBudW1iZXJ9ID0ge307XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBncGdwdTogR1BHUFVDb250ZXh0KSB7fVxuXG4gIGFjcXVpcmVUZXh0dXJlKHNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pOiBXZWJHTFRleHR1cmUge1xuICAgIGNvbnN0IHNoYXBlS2V5ID0gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZVJDKTtcbiAgICBpZiAoIShzaGFwZUtleSBpbiB0aGlzLmZyZWVUZXh0dXJlcykpIHtcbiAgICAgIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XSA9IFtdO1xuICAgIH1cbiAgICBpZiAoIShzaGFwZUtleSBpbiB0aGlzLnVzZWRUZXh0dXJlQ291bnQpKSB7XG4gICAgICB0aGlzLnVzZWRUZXh0dXJlQ291bnRbc2hhcGVLZXldID0gMDtcbiAgICB9XG4gICAgdGhpcy51c2VkVGV4dHVyZUNvdW50W3NoYXBlS2V5XSsrO1xuXG4gICAgaWYgKHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLm51bUZyZWVUZXh0dXJlcy0tO1xuICAgICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMrKztcbiAgICAgIHRoaXMubG9nKCk7XG4gICAgICByZXR1cm4gdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldLnNoaWZ0KCkhO1xuICAgIH1cbiAgICB0aGlzLm51bVVzZWRUZXh0dXJlcysrO1xuICAgIHRoaXMubG9nKCk7XG5cbiAgICByZXR1cm4gdGhpcy5ncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHNoYXBlUkNbMF0sIHNoYXBlUkNbMV0pO1xuICB9XG5cbiAgcmVsZWFzZVRleHR1cmUodGV4dHVyZTogV2ViR0xUZXh0dXJlLCBzaGFwZTogW251bWJlciwgbnVtYmVyXSk6IHZvaWQge1xuICAgIGNvbnN0IHNoYXBlS2V5ID0gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZSk7XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy5mcmVlVGV4dHVyZXMpKSB7XG4gICAgICB0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0gPSBbXTtcbiAgICB9XG4gICAgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldLnB1c2godGV4dHVyZSk7XG4gICAgdGhpcy5udW1GcmVlVGV4dHVyZXMrKztcbiAgICB0aGlzLm51bVVzZWRUZXh0dXJlcy0tO1xuICAgIHRoaXMudXNlZFRleHR1cmVDb3VudFtzaGFwZUtleV0tLTtcbiAgICB0aGlzLmxvZygpO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2coKSB7XG4gICAgaWYgKCF0aGlzLmxvZ0VuYWJsZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdG90YWwgPSB0aGlzLm51bUZyZWVUZXh0dXJlcyArIHRoaXMubnVtVXNlZFRleHR1cmVzO1xuICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAnRnJlZS9Vc2VkJywgdGhpcy5udW1GcmVlVGV4dHVyZXMgKyAnIC8gJyArIHRoaXMubnVtVXNlZFRleHR1cmVzLFxuICAgICAgICBgKCR7dG90YWx9KWApO1xuICB9XG5cbiAgZ2V0TnVtVXNlZFRleHR1cmVzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubnVtVXNlZFRleHR1cmVzO1xuICB9XG5cbiAgZ2V0TnVtRnJlZVRleHR1cmVzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubnVtRnJlZVRleHR1cmVzO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBmb3IgKGNvbnN0IHNoYXBlIGluIHRoaXMuZnJlZVRleHR1cmVzKSB7XG4gICAgICBpZiAodGhpcy5mcmVlVGV4dHVyZXMuaGFzT3duUHJvcGVydHkoc2hhcGUpKSB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVdLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgdGhpcy5ncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlXVtpXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZVJvd3NDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBzdHJpbmcge1xuICByZXR1cm4gc2hhcGVSb3dzQ29sWzBdICsgJ18nICsgc2hhcGVSb3dzQ29sWzFdO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG4vKipcbiAqIFNpbmVcbiAqL1xuZnVuY3Rpb24gZ2V0U2luVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoc2luKHZhbHVlKSwgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTaW5GcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U2luVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBzaW5Qcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc2luUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNpbkRvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFNpblVuYXJ5T3AoKSk7XG59XG5cbi8qKlxuICogVGFuaFxuICovXG5mdW5jdGlvbiBnZXRUYW5oVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGZsb2F0IGUyeCA9IGV4cCgtMi4wICogdmFsdWUpO1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoKDEuMCAtIGUyeCkgLyAoMS4wICsgZTJ4KSwgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUYW5oRnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFRhbmhVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdGFuaChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCB0YW5oUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHRhbmhQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkVGFuaERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFRhbmhVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJlc3VsdE9wOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeEEsIHJlc3VsdFVWKS5yO1xuICAgICAgJHtyZXN1bHRPcH1cbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYXJ5T3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgdW5hcnlPcFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnNldFByb2dyYW0odW5hcnlPcFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFVuYXJ5T3BEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIHJlc3VsdE9wOiBzdHJpbmcpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgZnJhZ21lbnRTaGFkZXJTcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyZXN1bHRPcCk7XG4gIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTcmMpO1xuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgdW5hcnlPcChncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleHR1cmUsIHJvd3MsIGNvbHVtbnMpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5sZXQgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xubGV0IFdFQkdMMl9FTkFCTEVEOiBib29sZWFufHVuZGVmaW5lZCA9IG51bGwhO1xubGV0IE1BWF9URVhUVVJFX1NJWkU6IG51bWJlciA9IG51bGwhO1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMge1xuICBhbHBoYT86IGJvb2xlYW47XG4gIGFudGlhbGlhcz86IGJvb2xlYW47XG4gIHByZW11bHRpcGxpZWRBbHBoYT86IGJvb2xlYW47XG4gIHByZXNlcnZlRHJhd2luZ0J1ZmZlcj86IGJvb2xlYW47XG4gIGRlcHRoPzogYm9vbGVhbjtcbiAgc3RlbmNpbD86IGJvb2xlYW47XG4gIGZhaWxJZk1ham9yUGVyZm9ybWFuY2VDYXZlYXQ/OiBib29sZWFuO1xufVxuXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGNvbnN0IElTX05BTl9TSEFERVJfRlVOQyA9IGBcbmJvb2wgaXNOYU4oZmxvYXQgdmFsKSB7XG4gIHJldHVybiB2YWwgPT0gdmFsID8gZmFsc2UgOiB0cnVlO1xufVxuYDtcblxuZXhwb3J0IGludGVyZmFjZSBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uIHsgbG9zZUNvbnRleHQoKTogdm9pZDsgfVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0KGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOlxuICAgIFdlYkdMUmVuZGVyaW5nQ29udGV4dCB7XG4gIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICBjYW52YXMud2lkdGggPSAxO1xuICBjYW52YXMuaGVpZ2h0ID0gMTtcbiAgcmV0dXJuIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoY2FudmFzLCBhdHRyaWJ1dGVzKTtcbn1cblxuLyoqXG4gKiBGb3JjZSB0aGUgbGlicmFyeSB0byBwcmVmZXIgV2ViR0wgMS4wIGluc3RlYWQgb2YgV2ViR0wgMi4wIGV2ZW4gd2hlbiBXZWJHTFxuICogMi4wIGlzIGF2YWlsYWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZWZlcldlYkdMMSgpIHtcbiAgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xuICBXRUJHTDJfRU5BQkxFRCA9IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBQcmVmZXIgV2ViR0wgMi4wIHRvIFdlYkdMIDEuMC4gVGhpcyBpcyB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZmVyV2ViR0wyKCkge1xuICBVU0VfV0VCR0wyX1dIRU5fQVZBSUxBQkxFID0gdHJ1ZTtcbiAgV0VCR0wyX0VOQUJMRUQgPSB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1dlYkdMMkVuYWJsZWQoKSB7XG4gIGlmICghVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGlmIChXRUJHTDJfRU5BQkxFRCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgdGVtcENhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNvbnN0IGdsID0gdGVtcENhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbDInKTtcbiAgICBpZiAoZ2wgIT0gbnVsbCkge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSB0cnVlO1xuXG4gICAgICBjb25zdCBsb3NlQ29udGV4dEV4dGVuc2lvbiA9XG4gICAgICAgICAgZ2V0RXh0ZW5zaW9uT3JUaHJvdyhcbiAgICAgICAgICAgICAgZ2wgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0LCAnV0VCR0xfbG9zZV9jb250ZXh0JykgYXNcbiAgICAgICAgICBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICAgICAgbG9zZUNvbnRleHRFeHRlbnNpb24ubG9zZUNvbnRleHQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIFdFQkdMMl9FTkFCTEVEO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0RnJvbUNhbnZhcyhcbiAgICBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50LFxuICAgIGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQge1xuICBsZXQgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgaWYgKGlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgZ2wgPSBjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wyJywgYXR0cmlidXRlcykgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB9IGVsc2Uge1xuICAgIGdsID0gKGNhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbCcsIGF0dHJpYnV0ZXMpIHx8XG4gICAgICAgICAgY2FudmFzLmdldENvbnRleHQoJ2V4cGVyaW1lbnRhbC13ZWJnbCcsIGF0dHJpYnV0ZXMpKSBhc1xuICAgICAgICBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIH1cblxuICBpZiAoZ2wgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignVGhpcyBicm93c2VyIGRvZXMgbm90IHN1cHBvcnQgV2ViR0wuJyk7XG4gIH1cbiAgcmV0dXJuIGdsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2FsbEFuZENoZWNrPFQ+KGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZ1bmM6ICgpID0+IFQpOiBUIHtcbiAgY29uc3QgcmV0dXJuVmFsdWUgPSBmdW5jKCk7XG4gIGNoZWNrV2ViR0xFcnJvcihnbCk7XG4gIHJldHVybiByZXR1cm5WYWx1ZTtcbn1cblxubGV0IHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCA9IGZhbHNlO1xuXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRGVidWdXZWJHTEVycm9yQ2hlY2tpbmcoZW5hYmxlZDogYm9vbGVhbikge1xuICB3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQgPSBlbmFibGVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tXZWJHTEVycm9yKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgaWYgKHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCkge1xuICAgIGNvbnN0IGVycm9yID0gZ2wuZ2V0RXJyb3IoKTtcbiAgICBpZiAoZXJyb3IgIT09IGdsLk5PX0VSUk9SKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlYkdMIEVycm9yOiAnICsgZ2V0V2ViR0xFcnJvck1lc3NhZ2UoZ2wsIGVycm9yKSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRXZWJHTEVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5OT19FUlJPUjpcbiAgICAgIHJldHVybiAnTk9fRVJST1InO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9FTlVNOlxuICAgICAgcmV0dXJuICdJTlZBTElEX0VOVU0nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9WQUxVRTpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9WQUxVRSc7XG4gICAgY2FzZSBnbC5JTlZBTElEX09QRVJBVElPTjpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9PUEVSQVRJT04nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9GUkFNRUJVRkZFUl9PUEVSQVRJT046XG4gICAgICByZXR1cm4gJ0lOVkFMSURfRlJBTUVCVUZGRVJfT1BFUkFUSU9OJztcbiAgICBjYXNlIGdsLk9VVF9PRl9NRU1PUlk6XG4gICAgICByZXR1cm4gJ09VVF9PRl9NRU1PUlknO1xuICAgIGNhc2UgZ2wuQ09OVEVYVF9MT1NUX1dFQkdMOlxuICAgICAgcmV0dXJuICdDT05URVhUX0xPU1RfV0VCR0wnO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gJ1Vua25vd24gZXJyb3IgY29kZSAnICsgc3RhdHVzO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHRlbnNpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGV4dGVuc2lvbk5hbWU6IHN0cmluZyk6IHt9IHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPHt9PihcbiAgICAgIGdsLCAoKSA9PiBnbC5nZXRFeHRlbnNpb24oZXh0ZW5zaW9uTmFtZSksXG4gICAgICAnRXh0ZW5zaW9uIFwiJyArIGV4dGVuc2lvbk5hbWUgKyAnXCIgbm90IHN1cHBvcnRlZCBvbiB0aGlzIGJyb3dzZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhTaGFkZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdmVydGV4U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IHZlcnRleFNoYWRlcjogV2ViR0xTaGFkZXIgPSB0aHJvd0lmTnVsbDxXZWJHTFNoYWRlcj4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlU2hhZGVyKGdsLlZFUlRFWF9TSEFERVIpLFxuICAgICAgJ1VuYWJsZSB0byBjcmVhdGUgdmVydGV4IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZSh2ZXJ0ZXhTaGFkZXIsIHZlcnRleFNoYWRlclNvdXJjZSkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmNvbXBpbGVTaGFkZXIodmVydGV4U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIodmVydGV4U2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0U2hhZGVySW5mb0xvZyh2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBjb21waWxlIHZlcnRleCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIHZlcnRleFNoYWRlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYWdtZW50U2hhZGVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZyYWdtZW50U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyOiBXZWJHTFNoYWRlciA9IHRocm93SWZOdWxsPFdlYkdMU2hhZGVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVTaGFkZXIoZ2wuRlJBR01FTlRfU0hBREVSKSxcbiAgICAgICdVbmFibGUgdG8gY3JlYXRlIGZyYWdtZW50IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZShmcmFnbWVudFNoYWRlciwgZnJhZ21lbnRTaGFkZXJTb3VyY2UpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jb21waWxlU2hhZGVyKGZyYWdtZW50U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIoZnJhZ21lbnRTaGFkZXIsIGdsLkNPTVBJTEVfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRTaGFkZXJJbmZvTG9nKGZyYWdtZW50U2hhZGVyKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gY29tcGlsZSBmcmFnbWVudCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIGZyYWdtZW50U2hhZGVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xQcm9ncmFtIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMUHJvZ3JhbT4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlUHJvZ3JhbSgpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTFByb2dyYW0uJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsaW5rUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5saW5rUHJvZ3JhbShwcm9ncmFtKSk7XG4gIGlmIChnbC5nZXRQcm9ncmFtUGFyYW1ldGVyKHByb2dyYW0sIGdsLkxJTktfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRQcm9ncmFtSW5mb0xvZyhwcm9ncmFtKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gbGluayB2ZXJ0ZXggYW5kIGZyYWdtZW50IHNoYWRlcnMuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlUHJvZ3JhbShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC52YWxpZGF0ZVByb2dyYW0ocHJvZ3JhbSkpO1xuICBpZiAoZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5WQUxJREFURV9TVEFUVVMpID09PSBmYWxzZSkge1xuICAgIGNvbnNvbGUubG9nKGdsLmdldFByb2dyYW1JbmZvTG9nKHByb2dyYW0pKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1NoYWRlciBwcm9ncmFtIHZhbGlkYXRpb24gZmFpbGVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdGF0aWNWZXJ0ZXhCdWZmZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgZGF0YTogRmxvYXQzMkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5idWZmZXJEYXRhKGdsLkFSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVN0YXRpY0luZGV4QnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGRhdGE6IFVpbnQxNkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGJ1ZmZlcikpO1xuICBjYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wuYnVmZmVyRGF0YShnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IG51bWJlciB7XG4gIGlmIChNQVhfVEVYVFVSRV9TSVpFICE9IG51bGwpIHtcbiAgICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbiAgfVxuICBNQVhfVEVYVFVSRV9TSVpFID1cbiAgICAgIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2whLmdldFBhcmFtZXRlcihnbCEuTUFYX1RFWFRVUkVfU0laRSkpO1xuICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldENoYW5uZWxzUGVyVGV4dHVyZSgpOiBudW1iZXIge1xuICBpZiAoaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICByZXR1cm4gMTtcbiAgfVxuICByZXR1cm4gNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVRleHR1cmUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMVGV4dHVyZSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTFRleHR1cmU+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVRleHR1cmUoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xUZXh0dXJlLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVUZXh0dXJlU2l6ZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlU2l6ZTogbnVtYmVyID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGlmICgod2lkdGggPD0gMCkgfHwgKGhlaWdodCA8PSAwKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcignUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICsgJyBpcyBpbnZhbGlkLicpO1xuICB9XG4gIGlmICgod2lkdGggPiBtYXhUZXh0dXJlU2l6ZSkgfHwgKGhlaWdodCA+IG1heFRleHR1cmVTaXplKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIGNvbnN0IG1heCA9ICdbJyArIG1heFRleHR1cmVTaXplICsgJ3gnICsgbWF4VGV4dHVyZVNpemUgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICtcbiAgICAgICAgJyBncmVhdGVyIHRoYW4gV2ViR0wgbWF4aW11bSBvbiB0aGlzIGJyb3dzZXIgLyBHUFUgJyArIG1heCArICcuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYW1lYnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEZyYW1lYnVmZmVyIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMRnJhbWVidWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUZyYW1lYnVmZmVyKCksICdVbmFibGUgdG8gY3JlYXRlIFdlYkdMRnJhbWVidWZmZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYXR0cmlidXRlOiBzdHJpbmcsXG4gICAgYnVmZmVyOiBXZWJHTEJ1ZmZlciwgYXJyYXlFbnRyaWVzUGVySXRlbTogbnVtYmVyLCBpdGVtU3RyaWRlSW5CeXRlczogbnVtYmVyLFxuICAgIGl0ZW1PZmZzZXRJbkJ5dGVzOiBudW1iZXIpIHtcbiAgY29uc3QgbG9jID0gZ2wuZ2V0QXR0cmliTG9jYXRpb24ocHJvZ3JhbSwgYXR0cmlidXRlKTtcbiAgaWYgKGxvYyA9PT0gLTEpIHtcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihcbiAgICAgICAgJ1VuYWJsZSB0byBnZXQgYXR0cmlidXRlIFwiJyArIGF0dHJpYnV0ZSArICdcIiBvbiBXZWJHTFByb2dyYW0uJyk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIChlcnJvciBhcyBhbnkpLm5hbWVkVmVydGV4QXR0cmlidXRlTm90Rm91bmQgPSBhdHRyaWJ1dGU7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudmVydGV4QXR0cmliUG9pbnRlcihcbiAgICAgICAgICBsb2MsIGFycmF5RW50cmllc1Blckl0ZW0sIGdsLkZMT0FULCBmYWxzZSwgaXRlbVN0cmlkZUluQnl0ZXMsXG4gICAgICAgICAgaXRlbU9mZnNldEluQnl0ZXMpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGVWZXJ0ZXhBdHRyaWJBcnJheShsb2MpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRUZXh0dXJlVW5pdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZFRleHR1cmVVbml0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICB1bmlmb3JtTmFtZTogc3RyaW5nKTogV2ViR0xVbmlmb3JtTG9jYXRpb24ge1xuICByZXR1cm4gdGhyb3dJZk51bGw8V2ViR0xVbmlmb3JtTG9jYXRpb24+KFxuICAgICAgZ2wsICgpID0+IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCB1bmlmb3JtTmFtZSksXG4gICAgICAndW5pZm9ybSBcIicgKyB1bmlmb3JtTmFtZSArICdcIiBub3QgcHJlc2VudCBpbiBwcm9ncmFtLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFRleHR1cmVUb1Byb2dyYW1Vbmlmb3JtU2FtcGxlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICB1bmlmb3JtU2FtcGxlck5hbWU6IHN0cmluZywgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGJpbmRUZXh0dXJlVW5pdChnbCwgdGV4dHVyZSwgdGV4dHVyZVVuaXQpKTtcbiAgY29uc3Qgc2FtcGxlckxvY2F0aW9uID1cbiAgICAgIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KGdsLCBwcm9ncmFtLCB1bmlmb3JtU2FtcGxlck5hbWUpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnVuaWZvcm0xaShzYW1wbGVyTG9jYXRpb24sIHRleHR1cmVVbml0KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEZyYW1lYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBudWxsKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmlld3BvcnQoMCwgMCwgZ2wuY2FudmFzLndpZHRoLCBnbC5jYW52YXMuaGVpZ2h0KSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuc2Npc3NvcigwLCAwLCBnbC5jYW52YXMud2lkdGgsIGdsLmNhbnZhcy5oZWlnaHQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIHRleHR1cmUsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIG51bGwsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlRnJhbWVidWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICBjb25zdCBzdGF0dXMgPSBnbC5jaGVja0ZyYW1lYnVmZmVyU3RhdHVzKGdsLkZSQU1FQlVGRkVSKTtcbiAgaWYgKHN0YXR1cyAhPT0gZ2wuRlJBTUVCVUZGRVJfQ09NUExFVEUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdFcnJvciBiaW5kaW5nIGZyYW1lYnVmZmVyOiAnICsgZ2V0RnJhbWVidWZmZXJFcnJvck1lc3NhZ2UoZ2wsIHN0YXR1cykpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFtZWJ1ZmZlckVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfQVRUQUNITUVOVCc7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX01JU1NJTkdfQVRUQUNITUVOVDpcbiAgICAgIHJldHVybiAnRlJBTUVCVUZGRVJfSU5DT01QTEVURV9NSVNTSU5HX0FUVEFDSE1FTlQnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9ESU1FTlNJT05TOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0RJTUVOU0lPTlMnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfVU5TVVBQT1JURUQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICd1bmtub3duIGVycm9yICcgKyBzdGF0dXM7XG4gIH1cbn1cblxuZnVuY3Rpb24gdGhyb3dJZk51bGw8VD4oXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcmV0dXJuVE9yTnVsbDogKCkgPT4gVCB8IG51bGwsXG4gICAgZmFpbHVyZU1lc3NhZ2U6IHN0cmluZyk6IFQge1xuICBjb25zdCB0T3JOdWxsOiBUfG51bGwgPSBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IHJldHVyblRPck51bGwoKSk7XG4gIGlmICh0T3JOdWxsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoZmFpbHVyZU1lc3NhZ2UpO1xuICB9XG4gIHJldHVybiB0T3JOdWxsIGFzIFQ7XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlVW5pdCA9IGdsLk1BWF9DT01CSU5FRF9URVhUVVJFX0lNQUdFX1VOSVRTIC0gMTtcbiAgY29uc3QgZ2xUZXh0dXJlVW5pdCA9IHRleHR1cmVVbml0ICsgZ2wuVEVYVFVSRTA7XG4gIGlmIChnbFRleHR1cmVVbml0IDwgZ2wuVEVYVFVSRTAgfHwgZ2xUZXh0dXJlVW5pdCA+IG1heFRleHR1cmVVbml0KSB7XG4gICAgY29uc3QgdGV4dHVyZVVuaXRSYW5nZSA9ICdbZ2wuVEVYVFVSRTAsIGdsLlRFWFRVUkUnICsgbWF4VGV4dHVyZVVuaXQgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKCd0ZXh0dXJlVW5pdCBtdXN0IGJlIGluICcgKyB0ZXh0dXJlVW5pdFJhbmdlICsgJy4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGV4dHVyZVNoYXBlRnJvbUxvZ2ljYWxTaGFwZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBsb2dpY2FsU2hhcGU6IG51bWJlcltdLFxuICAgIHByZWZlcnJlZFRleFNoYXBlPzogW251bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBtYXhUZXhTaXplID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUobG9naWNhbFNoYXBlKTtcbiAgaWYgKHByZWZlcnJlZFRleFNoYXBlICE9IG51bGwpIHtcbiAgICBjb25zdCBzaXplUHJlZmVycmVkID0gdXRpbC5zaXplRnJvbVNoYXBlKHByZWZlcnJlZFRleFNoYXBlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgc2l6ZSA9PT0gc2l6ZVByZWZlcnJlZCxcbiAgICAgICAgYFNpemUgb2Ygc2hhcGUgKCR7c2l6ZX0pIG11c3QgbWF0Y2ggc2l6ZSBvZiBgICtcbiAgICAgICAgICAgIGBwcmVmZXJyZWRTaGFwZSAoJHtzaXplUHJlZmVycmVkfSlgKTtcbiAgICBpZiAocHJlZmVycmVkVGV4U2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgICBwcmVmZXJyZWRUZXhTaGFwZVsxXSA8PSBtYXhUZXhTaXplKSB7XG4gICAgICByZXR1cm4gcHJlZmVycmVkVGV4U2hhcGU7XG4gICAgfVxuICB9XG5cbiAgaWYgKGxvZ2ljYWxTaGFwZS5sZW5ndGggPD0gMSAmJiBzaXplIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gW3NpemUsIDFdO1xuICB9IGVsc2UgaWYgKFxuICAgICAgbG9naWNhbFNoYXBlLmxlbmd0aCA9PT0gMiAmJiBsb2dpY2FsU2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgbG9naWNhbFNoYXBlWzFdIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gbG9naWNhbFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl07XG4gIH0gZWxzZSBpZiAoXG4gICAgICBsb2dpY2FsU2hhcGUubGVuZ3RoID09PSAzICYmIGxvZ2ljYWxTaGFwZVswXSA8PSBtYXhUZXhTaXplICYmXG4gICAgICBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl0gPD0gbWF4VGV4U2l6ZSkge1xuICAgIHJldHVybiBbbG9naWNhbFNoYXBlWzBdLCBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl1dO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB1dGlsLnNpemVUb1NxdWFyaXNoU2hhcGUoc2l6ZSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtBZGROb2RlLCBBcmdNYXhFcXVhbHNOb2RlLCBBcmdNYXhOb2RlLCBDb25jYXQzRE5vZGUsIENvbnZvbHV0aW9uMkROb2RlLCBEaXZpZGVOb2RlLCBFeHBOb2RlLCBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSwgR3JhcGgsIExvZ05vZGUsIE1hdE11bE5vZGUsIE1heFBvb2xOb2RlLCBNZWFuU3F1YXJlZENvc3ROb2RlLCBNdWx0aXBseU5vZGUsIE5vZGUsIFJlZHVjZVN1bU5vZGUsIFJlTFVOb2RlLCBSZXNoYXBlTm9kZSwgU2lnbW9pZE5vZGUsIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSwgU29mdG1heE5vZGUsIFNwbGl0Tm9kZSwgU3F1YXJlTm9kZSwgU3VidHJhY3ROb2RlLCBUYW5ITm9kZSwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7QWRkfSBmcm9tICcuL29wcy9hZGQnO1xuaW1wb3J0IHtBcmdNYXh9IGZyb20gJy4vb3BzL2FyZ21heCc7XG5pbXBvcnQge0FyZ01heEVxdWFsc30gZnJvbSAnLi9vcHMvYXJnbWF4ZXF1YWxzJztcbmltcG9ydCB7Q29uY2F0M0R9IGZyb20gJy4vb3BzL2NvbmNhdDNkJztcbmltcG9ydCB7Q29udm9sdXRpb24yRH0gZnJvbSAnLi9vcHMvY29udm9sdXRpb24nO1xuaW1wb3J0IHtEaXZpZGV9IGZyb20gJy4vb3BzL2RpdmlkZSc7XG5pbXBvcnQge1JlTFUsIFNpZ21vaWQsIFNxdWFyZSwgVGFuSH0gZnJvbSAnLi9vcHMvZWxlbWVudF93aXNlX2FjdGl2YXRpb24nO1xuaW1wb3J0IHtNZWFuU3F1YXJlZENvc3R9IGZyb20gJy4vb3BzL2VsZW1lbnRfd2lzZV9jb3N0JztcbmltcG9ydCB7RXhwfSBmcm9tICcuL29wcy9leHAnO1xuaW1wb3J0IHtMaW5lYXJDb21iaW5hdGlvbn0gZnJvbSAnLi9vcHMvbGluZWFyX2NvbWJpbmF0aW9uJztcbmltcG9ydCB7TG9nfSBmcm9tICcuL29wcy9sb2cnO1xuaW1wb3J0IHtNYXRNdWx9IGZyb20gJy4vb3BzL21hdG11bCc7XG5pbXBvcnQge01heFBvb2x9IGZyb20gJy4vb3BzL21heF9wb29sJztcbmltcG9ydCB7TXVsdGlwbHl9IGZyb20gJy4vb3BzL211bHRpcGx5JztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge1JlZHVjZVN1bX0gZnJvbSAnLi9vcHMvcmVkdWNlX3N1bSc7XG5pbXBvcnQge1Jlc2hhcGV9IGZyb20gJy4vb3BzL3Jlc2hhcGUnO1xuaW1wb3J0IHtTb2Z0bWF4LCBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdH0gZnJvbSAnLi9vcHMvc29mdG1heCc7XG5pbXBvcnQge1NwbGl0fSBmcm9tICcuL29wcy9zcGxpdCc7XG5pbXBvcnQge1N1YnRyYWN0fSBmcm9tICcuL29wcy9zdWJ0cmFjdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBlbWl0RnJvbUdyYXBoTm9kZXMobm9kZXM6IE5vZGVbXSk6IE9wZXJhdGlvbltdIHtcbiAgY29uc3Qgb3BzOiBPcGVyYXRpb25bXSA9IFtdO1xuICBub2Rlcy5mb3JFYWNoKG5vZGUgPT4gQXJyYXkucHJvdG90eXBlLnB1c2guYXBwbHkob3BzLCBlbWl0T3BGcm9tTm9kZShub2RlKSkpO1xuICByZXR1cm4gb3BzO1xufVxuXG5mdW5jdGlvbiBlbWl0T3BGcm9tTm9kZShub2RlOiBOb2RlKTogT3BlcmF0aW9uW10ge1xuICBpZiAobm9kZSBpbnN0YW5jZW9mIFJlc2hhcGVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVzaGFwZShub2RlLmlucHV0c1tSZXNoYXBlTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTWF0TXVsTm9kZSkge1xuICAgIGNvbnN0IHgxID0gbm9kZS5pbnB1dHNbTWF0TXVsTm9kZS5YMV07XG4gICAgY29uc3QgeDIgPSBub2RlLmlucHV0c1tNYXRNdWxOb2RlLlgyXTtcbiAgICByZXR1cm4gW25ldyBNYXRNdWwoeDEsIHgyLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBDb252b2x1dGlvbjJETm9kZSkge1xuICAgIGNvbnN0IHcgPSBub2RlLmlucHV0c1tDb252b2x1dGlvbjJETm9kZS5XXTtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbQ29udm9sdXRpb24yRE5vZGUuWF07XG4gICAgY29uc3QgYiA9IG5vZGUuaW5wdXRzW0NvbnZvbHV0aW9uMkROb2RlLkJdO1xuICAgIHJldHVybiBbbmV3IENvbnZvbHV0aW9uMkQoXG4gICAgICAgIHcsIHgsIGIsIG5vZGUub3V0cHV0LCBub2RlLmZpZWxkU2l6ZSwgbm9kZS5vdXRwdXREZXB0aCwgbm9kZS5zdHJpZGUsXG4gICAgICAgIG5vZGUuemVyb1BhZCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNYXhQb29sTm9kZSkge1xuICAgIGNvbnN0IHggPSBub2RlLmlucHV0c1tNYXhQb29sTm9kZS5YXTtcbiAgICByZXR1cm4gW25ldyBNYXhQb29sKFxuICAgICAgICB4LCBub2RlLm91dHB1dCwgbm9kZS5maWVsZFNpemUsIG5vZGUuc3RyaWRlLCBub2RlLnplcm9QYWQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRXhwTm9kZSkge1xuICAgIHJldHVybiBbbmV3IEV4cChub2RlLmlucHV0c1tFeHBOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBMb2dOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgTG9nKG5vZGUuaW5wdXRzW0xvZ05vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFJlTFVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVMVShub2RlLmlucHV0c1tSZUxVTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgVGFuSE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBUYW5IKG5vZGUuaW5wdXRzW1RhbkhOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTaWdtb2lkTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNpZ21vaWQobm9kZS5pbnB1dHNbU2lnbW9pZE5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSkge1xuICAgIGNvbnN0IHggPSBub2RlLmlucHV0c1tTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUuWF07XG4gICAgY29uc3QgdGFyZ2V0ID0gbm9kZS5pbnB1dHNbU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlLlRBUkdFVF07XG4gICAgcmV0dXJuIFtuZXcgU29mdG1heENyb3NzRW50cm9weUNvc3QoeCwgdGFyZ2V0LCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTb2Z0bWF4Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNvZnRtYXgobm9kZS5pbnB1dHNbU29mdG1heE5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE1lYW5TcXVhcmVkQ29zdE5vZGUpIHtcbiAgICBjb25zdCBsYWJlbCA9IG5vZGUuaW5wdXRzW01lYW5TcXVhcmVkQ29zdE5vZGUuTEFCRUxdO1xuICAgIGNvbnN0IHByZWRpY3Rpb24gPSBub2RlLmlucHV0c1tNZWFuU3F1YXJlZENvc3ROb2RlLlBSRURJQ1RJT05dO1xuICAgIHJldHVybiBbbmV3IE1lYW5TcXVhcmVkQ29zdChsYWJlbCwgcHJlZGljdGlvbiwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQXJnTWF4RXF1YWxzTm9kZSkge1xuICAgIHJldHVybiBbbmV3IEFyZ01heEVxdWFscyhcbiAgICAgICAgbm9kZS5pbnB1dHNbQXJnTWF4RXF1YWxzTm9kZS5YMV0sIG5vZGUuaW5wdXRzW0FyZ01heEVxdWFsc05vZGUuWDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBBcmdNYXhOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQXJnTWF4KG5vZGUueCwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBMaW5lYXJDb21iaW5hdGlvbihcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuVDFdLFxuICAgICAgICBub2RlLmlucHV0c1tGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZS5UMl0sXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLkMxXSxcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuQzJdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBDb25jYXQzRE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBDb25jYXQzRChcbiAgICAgICAgbm9kZS5pbnB1dHNbQ29uY2F0M0ROb2RlLlgxXSwgbm9kZS5pbnB1dHNbQ29uY2F0M0ROb2RlLlgyXSwgbm9kZS5heGlzLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTcXVhcmVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgU3F1YXJlKG5vZGUuaW5wdXRzW1NxdWFyZU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEFkZE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBBZGQoXG4gICAgICAgIG5vZGUuaW5wdXRzW0FkZE5vZGUuVDFdLCBub2RlLmlucHV0c1tBZGROb2RlLlQyXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU3VidHJhY3ROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgU3VidHJhY3QoXG4gICAgICAgIG5vZGUuaW5wdXRzW1N1YnRyYWN0Tm9kZS5UMV0sIG5vZGUuaW5wdXRzW1N1YnRyYWN0Tm9kZS5UMl0sXG4gICAgICAgIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE11bHRpcGx5Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IE11bHRpcGx5KFxuICAgICAgICBub2RlLmlucHV0c1tNdWx0aXBseU5vZGUuVDFdLCBub2RlLmlucHV0c1tNdWx0aXBseU5vZGUuVDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBEaXZpZGVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgRGl2aWRlKFxuICAgICAgICBub2RlLmlucHV0c1tEaXZpZGVOb2RlLlQxXSwgbm9kZS5pbnB1dHNbRGl2aWRlTm9kZS5UMl0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFNwbGl0Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNwbGl0KG5vZGUuaW5wdXRzW1NwbGl0Tm9kZS5YXSwgbm9kZS5vdXRwdXRzKV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFJlZHVjZVN1bU5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBSZWR1Y2VTdW0obm9kZS5pbnB1dHNbUmVkdWNlU3VtTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChncmFwaF91dGlsLmlzSW5wdXROb2RlKG5vZGUpKSB7XG4gICAgcmV0dXJuIFtdO1xuICB9IGVsc2Uge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICB0aHJvdyBFcnJvcignVW5zdXBwb3J0ZWQgbm9kZSB0eXBlOiAnICsgKG5vZGUuY29uc3RydWN0b3IgYXMgYW55KS5uYW1lKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFkZCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgZHlTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgLyoqIEVsZW1lbnQtd2lzZSBhZGQgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpcyBzY2FsYXIuICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoeDEsIHgyKTtcbiAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhclBsdXNBcnJheSh4MiwgeDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5hZGQoeDEsIHgyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy54MVRlbnNvci5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShkeSk7XG4gICAgICAgICAgaWYgKHRoaXMuZHlTaXplU2NhbGFyID09IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuZHlTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhkeS5zaXplKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZGl2aWRlKHN1bSwgdGhpcy5keVNpemVTY2FsYXIpKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGR5KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDJUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MlRlbnNvciwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBkeSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuZHlTaXplU2NhbGFyICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZHlTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFyZ01heCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBbiBBcmdNYXggb3BlcmF0aW9uLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmFyZ01heCh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRocm93IG5ldyBFcnJvcignQXJnTWF4IGJhY2twcm9wIHVuaW1wbGVtZW50ZWQnKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4RXF1YWxzIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEFuIEFyZ01heEVxdWFscyBvcGVyYXRpb24uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKG1hdGguYXJnTWF4RXF1YWxzKHgxLCB4MikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FyZ01heEVxdWFscyBiYWNrcHJvcCB1bmltcGxlbWVudGVkJyk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi4vbWF0aC9jb25jYXQzZF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5M0QsIE5EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25jYXQzRCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBIENvbmNhdCAzRCBvcGVyYXRpb24uXG4gICAqXG4gICAqIENvbmNhdHMgdHdvIDNEIHRlbnNvcnMgYWxvbmcgYW4gYXhpcy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgYXhpczogbnVtYmVyLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIGNvbmNhdDNkX3V0aWwuYXNzZXJ0Q29uY2F0M0RTaGFwZXNNYXRjaChcbiAgICAgICAgeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlLCBheGlzKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBjb25jYXRSZXN1bHQgPSBtYXRoLmNvbmNhdDNEKHgxLCB4MiwgdGhpcy5heGlzKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKGNvbmNhdFJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRocm93IG5ldyBFcnJvcignQ29uY2F0M0QgYmFja3Byb3Agbm90IGltcGxlbWVudGVkLicpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbiwgTkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbnZvbHV0aW9uMkQgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIHplcm9QYWQ6IG51bWJlcjtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIGNvbnZvbHV0aW9uIG9wIHdpdGggdGhlIHNwZWNpZmllZCBwcm9wZXJ0aWVzLlxuICAgKlxuICAgKiBAcGFyYW0gaW5wdXRTaGFwZSBUaGUgc2hhcGUgb2YgdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSBmaWVsZFNpemUgVGhlIHNpemUgb2YgdGhlIGZpbHRlciAocm93cy9jb2xzIG9mIHNsaWRpbmcgd2luZG93KS5cbiAgICogQHBhcmFtIG91dHB1dERlcHRoIFRoZSBkZXB0aCBvZiB0aGUgb3V0cHV0IChOdW1iZXIgb2YgZmlsdGVycykuXG4gICAqIEBwYXJhbSBzdHJpZGUgSG93IG1hbnkgcGl4ZWxzIHRvIHNoaWZ0IHRoZSBmaWx0ZXIgYnkgd2hlbiBzbGlkaW5nLlxuICAgKiAgICAgRGVmYXVsdHMgdG8gMS5cbiAgICogQHBhcmFtIHplcm9QYWQgSG93IG1hbnkgcGl4ZWxzIHRvIHBhZCB0aGUgaW5wdXQgZnJvbSBlYWNoIHNpZGUuIERlZmF1bHRzIHRvXG4gICAqICAgICBhIHZhbHVlIHNvIHRoYXQgdGhlIHJvd3MgYW5kIGNvbHVtbnMgb2YgdGhlIG91dHB1dCBuZGFycmF5IGlzXG4gICAqICAgICB0aGUgc2FtZSBhcyB0aGUgaW5wdXQgbmRhcnJheS5cbiAgICogQHBhcmFtIHdlaWdodHMgT3B0aW9uYWwuIFRoZSB3ZWlnaHRzIG9mIHRoZSBmaWx0ZXJzLlxuICAgKiBAcGFyYW0gYmlhc2VzIE9wdGlvbmFsLiBUaGUgYmlhcyB0ZXJtcyBvZiB0aGUgZmlsdGVycy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB3VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIGJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yLCBwcml2YXRlIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgICAgcHJpdmF0ZSBvdXRwdXREZXB0aDogbnVtYmVyLCBwcml2YXRlIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuYXNzZXJ0V2VpZ2h0c1NoYXBlKHdUZW5zb3Iuc2hhcGUpO1xuICAgIHRoaXMuemVyb1BhZCA9IHplcm9QYWQgIT0gbnVsbCA/XG4gICAgICAgIHplcm9QYWQgOlxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgICAgICAgICB0aGlzLnhUZW5zb3Iuc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB0aGlzLmZpZWxkU2l6ZSxcbiAgICAgICAgICAgIHRoaXMuc3RyaWRlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc0ludCh0aGlzLnplcm9QYWQpLFxuICAgICAgICBgVGhlIHplcm8gcGFkZGluZyAoJHt0aGlzLnplcm9QYWR9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSB0aGUgYCArXG4gICAgICAgICAgICBgc3RyaWRlIGFuZC9vciB6ZXJvIHBhZCBwYXJhbWV0ZXJzYCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHdlaWdodHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMud1RlbnNvcikgYXMgQXJyYXk0RDtcbiAgICBjb25zdCBiaWFzZXMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYlRlbnNvcikgYXMgQXJyYXkxRDtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICBrZWVwKG1hdGguY29udjJkKHgsIHdlaWdodHMsIGJpYXNlcywgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB3ZWlnaHRzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLndUZW5zb3IpIGFzIEFycmF5NEQ7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgQXJyYXkzRDtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IHtkdywgZGIsIGR4fSA9XG4gICAgICAgICAgbWF0aC5jb252MmRCYWNrUHJvcCh4LCBkeSwgd2VpZ2h0cywgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy53VGVuc29yLCBrZWVwKGR3KSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5iVGVuc29yLCBrZWVwKGRiKSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54VGVuc29yLCBrZWVwKGR4KSk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzc2VydFdlaWdodHNTaGFwZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHNTaGFwZVswXSA9PT0gdGhpcy5maWVsZFNpemUgJiZcbiAgICAgICAgICAgIHdlaWdodHNTaGFwZVsxXSA9PT0gdGhpcy5maWVsZFNpemUgJiZcbiAgICAgICAgICAgIHdlaWdodHNTaGFwZVsyXSA9PT0gdGhpcy54VGVuc29yLnNoYXBlWzJdICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbM10gPT09IHRoaXMub3V0cHV0RGVwdGgsXG4gICAgICAgIGB3ZWlnaHRzIG11c3QgYmUgb2Ygc2hhcGUgWyR7dGhpcy5maWVsZFNpemV9LCR7dGhpcy5maWVsZFNpemV9LGAgK1xuICAgICAgICAgICAgYCR7dGhpcy54VGVuc29yLnNoYXBlWzJdfSwke3RoaXMub3V0cHV0RGVwdGh9XSBidXQgdGhleSBhcmUgb2ZgICtcbiAgICAgICAgICAgIGBzaGFwZSBbJHt3ZWlnaHRzU2hhcGV9XWApO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRGl2aWRlIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBvbmVzOiBOREFycmF5O1xuXG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2UgZGl2aWRlIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHgxVGVuc29yLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHgyVGVuc29yLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh4MVRlbnNvci5zaGFwZSwgeDJUZW5zb3Iuc2hhcGUpLFxuICAgICAgICAnT25lIG9mIHQxIG9yIHQyIG11c3QgYmUgYSBzY2FsYXIsIG9yIHQxIGFuZCB0MiBtdXN0IGhhdmUgJyArXG4gICAgICAgICAgICAndGhlIHNhbWUgc2hhcGUnKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCByZXN1bHQ6IE5EQXJyYXk7XG4gICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQxLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhckRpdmlkZWRCeUFycmF5KHQxLCB0Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5hcnJheURpdmlkZWRCeVNjYWxhcih0MSwgdDIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5kaXZpZGUodDEsIHQyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIGNvbnN0IHgxSXNTY2FsYXIgPSB1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpO1xuICAgIGNvbnN0IHgySXNTY2FsYXIgPSB1dGlsLmlzU2NhbGFyU2hhcGUoeDIuc2hhcGUpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgaWYgKHgxSXNTY2FsYXIpIHtcbiAgICAgICAgICBjb25zdCBkaXYgPSBtYXRoLmRpdmlkZShkeSwgeDIpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zdW0oZGl2KSkpO1xuXG4gICAgICAgICAgZGl2LmRpc3Bvc2UoKTtcbiAgICAgICAgfSBlbHNlIGlmICh4MklzU2NhbGFyKSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIoZHksIHgyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZGl2aWRlKGR5LCB4MikpKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICAvLyBkeDIgPSAtMSAqIHgxICogeDIgXiAtMi5cbiAgICAgICAgY29uc3QgeDJTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh4MiwgeDIpO1xuXG4gICAgICAgIGxldCB4MU92ZXJYMlNxdWFyZWQ6IE5EQXJyYXk7XG4gICAgICAgIGlmICh4MklzU2NhbGFyKSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5hcnJheURpdmlkZWRCeVNjYWxhcih4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfSBlbHNlIGlmICh4MUlzU2NhbGFyKSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5zY2FsYXJEaXZpZGVkQnlBcnJheSh4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB4MU92ZXJYMlNxdWFyZWQgPSBtYXRoLmRpdmlkZSh4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGR4MiA9IG1hdGgubmVnKHgxT3ZlclgyU3F1YXJlZCk7XG4gICAgICAgIGNvbnN0IGR5VGltZXNEZXJpdmF0aXZlID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgZHgyKTtcblxuICAgICAgICBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc3VtKGR5VGltZXNEZXJpdmF0aXZlKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKGR5VGltZXNEZXJpdmF0aXZlKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtBY3RpdmF0aW9uRnVuY3Rpb24sIFJlTFVGdW5jLCBTaWdtb2lkRnVuYywgU3F1YXJlRnVuYywgVGFuSEZ1bmN9IGZyb20gJy4uL21hdGgvYWN0aXZhdGlvbl9mdW5jdGlvbnMnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRWxlbWVudFdpc2VBY3RpdmF0aW9uIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcm90ZWN0ZWQgeFRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgeVRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBmdW5jOiBBY3RpdmF0aW9uRnVuY3Rpb24pIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAodGhpcy5mdW5jLm91dHB1dChtYXRoLCB4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIC8vIGRFL2R4X2kgPSBzdW1faiBkRS9keV9qICogZHlfai9keF9pXG4gICAgLy8gICAgICAgICA9IGRFL2R5X2kgKiBkeV9pL2R4X2lcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuICAgIGNvbnN0IHkgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGR5ZHggPSB0aGlzLmZ1bmMuZGVyKG1hdGgsIHgsIHkpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChtYXRoLmVsZW1lbnRXaXNlTXVsKGR5LCBkeWR4KSkpO1xuICAgICAgZHlkeC5kaXNwb3NlKCk7XG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZUxVIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgUmVMVUZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBUYW5IIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgVGFuSEZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaWdtb2lkIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgU2lnbW9pZEZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcXVhcmUgZXh0ZW5kcyBFbGVtZW50V2lzZUFjdGl2YXRpb24ge1xuICBjb25zdHJ1Y3Rvcih4VGVuc29yOiBUZW5zb3IsIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKHhUZW5zb3IsIHlUZW5zb3IsIG5ldyBTcXVhcmVGdW5jKCkpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtFbGVtZW50V2lzZUNvc3RGdW5jdGlvbiwgU3F1YXJlQ29zdEZ1bmN9IGZyb20gJy4uL21hdGgvY29zdF9mdW5jdGlvbnMnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRWxlbWVudFdpc2VDb3N0PFQgZXh0ZW5kcyBOREFycmF5PiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgb25lT3Zlck5TY2FsYXI6IFNjYWxhcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByb3RlY3RlZCB4MVRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByb3RlY3RlZCB5VGVuc29yOiBUZW5zb3IsIHByb3RlY3RlZCBmdW5jOiBFbGVtZW50V2lzZUNvc3RGdW5jdGlvbikge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5vbmVPdmVyTlNjYWxhciA9IFNjYWxhci5uZXcoMSAvIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgZWxlbWVudFdpc2VDb3N0ID0gdGhpcy5mdW5jLmNvc3QobWF0aCwgeDEsIHgyKTtcbiAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGVsZW1lbnRXaXNlQ29zdCk7XG4gICAgICBjb25zdCByZXN1bHQgPSBtYXRoLnNjYWxhclRpbWVzQXJyYXkodGhpcy5vbmVPdmVyTlNjYWxhciwgc3VtKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcCh0aGlzLmZ1bmMuZGVyKG1hdGgsIHgxLCB4MikpKTtcbiAgICAgIH1cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKHRoaXMuZnVuYy5kZXIobWF0aCwgeDIsIHgxKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmZ1bmMuZGlzcG9zZSgpO1xuICAgIHRoaXMub25lT3Zlck5TY2FsYXIuZGlzcG9zZSgpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWVhblNxdWFyZWRDb3N0IGV4dGVuZHMgRWxlbWVudFdpc2VDb3N0PEFycmF5MUQ+IHtcbiAgY29uc3RydWN0b3IoeDFUZW5zb3I6IFRlbnNvciwgeDJUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeDFUZW5zb3IsIHgyVGVuc29yLCB5VGVuc29yLCBuZXcgU3F1YXJlQ29zdEZ1bmMoKSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBFeHAgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogRXhwb25lbnRhdGlvbiBvcGVyYXRpb24gLSBlXnguXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5leHAoeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnhUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bCh5LCBkeSkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMaW5lYXJDb21iaW5hdGlvbiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBIDItdGVuc29yIGxpbmVhciBjb21iaW5hdGlvbiBvcGVyYXRpb24uXG4gICAqXG4gICAqIENvbWJpbmVzIHRlbnNvcnMgeDEgYW5kIHgyIChvZiB0aGUgc2FtZSBzaGFwZSkgd2l0aCB3ZWlnaHRzIGMxICYgYzI7XG4gICAqIENvbXB1dGVzIGMxKngxICsgYzIqeDIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBjMVRlbnNvcjogVGVuc29yLCBwcml2YXRlIGMyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIG91dFRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBjMSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMVRlbnNvcikuYXNTY2FsYXIoKTtcbiAgICBjb25zdCBjMiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMlRlbnNvcikuYXNTY2FsYXIoKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy5vdXRUZW5zb3IsIGtlZXAobWF0aC5zY2FsZWRBcnJheUFkZChjMSwgeDEsIGMyLCB4MikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGMxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmMxVGVuc29yKTtcbiAgICBjb25zdCBjMiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGMxLCBkeSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGMyLCBkeSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy5jMVRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZG90UHJvZHVjdDEgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHgxLCBkeSk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmMxVGVuc29yLCBrZWVwKG1hdGguc3VtKGRvdFByb2R1Y3QxKSkpO1xuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLmMyVGVuc29yKSkge1xuICAgICAgICBjb25zdCBkb3RQcm9kdWN0MiA9IG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIGR5KTtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuYzJUZW5zb3IsIGtlZXAobWF0aC5zdW0oZG90UHJvZHVjdDIpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2cgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogTmF0dXJhbCBsb2cgb3BlcmF0aW9uIC0gbG4oeClcbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmxvZyh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueFRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChtYXRoLmRpdmlkZShkeSwgeCkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF0TXVsIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDIgJiYgeDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5tYXRNdWwoeDEgYXMgQXJyYXkyRCwgeDIgYXMgQXJyYXkyRCkpKTtcbiAgICAgIH0gZWxzZSBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHgyLnNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgICAga2VlcChtYXRoLm1hdHJpeFRpbWVzVmVjdG9yKHgxIGFzIEFycmF5MkQsIHgyIGFzIEFycmF5MUQpKSk7XG4gICAgICB9IGVsc2UgaWYgKHgxLnNoYXBlLmxlbmd0aCA9PT0gMSAmJiB4Mi5zaGFwZS5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICAgIGtlZXAobWF0aC52ZWN0b3JUaW1lc01hdHJpeCh4MSBhcyBBcnJheTFELCB4MiBhcyBBcnJheTJEKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGxldCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgbGV0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBsZXQgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgIHgxID0geDEucmVzaGFwZShbMSwgeDEuc2l6ZV0pO1xuICAgICAgZHkgPSBkeS5yZXNoYXBlKFsxLCBkeS5zaXplXSk7XG4gICAgfVxuICAgIGlmICh4Mi5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgIHgyID0geDIucmVzaGFwZShbeDIuc2l6ZSwgMV0pO1xuICAgICAgZHkgPSBkeS5yZXNoYXBlKFtkeS5zaXplLCAxXSk7XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgLy8geSA9IHgxICogeDJcbiAgICAgIC8vIGR4MSA9IGR5ICogeDJUXG4gICAgICAvLyBkeDIgPSB4MVQgKiBkeVxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZHgxID0gbWF0aC5tYXRNdWwoXG4gICAgICAgICAgICBkeSBhcyBBcnJheTJELCB4MiBhcyBBcnJheTJELCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgICAgICAgTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRCk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueDFUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKHRoaXMueDFUZW5zb3Iuc2hhcGUubGVuZ3RoID09PSAxID8gZHgxLmFzMUQoKSA6IGR4MSkpO1xuICAgICAgfVxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZHgyID0gbWF0aC5tYXRNdWwoXG4gICAgICAgICAgICB4MSBhcyBBcnJheTJELCBkeSBhcyBBcnJheTJELCBNYXRyaXhPcmllbnRhdGlvbi5UUkFOU1BPU0VELFxuICAgICAgICAgICAgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueDJUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKHRoaXMueDJUZW5zb3Iuc2hhcGUubGVuZ3RoID09PSAxID8gZHgyLmFzMUQoKSA6IGR4MikpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkyRCwgQXJyYXkzRCwgTkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1heFBvb2wgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIHBhZDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBmaWVsZFNpemU6IG51bWJlciwgcHJpdmF0ZSBzdHJpZGUgPSAxLCBwYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcigpO1xuXG4gICAgaWYgKHBhZCAhPSBudWxsKSB7XG4gICAgICB0aGlzLnBhZCA9IHBhZDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5wYWQgPSBjb252X3V0aWwuY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgICAgICAgeFRlbnNvci5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHRoaXMuZmllbGRTaXplLFxuICAgICAgICAgIHRoaXMuc3RyaWRlKTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc0ludCh0aGlzLnBhZCksXG4gICAgICAgIGBUaGUgemVybyBwYWRkaW5nICgke3RoaXMucGFkfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLm1heFBvb2woeCwgdGhpcy5maWVsZFNpemUsIHRoaXMuc3RyaWRlLCB0aGlzLnBhZCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueFRlbnNvcixcbiAgICAgICAgICBrZWVwKG1hdGgubWF4UG9vbEJhY2twcm9wKFxuICAgICAgICAgICAgICBkeSwgeCwgdGhpcy5maWVsZFNpemUsIHRoaXMuc3RyaWRlLCB0aGlzLnBhZCkpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE11bHRpcGx5IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEVsZW1lbnQtd2lzZSBtdWx0aXBseSBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHQxLCB0Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHQyLCB0MSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDFUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3QgbXVsID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgeDIpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zdW0obXVsKSkpO1xuXG4gICAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKSkge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLnNjYWxhclRpbWVzQXJyYXkoeDIsIGR5KSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy54MlRlbnNvci5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBtdWwgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKGR5LCB4MSk7XG5cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChtYXRoLnN1bShtdWwpKSk7XG5cbiAgICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpKSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheSh4MSwgZHkpKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bCh4MSwgZHkpKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBPcGVyYXRpb24ge1xuICBhYnN0cmFjdCBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCk6XG4gICAgICB2b2lkO1xuXG4gIGFic3RyYWN0IGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGRpc3Bvc2VUcmFuc2llbnRBcnJheXMoXG4gICAgICBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHt9XG5cbiAgZGlzcG9zZSgpIHt9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlZHVjZVN1bSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKiBFbGVtZW50LXdpc2UgYWRkIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLiAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHg6IFRlbnNvciwgcHJpdmF0ZSBvdXRUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChvdXRUZW5zb3Iuc2hhcGUsIFtdKTtcbiAgfVxuXG4gIHByaXZhdGUgb25lczogTkRBcnJheTtcblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueCk7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0VGVuc29yLCBrZWVwKG1hdGguc3VtKHgpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgaWYgKCFncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dFRlbnNvcik7XG4gICAgICBpZiAodGhpcy5vbmVzID09IG51bGwpIHtcbiAgICAgICAgY29uc3QgeEFycmF5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngpO1xuICAgICAgICB0aGlzLm9uZXMgPSBOREFycmF5Lnplcm9zTGlrZSh4QXJyYXkpO1xuICAgICAgICB0aGlzLm9uZXMuZmlsbCgxKTtcbiAgICAgIH1cbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGR5LCB0aGlzLm9uZXMpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuZXhwb3J0IGNsYXNzIFJlc2hhcGU8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICBjb25zdCB4U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh4VGVuc29yLnNoYXBlKTtcbiAgICBjb25zdCB5U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh5VGVuc29yLnNoYXBlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeFNpemUgPT09IHlTaXplLFxuICAgICAgICBgVGhlIGlucHV0IHNpemUgKCR7eFNpemV9KSBhbmQgb3V0cHV0IHNpemUgKCR7eVNpemV9KSBtdXN0IG1hdGNoYCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcikgYXMgVDE7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvciwga2VlcChtYXRoLnJlc2hhcGU8VDEsIFQyPih4LCB0aGlzLnlUZW5zb3Iuc2hhcGUpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKSBhcyBUMjtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5yZXNoYXBlPFQyLCBUMT4oZHksIHRoaXMueFRlbnNvci5zaGFwZSkpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBsb2dpdHNUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBvdXRwdXQ6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGxvZ2l0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5sb2dpdHNUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5vdXRwdXQsIGtlZXAobWF0aC5zb2Z0bWF4KGxvZ2l0cykpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKCkge1xuICAgIHRocm93IEVycm9yKCdTb2Z0bWF4IGJhY2twcm9wIGlzIG5vdCB5ZXQgaW1wbGVtZW50ZWQnKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU29mdG1heENyb3NzRW50cm9weUNvc3QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgbG9naXRzVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgbGFiZWxUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLnNvZnRtYXhUZW5zb3IgPSBuZXcgVGVuc29yKGxvZ2l0c1RlbnNvci5zaGFwZSk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGxvZ2l0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5sb2dpdHNUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgY29uc3QgbGFiZWwgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubGFiZWxUZW5zb3IpIGFzIEFycmF5MUQ7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBzb2Z0bWF4UmVzdWx0ID0gbWF0aC5zb2Z0bWF4KGxvZ2l0cyk7XG5cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5zb2Z0bWF4VGVuc29yLCBrZWVwKHNvZnRtYXhSZXN1bHQpKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgIGtlZXAoY3Jvc3NFbnRyb3B5Q29zdChtYXRoLCBzb2Z0bWF4UmVzdWx0LCBsYWJlbCwgdGhpcy5lcHNpbG9uKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHNvZnRtYXggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuc29mdG1heFRlbnNvcik7XG4gICAgY29uc3QgbGFiZWwgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubGFiZWxUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMubG9naXRzVGVuc29yLCBrZWVwKG1hdGguc3ViKHNvZnRtYXgsIGxhYmVsKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZVRyYW5zaWVudEFycmF5cyhcbiAgICAgIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGluZmVyZW5jZUFycmF5cy5kaXNwb3NlQXJyYXkodGhpcy5zb2Z0bWF4VGVuc29yKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5lcHNpbG9uLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIHByaXZhdGUgc29mdG1heFRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIGVwc2lsb24gPSBTY2FsYXIubmV3KDFlLTUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3Jvc3NFbnRyb3B5Q29zdChcbiAgICBtYXRoOiBOREFycmF5TWF0aCwgeTogQXJyYXkxRCwgdGFyZ2V0OiBBcnJheTFELCBlcHNpbG9uOiBTY2FsYXIpOiBTY2FsYXIge1xuICB1dGlsLmFzc2VydChcbiAgICAgIHkuc2l6ZSA9PT0gdGFyZ2V0LnNpemUsICdUaGUgb3V0cHV0IGFuZCB0YXJnZXQgbXVzdCBiZSB0aGUgc2FtZSBzaXplJyk7XG5cbiAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgIGNvbnN0IHlQbHVzRXBzID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoZXBzaWxvbiwgeSk7XG4gICAgY29uc3QgbG9nT3V0cHV0ID0gbWF0aC5sb2coeVBsdXNFcHMpO1xuICAgIGNvbnN0IHRhckxvZ091dHB1dCA9IG1hdGguZWxlbWVudFdpc2VNdWwodGFyZ2V0LCBsb2dPdXRwdXQpO1xuICAgIGNvbnN0IGNvc3RWZWN0b3IgPSBtYXRoLm5lZyh0YXJMb2dPdXRwdXQpO1xuICAgIHJldHVybiBtYXRoLnN1bShjb3N0VmVjdG9yKTtcbiAgfSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogU3BsaXQgb3BzIGFyZSB1c2VkIHRvIGFjY3VtdWxhdGUgYmFja3Byb3AgZGVyaXZhdGl2ZXMgd2hlbiBhIG5vZGUncyBvdXRwdXRcbiAqIHRlbnNvciBpcyBjb25zdW1lZCBieSBtdWx0aXBsZSBub2Rlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFNwbGl0IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBpbnB1dDogVGVuc29yLCBwcml2YXRlIG91dHB1dHM6IFRlbnNvcltdKSB7XG4gICAgc3VwZXIoKTtcbiAgICBvdXRwdXRzLmZvckVhY2gob3V0cHV0ID0+IHtcbiAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goaW5wdXQuc2hhcGUsIG91dHB1dC5zaGFwZSk7XG4gICAgfSk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGlucHV0QXJyYXkgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuaW5wdXQpO1xuICAgIHRoaXMub3V0cHV0cy5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KG91dHB1dCwgaW5wdXRBcnJheSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgaWYgKCFncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMuaW5wdXQpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IGR4ID0gbWF0aC5hZGQoXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0cHV0c1swXSksXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0cHV0c1sxXSkpO1xuICAgICAgLy8gU3VtIGFjcm9zcyBhbGwgdGhlIGRlcml2YXRpdmVzIG9mIHRoZSBjb25zdW1lcnMgb2YgdGhpcyBub2RlLlxuICAgICAgdGhpcy5vdXRwdXRzLnNsaWNlKDIpLmZvckVhY2gob3V0cHV0ID0+IHtcbiAgICAgICAgZHggPSBtYXRoLmFkZChkeCwgZ3JhZGllbnRBcnJheXMuZ2V0KG91dHB1dCkpO1xuICAgICAgfSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5pbnB1dCwga2VlcChkeCkpO1xuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBTdWJ0cmFjdCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgZHlTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIEVsZW1lbnQtd2lzZSBzdWJ0cmFjdCBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IsIHByaXZhdGUgb3V0VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHQxLnNoYXBlLCB0Mi5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB0MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy50MSk7XG4gICAgY29uc3QgdDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDIpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyTWludXNBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYXJyYXlNaW51c1NjYWxhcih0MSwgdDIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zdWIodDEsIHQyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5vdXRUZW5zb3IsIGtlZXAocmVzdWx0KSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDEpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQyKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dFRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnQxKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMudDEuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy50MSwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnQxLCBrZWVwKGR5KSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy50MikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLnQyLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBjb25zdCBuZWdTdW0gPSBtYXRoLm5lZyhzdW0pO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy50Miwga2VlcChtYXRoLmRpdmlkZShuZWdTdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnQyLCBrZWVwKG1hdGgubmVnKGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmR5U2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7Tm9kZSwgVGVuc29yLCBWYXJpYWJsZU5vZGV9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtTZXNzaW9uUnVudGltZX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBPcHRpbWl6ZXIge1xuICBwcm90ZWN0ZWQgdmFyaWFibGVOb2RlczogVmFyaWFibGVOb2RlW107XG4gIHByb3RlY3RlZCBzcGVjaWZpZWRWYXJpYWJsZU5vZGVzOiBWYXJpYWJsZU5vZGVbXXxudWxsO1xuXG4gIGNvbnN0cnVjdG9yKHNwZWNpZmllZFZhcmlhYmxlTGlzdD86IE5vZGVbXSkge1xuICAgIGlmIChzcGVjaWZpZWRWYXJpYWJsZUxpc3QgIT0gbnVsbCkge1xuICAgICAgdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzID0gc3BlY2lmaWVkVmFyaWFibGVMaXN0IGFzIFZhcmlhYmxlTm9kZVtdO1xuICAgIH1cbiAgfVxuXG4gIGFic3RyYWN0IGJlZm9yZUJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgYWZ0ZXJFeGFtcGxlKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKTogdm9pZDtcblxuICBhYnN0cmFjdCBhZnRlckJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgZGlzcG9zZSgpOiB2b2lkO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vKipcbiAqIERlZmF1bHQgY29tcGFyaXNvbiBmdW5jdGlvbiBmb3IgdGhlIHByaW9yaXR5IHF1ZXVlLlxuICogQHBhcmFtIGEgVGhlIGZpcnN0IGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgZWxlbWVudCB0byBjb21wYXJlLlxuICogQHJldHVybiBcImEgPiBiXCIgcmV0dXJucyA+IDAuIFwiYSA8IGJcIiByZXR1cm5zIDwgMC4gXCJhID09PSBiXCIgcmV0dXJucyAwLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVmYXVsdENvbXBhcmU8VD4oYTogVCwgYjogVCk6IG51bWJlciB7XG4gIGlmIChhID09PSBiKSB7XG4gICAgcmV0dXJuIDA7XG4gIH0gZWxzZSBpZiAoYSA8IGIpIHtcbiAgICByZXR1cm4gLTE7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbn1cblxuLyoqXG4gKiBBIENvbXBhcmF0b3IgaXMgYSB1c2VyLXByb3ZpZGVkIGZ1bmN0aW9uIHRoYXQgY29tcGFyZXMgdHdvIFQgaW5zdGFuY2VzLiBUaGVcbiAqIGNvbnZlbnRpb24gZm9yIGRlZmF1bHRDb21wYXJlIGlzIGV4cGVjdGVkIHRvIGJlIGZvbGxvd2VkIHRvIG1haW50YWluIHRoZVxuICogYmluYXJ5IG1pbi1oZWFwIGludGVncml0eS5cbiAqIEBwYXJhbSBhIFRoZSBmaXJzdCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0gYiBUaGUgc2Vjb25kIGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqL1xuZXhwb3J0IHR5cGUgQ29tcGFyYXRvcjxUPiA9IChhOiBULCBiOiBUKSA9PiBudW1iZXI7XG5cbi8qKlxuICogSW5kZXhPYnNlcnZlciBpcyBhIHVzZXItcHJvdmlkZWQgY2FsbGJhY2sgdGhhdCBpbmZvcm1zIHRoZSBjYWxsZXIgd2hlbiBhblxuICogZWxlbWVudCBpbiB0aGUgcHJpb3JpdHkgcXVldWUncyBiaW5hcnkgbWluLWhlYXAgaGFzIGJlZW4gcmVsb2NhdGVkLlxuICogQHBhcmFtIHQgVGhlIGVsZW1lbnQgdGhhdCB3YXMgcmVsb2NhdGVkLlxuICogQHBhcmFtIG5ld0luZGV4IFRoZSBuZXcgbG9jYXRpb24gaW4gdGhlIGJpbmFyeSBtaW4taGVhcCBvZiB0aGUgZWxlbWVudC5cbiAqL1xuZXhwb3J0IHR5cGUgSW5kZXhPYnNlcnZlcjxUPiA9ICh0OiBULCBuZXdJbmRleDogbnVtYmVyKSA9PiB2b2lkO1xuXG4vKipcbiAqIEEgcHJpb3JpdHkgcXVldWUsIGltcGxlbWVudGVkIGluIHRlcm1zIG9mIGEgYmluYXJ5IG1pbi1oZWFwLiBMb3dlciBwcmlvcml0eVxuICogbnVtYmVycyBhcmUgY29uc2lkZXJlZCBoaWdoZXIgcHJpb3JpdHkuXG4gKiBlbnF1ZXVlLCBkZXF1ZXVlLCBhbmQgdXBkYXRlIGFyZSBhbGwgTyhsb2cgTikgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2ZcbiAqIGVsZW1lbnRzIGluIHRoZSBxdWV1ZS5cbiAqL1xuZXhwb3J0IGNsYXNzIFByaW9yaXR5UXVldWU8VD4ge1xuICBwcml2YXRlIGhlYXA6IFRbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gY29tcGFyYXRvciBBIGZ1bmN0aW9uIHRoYXQgY29tcGFyZXMgdHdvIHF1ZXVlIGVsZW1lbnRzLlxuICAgKiBAcGFyYW0gaW5kZXhPYnNlcnZlciBBbiBvcHRpb25hbCBjYWxsYmFjayByYWlzZWQgd2hlbiB0aGUgcHJpb3JpdHkgcXVldWVcbiAgICogY2hhbmdlcyB0aGUgb3JkZXIgb2YgZWxlbWVudHMgaW4gaXRzIG1pbi1oZWFwLiBVc2VmdWwgZm9yIHRyYWNraW5nIHRoZVxuICAgKiBwb3NpdGlvbnMgb2YgZWxlbWVudHMgdGhhdCBuZWVkIHVwZGF0aW5nLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIGNvbXBhcmF0b3I6IENvbXBhcmF0b3I8VD4sXG4gICAgICBwcml2YXRlIGluZGV4T2JzZXJ2ZXI/OiBJbmRleE9ic2VydmVyPFQ+KSB7fVxuXG4gIC8qKlxuICAgKiBBZGQgYW4gZWxlbWVudCB0byB0aGUgcHJpb3JpdHkgcXVldWUuXG4gICAqIEBwYXJhbSB0IFRoZSBlbGVtZW50IHRvIGVucXVldWUuXG4gICAqL1xuICBlbnF1ZXVlKHQ6IFQpIHtcbiAgICB0aGlzLmhlYXAucHVzaCh0KTtcbiAgICB0aGlzLm9uSW5kZXhDaGFuZ2VkKHQsIHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICB0aGlzLnNpZnRVcCh0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGFuIGVsZW1lbnQgZnJvbSB0aGUgcHJpb3JpdHkgcXVldWUuXG4gICAqIEByZXR1cm4gVGhlIGVsZW1lbnQgaW4gdGhlIHByaW9yaXR5IHF1ZXVlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHlcbiAgICogKGxvd2VzdCBudW1lcmljIHByaW9yaXR5IHZhbHVlKS5cbiAgICovXG4gIGRlcXVldWUoKTogVCB7XG4gICAgaWYgKHRoaXMuZW1wdHkoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdkZXF1ZXVlIGNhbGxlZCBvbiBlbXB0eSBwcmlvcml0eSBxdWV1ZS4nKTtcbiAgICB9XG4gICAgY29uc3QgdCA9IHRoaXMuaGVhcFswXTtcbiAgICB0aGlzLnN3YXAoMCwgdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIHRoaXMuaGVhcC5wb3AoKTtcbiAgICB0aGlzLnNpZnREb3duKDApO1xuICAgIHJldHVybiB0O1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgYW4gZWxlbWVudCBhdCB0aGUgc3BlY2lmaWVkIGluZGV4LiBUaGlzIGNhbiBiZSBhIGZ1bGwgZWxlbWVudFxuICAgKiByZXBsYWNlbWVudCwgb3IgaXQgY2FuIGJlIGFuIGluLXBsYWNlIHVwZGF0ZS4gVGhlIHByaW9yaXR5IGlzIGFzc3VtZWQgdG8gYmVcbiAgICogY2hhbmdlZCwgYW5kIHRoZSBpbnRlcm5hbCBzdG9yYWdlIGlzIHVwZGF0ZWQuIFRoaXMgZnVuY3Rpb24gaXMgb25seSB1c2VmdWxcbiAgICogaWYgdGhlIHN0b3JhZ2UgaW5kZXggb2YgdGhlIHVwZGF0ZWQgZWxlbWVudCBpcyBrbm93bjsgY29uc3RydWN0IHRoZVxuICAgKiBQcmlvcml0eVF1ZXVlIHdpdGggYW4gSW5kZXhPYnNlcnZlciB0byB0cmFjayBlbGVtZW50IGxvY2F0aW9ucy5cbiAgICogQHBhcmFtIG5ld1QgVGhlIG5ldyBlbGVtZW50IHRvIHJlcGxhY2UgaW4gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcGFyYW0gaW5kZXggVGhlIGluZGV4IHRvIGluc2VydCB0aGUgbmV3IGVsZW1lbnQgaW50by5cbiAgICovXG4gIHVwZGF0ZShuZXdUOiBULCBpbmRleDogbnVtYmVyKSB7XG4gICAgLyogSWYgdGhlIGVsZW1lbnQgaXMgYXQgdGhlIHZlcnkgZW5kIG9mIHRoZSBoZWFwLCBubyBzaWZ0aW5nIGlzIG5lY2Vzc2FyeSxcbiAgICAgKiBpdCBjYW4gYmUgc2FmZWx5IHJlbW92ZWQuICovXG4gICAgY29uc3QgbGFzdCA9IChpbmRleCA9PT0gdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIGlmICghbGFzdCkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCB0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gICAgfVxuICAgIHRoaXMuaGVhcC5wb3AoKTtcbiAgICBpZiAoIWxhc3QpIHtcbiAgICAgIC8qIFRoZSBlbGVtZW50IGF0ICdpbmRleCcgaGFzIGJlZW4gcmVtb3ZlZCwgYW5kIHJlcGxhY2VkIHdpdGggd2hhdGV2ZXIgd2FzXG4gICAgICAgKiBhdCB0aGUgZW5kIG9mIHRoZSBoZWFwLiBTaW5jZSB0aGF0IGVsZW1lbnQgbWlnaHQgaGF2ZSBjb21lIGZyb20gYVxuICAgICAgICogZGlmZmVyZW50IHN1YnRyZWUgKGFuZCBub3QgYmUgYSBkaXJlY3QgZGVzY2VuZGFudCBvZiB0aGUgbm9kZSBhdFxuICAgICAgICogJ2luZGV4JyksIHdlIG1pZ2h0IG5lZWQgdG8gc2lmdCB0aGlzIG5ldyB2YWx1ZSB1cCBpbnN0ZWFkIG9mIGRvd24uIFRlc3RcbiAgICAgICAqIGJvdGggZGlyZWN0aW9ucywgYW5kIHNpZnQgdG8gd2hlcmV2ZXIgdGhlIG5vZGUgbmVlZHMgdG8gZ28uXG4gICAgICAgKi9cbiAgICAgIGlmICh0aGlzLnNpZnRVcEluZGV4KGluZGV4KSAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5zaWZ0VXAoaW5kZXgpO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLnNpZnREb3duSW5kZXgoaW5kZXgpICE9PSAtMSkge1xuICAgICAgICB0aGlzLnNpZnREb3duKGluZGV4KTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5lbnF1ZXVlKG5ld1QpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByZWRpY2F0ZSBmb3IgdGVzdGluZyB3aGV0aGVyIHRoZSBQcmlvcml0eVF1ZXVlIGlzIGVtcHR5LlxuICAgKiBAcmV0dXJuIFRydWUgaWYgdGhlIFByaW9yaXR5UXVldWUgaXMgZW1wdHksIG90aGVyd2lzZSBGYWxzZS5cbiAgICovXG4gIGVtcHR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmhlYXAubGVuZ3RoID09PSAwO1xuICB9XG5cbiAgcHJpdmF0ZSBvbkluZGV4Q2hhbmdlZCh0OiBULCBuZXdJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuaW5kZXhPYnNlcnZlcikge1xuICAgICAgdGhpcy5pbmRleE9ic2VydmVyKHQsIG5ld0luZGV4KTtcbiAgICB9XG4gIH1cblxuICAvKlxuICAgKiBTdGFuZGFyZCB6ZXJvLWluZGV4ZWQgYmluYXJ5IGhlYXAgYXJyYXkgbGF5b3V0OlxuICAgKiAgIFBhcmVudChOKSA9IEZsb29yKChOIC0gMSkgLyAyKVxuICAgKiAgIExlZnRDaGlsZChOKSA9IChOICogMikgKyAxXG4gICAqICAgUmlnaHRDaGlsZChOKSA9IChOICogMikgKyAyXG4gICAqL1xuXG4gIHByaXZhdGUgZ2V0UGFyZW50SW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKGluZGV4ID09PSAwKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIHJldHVybiBNYXRoLmZsb29yKChpbmRleCAtIDEpIC8gMik7XG4gIH1cblxuICBwcml2YXRlIGdldExlZnRDaGlsZEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGluZGV4ICogMiArIDE7XG4gICAgcmV0dXJuIGNhbmRpZGF0ZSA8IHRoaXMuaGVhcC5sZW5ndGggPyBjYW5kaWRhdGUgOiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0UmlnaHRDaGlsZEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGluZGV4ICogMiArIDI7XG4gICAgcmV0dXJuIGNhbmRpZGF0ZSA8IHRoaXMuaGVhcC5sZW5ndGggPyBjYW5kaWRhdGUgOiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgc2lmdFVwSW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgY29uc3QgcGFyZW50SW5kZXggPSB0aGlzLmdldFBhcmVudEluZGV4KGluZGV4KTtcbiAgICBpZiAocGFyZW50SW5kZXggPT09IC0xKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbXBhcmUocGFyZW50SW5kZXgsIGluZGV4KSA+IDApIHtcbiAgICAgIHJldHVybiBwYXJlbnRJbmRleDtcbiAgICB9XG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0VXAoaW5kZXg6IG51bWJlcikge1xuICAgIGxldCBzaWZ0SW5kZXggPSB0aGlzLnNpZnRVcEluZGV4KGluZGV4KTtcbiAgICB3aGlsZSAoc2lmdEluZGV4ICE9PSAtMSkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCBzaWZ0SW5kZXgpO1xuICAgICAgaW5kZXggPSBzaWZ0SW5kZXg7XG4gICAgICBzaWZ0SW5kZXggPSB0aGlzLnNpZnRVcEluZGV4KGluZGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNpZnREb3duSW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKGluZGV4ID49IHRoaXMuaGVhcC5sZW5ndGgpIHtcbiAgICAgIHJldHVybiAtMTtcbiAgICB9XG4gICAgbGV0IGxhcmdlc3RDaGlsZEluZGV4ID0gaW5kZXg7XG4gICAgY29uc3QgbGVmdENoaWxkSW5kZXggPSB0aGlzLmdldExlZnRDaGlsZEluZGV4KGluZGV4KTtcbiAgICBpZiAoKGxlZnRDaGlsZEluZGV4ICE9PSAtMSkgJiZcbiAgICAgICAgKHRoaXMuY29tcGFyZShsZWZ0Q2hpbGRJbmRleCwgbGFyZ2VzdENoaWxkSW5kZXgpIDwgMCkpIHtcbiAgICAgIGxhcmdlc3RDaGlsZEluZGV4ID0gbGVmdENoaWxkSW5kZXg7XG4gICAgfVxuICAgIGNvbnN0IHJpZ2h0Q2hpbGRJbmRleCA9IHRoaXMuZ2V0UmlnaHRDaGlsZEluZGV4KGluZGV4KTtcbiAgICBpZiAoKHJpZ2h0Q2hpbGRJbmRleCAhPT0gLTEpICYmXG4gICAgICAgICh0aGlzLmNvbXBhcmUocmlnaHRDaGlsZEluZGV4LCBsYXJnZXN0Q2hpbGRJbmRleCkgPCAwKSkge1xuICAgICAgbGFyZ2VzdENoaWxkSW5kZXggPSByaWdodENoaWxkSW5kZXg7XG4gICAgfVxuICAgIHJldHVybiAobGFyZ2VzdENoaWxkSW5kZXggPT09IGluZGV4KSA/IC0xIDogbGFyZ2VzdENoaWxkSW5kZXg7XG4gIH1cblxuICBwcml2YXRlIHNpZnREb3duKGluZGV4OiBudW1iZXIpIHtcbiAgICBsZXQgc2lmdEluZGV4ID0gdGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KTtcbiAgICB3aGlsZSAoc2lmdEluZGV4ICE9PSAtMSkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCBzaWZ0SW5kZXgpO1xuICAgICAgaW5kZXggPSBzaWZ0SW5kZXg7XG4gICAgICBzaWZ0SW5kZXggPSB0aGlzLnNpZnREb3duSW5kZXgoaW5kZXgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY29tcGFyZShhSW5kZXg6IG51bWJlciwgYkluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmNvbXBhcmF0b3IodGhpcy5oZWFwW2FJbmRleF0sIHRoaXMuaGVhcFtiSW5kZXhdKTtcbiAgfVxuXG4gIHByaXZhdGUgc3dhcChhOiBudW1iZXIsIGI6IG51bWJlcikge1xuICAgIGNvbnN0IHRlbXAgPSB0aGlzLmhlYXBbYV07XG4gICAgdGhpcy5oZWFwW2FdID0gdGhpcy5oZWFwW2JdO1xuICAgIHRoaXMuaGVhcFtiXSA9IHRlbXA7XG4gICAgdGhpcy5vbkluZGV4Q2hhbmdlZCh0aGlzLmhlYXBbYV0sIGEpO1xuICAgIHRoaXMub25JbmRleENoYW5nZWQodGhpcy5oZWFwW2JdLCBiKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoLCBOb2RlLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyBvcGVyYXRpb25fZW1pdHRlciBmcm9tICcuL29wZXJhdGlvbl9lbWl0dGVyJztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge09wdGltaXplcn0gZnJvbSAnLi9vcHRpbWl6ZXInO1xuaW1wb3J0ICogYXMgc2Vzc2lvbl91dGlsIGZyb20gJy4vc2Vzc2lvbl91dGlsJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogRmVlZEVudHJ5IGFzc29jaWF0ZXMgYSB0ZW5zb3Igd2l0aCB1c2VyLXByb3ZpZGVkIE5EQXJyYXkgZGF0YS5cbiAqL1xuZXhwb3J0IHR5cGUgRmVlZEVudHJ5ID0ge1xuICB0ZW5zb3I6IFRlbnNvcixcbiAgZGF0YTogTkRBcnJheXxJbnB1dFByb3ZpZGVyXG59O1xuXG4vKipcbiAqIEEgRmVlZERpY3Rpb25hcnkgaG9sZHMgYSBtYXAgZnJvbSB0ZW5zb3JzIHRvIHVzZXItcHJvdmlkZWQgTkRBcnJheXMuIEZlZWRcbiAqIGRpY3Rpb25hcmllcyByZXByZXNlbnQgdGhlICdlbnRyeSBwb2ludHMnIG9mIGV2YWx1YXRpb24sIHNpbmNlIGdyYXBoIG5vZGVzXG4gKiB0aGF0IGFyZSByZXBsYWNlZCBieSBmZWVkcyBkb24ndCBuZWVkIHRvIGhhdmUgdGhlaXIgaW5wdXQgbm9kZXMgZXZhbHVhdGVkLlxuICogRmVlZCBkaWN0aW9uYXJpZXMgdXN1YWxseSBwcm92aWRlIE5EQXJyYXkgZGF0YSBmb3IgUGxhY2Vob2xkZXIgbm9kZXMsIGJ1dCBhbnlcbiAqIG5vZGUgaW4gdGhlIGdyYXBoIGNhbiBiZSByZXBsYWNlZCBieSBhIGZlZWQgZGljdGlvbmFyeSBlbnRyeS5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZWVkRGljdGlvbmFyeSB7XG4gIGRpY3Q6IHtbdGVuc29ySUQ6IG51bWJlcl06IEZlZWRFbnRyeX0gPSB7fTtcblxuICAvKipcbiAgICogT3B0aW9uYWxseSBjb25zdHJ1Y3QgYSBGZWVkRGljdGlvbmFyeSBmcm9tIGFuIGFycmF5IG9mIGVudHJpZXMuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBPcHRpb25hbCBhcnJheSBvZiBGZWVkRW50cnkgb2JqZWN0cy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKGZlZWRFbnRyaWVzPzogRmVlZEVudHJ5W10pIHtcbiAgICBpZiAoZmVlZEVudHJpZXMpIHtcbiAgICAgIGZlZWRFbnRyaWVzLmZvckVhY2goZW50cnkgPT4gdGhpcy5kaWN0W2VudHJ5LnRlbnNvci5pZF0gPSBlbnRyeSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBlbnVtIENvc3RSZWR1Y3Rpb24ge1xuICBOT05FLFxuICBTVU0sXG4gIE1FQU5cbn1cblxuLyoqXG4gKiBBIFNlc3Npb24gbWFpbnRhaW5zIHRoZSBydW50aW1lIHN0YXRlIHJlcXVpcmVkIHRvIGVmZmljaWVudGx5IGV2YWx1YXRlIG5vZGVzLlxuICogT24gdGhlaXIgb3duLCBncmFwaCBvYmplY3RzIGFyZSB2ZXJ5IGxpZ2h0d2VpZ2h0IGxvZ2ljYWwgdG9wb2xvZ2llczsgdGhleVxuICogaGF2ZSBubyByZWxhdGlvbnNoaXAgd2l0aCB0aGUgR1BVLiBTZXNzaW9ucyBlbmNhcHN1bGF0ZSB0aGUgZXZhbHVhdGlvbiBvZlxuICogbm9kZXMsIHRoZSBtYW5hZ2VtZW50IG9mIEdQVSByZXNvdXJjZXMsIHRoZSBjYWNoaW5nIG9mIGV2YWx1YXRpb24gcGF0aHMsIGFuZFxuICogYW55dGhpbmcgZWxzZSByZXF1aXJlZCB0byBldmFsdWF0ZSBvciB0cmFpbiBhIG5ldHdvcmsuXG4gKi9cbmV4cG9ydCBjbGFzcyBTZXNzaW9uIHtcbiAgLyoqXG4gICAqIEBwYXJhbSBncmFwaCBUaGUgZ3JhcGggdG8gYXNzb2NpYXRlIHdpdGggdGhpcyBTZXNzaW9uLlxuICAgKiBAcGFyYW0gbWF0aCBUaGUgTkRBcnJheU1hdGggaW50ZXJmYWNlIHRoYXQgdGhpcyBTZXNzaW9uIHNob3VsZCB1c2UuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSBtYXRoOiBOREFycmF5TWF0aCkge31cblxuICAvKipcbiAgICogUmVsZWFzZSBhbGwgc3lzdGVtIHJlc291cmNlcyBhc3NvY2lhdGVkIHdpdGggdGhpcyBTZXNzaW9uLlxuICAgKi9cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmFjdGl2YXRpb25BcnJheU1hcC5kaXNwb3NlKCk7XG4gICAgT2JqZWN0LmtleXModGhpcy5ydW50aW1lQ2FjaGUpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IHJ1bnRpbWUgPSB0aGlzLnJ1bnRpbWVDYWNoZVtrZXldO1xuICAgICAgaWYgKHJ1bnRpbWUub3BlcmF0aW9ucykge1xuICAgICAgICBydW50aW1lLm9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5kaXNwb3NlKCkpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucnVudGltZUNhY2hlID0ge307XG4gICAgaWYgKHRoaXMuYmF0Y2hTaXplU2NhbGFyICE9IG51bGwpIHtcbiAgICAgIHRoaXMuYmF0Y2hTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgdGhpcy5vbmVTY2FsYXIuZGlzcG9zZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV2YWx1YXRlIGEgbGlzdCBvZiB0ZW5zb3JzLCB1c2luZyB0aGUgcHJvdmlkZWQgZmVlZCBlbnRyaWVzIHRvIHByb3ZpZGVcbiAgICogdXBzdHJlYW0gTkRBcnJheSBpbnB1dC5cbiAgICogV2hlbiB1c2luZyBhIGBOREFycmF5TWF0aGAgb2JqZWN0IGluIHNhZmUgbW9kZSB0aGlzIG11c3QgYmUgdXNlZCBpbiBhXG4gICAqIG1hdGguc2NvcGUoKS5cbiAgICogQHBhcmFtIHRlbnNvcnMgVGhlIGxpc3Qgb2YgdGVuc29ycyB0byBldmFsdWF0ZS5cbiAgICogQHBhcmFtIGZlZWRFbnRyaWVzIExpc3Qgb2YgYEZlZWRFbnRyeWAgdG8gcmVhZCB3aGVuIHJlcGxhY2luZyBncmFwaFxuICAgKiB0ZW5zb3JzIHdpdGggTkRBcnJheXMuXG4gICAqIEByZXR1cm4gVGhlIGNvbXB1dGVkIHZhbHVlcyBvZiB0aGUgdGVuc29ycy5cbiAgICovXG4gIGV2YWxBbGwodGVuc29yczogVGVuc29yW10sIGZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSk6IE5EQXJyYXlbXSB7XG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICBjb25zdCBmZWVkID0gbmV3IEZlZWREaWN0aW9uYXJ5KGZlZWRFbnRyaWVzKTtcbiAgICAgIGNvbnN0IHJ1bnRpbWUgPSB0aGlzLmdldE9yQ3JlYXRlUnVudGltZSh0ZW5zb3JzLCBmZWVkKTtcblxuICAgICAgY29uc3QgYWN0aXZhdGlvbnMgPSB0aGlzLmFjdGl2YXRpb25BcnJheU1hcDtcblxuICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICAgICAgICBydW50aW1lLm5vZGVzLCBhY3RpdmF0aW9ucyk7XG4gICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZVRyYW5zaWVudE9wZXJhdGlvbkFycmF5cyhcbiAgICAgICAgICBydW50aW1lLm9wZXJhdGlvbnMsIHRoaXMuYWN0aXZhdGlvbkFycmF5TWFwLCB0aGlzLmdyYWRpZW50QXJyYXlNYXApO1xuXG4gICAgICBzZXNzaW9uX3V0aWwuYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgc2Vzc2lvbl91dGlsLmxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgIGZlZWQsIGFjdGl2YXRpb25zLCB0aGlzLm1hdGgpO1xuXG4gICAgICBydW50aW1lLm9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5mZWVkRm9yd2FyZCh0aGlzLm1hdGgsIGFjdGl2YXRpb25zKSk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdHMgPSB0ZW5zb3JzLm1hcCh4ID0+IGFjdGl2YXRpb25zLmdldCh4KSk7XG4gICAgICB0ZW5zb3JzLmZvckVhY2goeCA9PiBhY3RpdmF0aW9ucy5kZWxldGUoeCkpO1xuXG4gICAgICBzZXNzaW9uX3V0aWwucmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgICAgICAgIGZlZWQsIGFjdGl2YXRpb25zLCB0aGlzLm1hdGgpO1xuXG4gICAgICByZXR1cm4gcmVzdWx0cztcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZSBhIHRlbnNvciwgdXNpbmcgdGhlIHByb3ZpZGVkIGZlZWQgZW50cmllcyB0byBwcm92aWRlXG4gICAqIHVwc3RyZWFtIE5EQXJyYXkgaW5wdXQuXG4gICAqXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciB0byBldmFsdWF0ZS5cbiAgICogQHBhcmFtIGZlZWRFbnRyaWVzIExpc3Qgb2YgYEZlZWRFbnRyeWAgdG8gcmVhZCB3aGVuIHJlcGxhY2luZyBncmFwaFxuICAgKiB0ZW5zb3JzIHdpdGggTkRBcnJheXMuXG4gICAqIEByZXR1cm4gVGhlIGNvbXB1dGVkIHZhbHVlIG9mIHRoZSB0ZW5zb3IuXG4gICAqL1xuICBldmFsKHRlbnNvcjogVGVuc29yLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10pOiBOREFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5ldmFsQWxsKFt0ZW5zb3JdLCBmZWVkRW50cmllcylbMF07XG4gIH1cblxuICAvKipcbiAgICogVHJhaW5zIGEgYmF0Y2guXG4gICAqIFJldHVybnMgYSByZWR1Y2VkIGNvc3QgaWYgdGhlIGNvc3RSZWR1Y3Rpb24gcGFyYW1ldGVyIGlzIHNldC5cbiAgICogV2hlbiB1c2luZyBhIGBOREFycmF5TWF0aGAgb2JqZWN0IGluIHNhZmUgbW9kZSB0aGlzIG11c3QgYmUgdXNlZCBpbiBhXG4gICAqIG1hdGguc2NvcGUoKS5cbiAgICogQHBhcmFtIGNvc3RUZW5zb3IgQSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBjb3N0IHRvIG9wdGltaXplLiBTaG91bGQgYmUgYVxuICAgKiBzY2FsYXIuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBGZWVkIGVudHJpZXMgZm9yIHRoaXMgdHJhaW4gcnVuLiBQcm92aWRlcyBpbnB1dHMuXG4gICAqIEBwYXJhbSBiYXRjaFNpemUgQmF0Y2ggc2l6ZSBmb3IgdGhpcyB0cmFpbiBsb29wLlxuICAgKiBAcGFyYW0gb3B0aW1pemVyIEFuIG9wdGltaXplciB0byBwZXJmb3JtIHdlaWdodCB1cGRhdGVzLlxuICAgKiBAcGFyYW0gY29zdFJlZHVjdGlvbiBBbiBvcHRpb24gdG8gYWxsb3cgdGhlIHVzZXIgdG8gZ2V0IGEgc3VtbWVkLCBhdmVyYWdlZCxcbiAgICogb3Igbm8gY29zdCBiYWNrLlxuICAgKiBAcmV0dXJuIFRoZSByZWR1Y2VkIGNvc3QsIGlmIGNvc3QgcmVkdWN0aW9uIGlzIG5vdCBOT05FLiBUaGUgdXNlciBpc1xuICAgKiByZXNwb25zaWJsZSBmb3IgZGlzcG9zaW5nIHRoZSBjb3N0IE5EQXJyYXkgYmV0d2VlbiB0cmFpbiBsb29wcy5cbiAgICovXG4gIHRyYWluKFxuICAgICAgY29zdFRlbnNvcjogVGVuc29yLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10sIGJhdGNoU2l6ZTogbnVtYmVyLFxuICAgICAgb3B0aW1pemVyOiBPcHRpbWl6ZXIsIGNvc3RSZWR1Y3Rpb24gPSBDb3N0UmVkdWN0aW9uLk5PTkUpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmlzU2NhbGFyU2hhcGUoY29zdFRlbnNvci5zaGFwZSksXG4gICAgICAgICdDb3N0IHRlbnNvciBmb3IgdHJhaW5pbmcgbXVzdCBiZSBhIHNjYWxhciB2YWx1ZS4nKTtcblxuICAgIGlmICh0aGlzLnByZXZCYXRjaFNpemUgIT09IGJhdGNoU2l6ZSkge1xuICAgICAgdGhpcy5wcmV2QmF0Y2hTaXplID0gYmF0Y2hTaXplO1xuICAgICAgdGhpcy5iYXRjaFNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGJhdGNoU2l6ZSk7XG4gICAgfVxuXG4gICAgY29uc3QgZmVlZCA9IG5ldyBGZWVkRGljdGlvbmFyeShmZWVkRW50cmllcyk7XG4gICAgc2Vzc2lvbl91dGlsLnRocm93SWZGZWVkRGljdGlvbmFyeUNvbnRhaW5zTkRBcnJheXMoZmVlZCk7XG5cbiAgICBjb25zdCBydW50aW1lID0gdGhpcy5nZXRPckNyZWF0ZVJ1bnRpbWUoW2Nvc3RUZW5zb3JdLCBmZWVkKTtcbiAgICBjb25zdCBpbmZlcmVuY2VPcGVyYXRpb25zID0gcnVudGltZS5vcGVyYXRpb25zO1xuICAgIGNvbnN0IGJhY2tQcm9wT3BlcmF0aW9ucyA9IHJ1bnRpbWUub3BlcmF0aW9ucy5zbGljZSgpLnJldmVyc2UoKTtcbiAgICBjb25zdCBhY3RpdmF0aW9ucyA9IHRoaXMuYWN0aXZhdGlvbkFycmF5TWFwO1xuICAgIGNvbnN0IGdyYWRpZW50cyA9IHRoaXMuZ3JhZGllbnRBcnJheU1hcDtcbiAgICBncmFkaWVudHMuc2V0KGNvc3RUZW5zb3IsIHRoaXMub25lU2NhbGFyKTtcblxuICAgIHNlc3Npb25fdXRpbC5hZGRQZXJzaXN0ZW50QXJyYXlzVG9UZW5zb3JBcnJheU1hcChcbiAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuXG4gICAgb3B0aW1pemVyLmJlZm9yZUJhdGNoKFxuICAgICAgICB0aGlzLm1hdGgsIGJhdGNoU2l6ZSwgcnVudGltZSwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRoLnNjb3BlKChrZWVwLCB0cmFjaykgPT4ge1xuICAgICAgbGV0IGNvc3QgPSB0cmFjayhTY2FsYXIubmV3KDApKTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBiYXRjaFNpemU7ICsraSkge1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25PdXRwdXRzKFxuICAgICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25JbnB1dEdyYWRpZW50cyhcbiAgICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGdyYWRpZW50cyk7XG4gICAgICAgIHNlc3Npb25fdXRpbC5kaXNwb3NlVHJhbnNpZW50T3BlcmF0aW9uQXJyYXlzKFxuICAgICAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgICBzZXNzaW9uX3V0aWwubG9hZElucHV0c0Zyb21GZWVkRGljdGlvbmFyeVRvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgICBpbmZlcmVuY2VPcGVyYXRpb25zLmZvckVhY2goXG4gICAgICAgICAgICBvcCA9PiBvcC5mZWVkRm9yd2FyZCh0aGlzLm1hdGgsIGFjdGl2YXRpb25zKSk7XG4gICAgICAgIGJhY2tQcm9wT3BlcmF0aW9ucy5mb3JFYWNoKFxuICAgICAgICAgICAgb3AgPT4gb3AuYmFja1Byb3AodGhpcy5tYXRoLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKSk7XG5cbiAgICAgICAgb3B0aW1pemVyLmFmdGVyRXhhbXBsZSh0aGlzLm1hdGgsIHJ1bnRpbWUsIGFjdGl2YXRpb25zLCBncmFkaWVudHMpO1xuXG4gICAgICAgIHNlc3Npb25fdXRpbC5yZWxlYXNlRmVlZERpY3Rpb25hcnlJbnB1dHNGcm9tVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgICBjb3N0ID0gdGhpcy51cGRhdGVDb3N0Rm9yRXhhbXBsZShcbiAgICAgICAgICAgIGNvc3QsIGFjdGl2YXRpb25zLmdldChjb3N0VGVuc29yKSwgY29zdFJlZHVjdGlvbik7XG4gICAgICB9XG5cbiAgICAgIG9wdGltaXplci5hZnRlckJhdGNoKFxuICAgICAgICAgIHRoaXMubWF0aCwgYmF0Y2hTaXplLCBydW50aW1lLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgcmV0dXJuIHRoaXMudXBkYXRlQ29zdEZvckJhdGNoKGNvc3QsIGNvc3RSZWR1Y3Rpb24pO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVDb3N0Rm9yRXhhbXBsZShcbiAgICAgIHRvdGFsQ29zdDogU2NhbGFyLCBjdXJyQ29zdDogU2NhbGFyLFxuICAgICAgY29zdFJlZHVjdGlvbjogQ29zdFJlZHVjdGlvbik6IFNjYWxhciB7XG4gICAgaWYgKGNvc3RSZWR1Y3Rpb24gPT09IENvc3RSZWR1Y3Rpb24uTUVBTiB8fFxuICAgICAgICBjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLlNVTSkge1xuICAgICAgcmV0dXJuIHRoaXMubWF0aC5hZGQodG90YWxDb3N0LCBjdXJyQ29zdCk7XG4gICAgfVxuICAgIHJldHVybiB0b3RhbENvc3Q7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZUNvc3RGb3JCYXRjaCh0b3RhbENvc3Q6IFNjYWxhciwgY29zdFJlZHVjdGlvbjogQ29zdFJlZHVjdGlvbik6XG4gICAgICBTY2FsYXIge1xuICAgIGlmIChjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLk1FQU4pIHtcbiAgICAgIHJldHVybiB0aGlzLm1hdGguZGl2aWRlKHRvdGFsQ29zdCwgdGhpcy5iYXRjaFNpemVTY2FsYXIpO1xuICAgIH1cbiAgICByZXR1cm4gdG90YWxDb3N0O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRPckNyZWF0ZVJ1bnRpbWUodGVuc29yczogVGVuc29yW10sIGZlZWQ6IEZlZWREaWN0aW9uYXJ5KTpcbiAgICAgIFNlc3Npb25SdW50aW1lIHtcbiAgICBjb25zdCBrZXkgPSB0aGlzLm1ha2VSdW50aW1lQ2FjaGVLZXkodGVuc29ycywgZmVlZCk7XG4gICAgbGV0IHJ1bnRpbWUgPSB0aGlzLnJ1bnRpbWVDYWNoZVtrZXldO1xuICAgIGlmIChydW50aW1lID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGxldCBub2RlcyA9XG4gICAgICAgICAgc2Vzc2lvbl91dGlsLmdldE9yZGVyZWRFdmFsdWF0aW9uU2V0RnJvbUV2YWxUZW5zb3IodGVuc29ycywgZmVlZCk7XG4gICAgICAvLyBJbiBpbmZlcmVuY2UgbW9kZSBzcGxpdCBub2RlcyBhcmUgbm90IG5lZWRlZCwgYnV0IHRoZWlyIGNvc3QgaXNcbiAgICAgIC8vIG5lZ2xpZ2libGUsIGFuZCBhbHdheXMgYWRkaW5nIHRoZW0gaW4gYWxsb3dzIGZvciBjYWNoaW5nIG9mIDEgcnVudGltZVxuICAgICAgLy8gZm9yIGJvdGggdHJhaW4vZXZhbC5cbiAgICAgIG5vZGVzID0gc2Vzc2lvbl91dGlsLmFkZFNwbGl0Tm9kZXMobm9kZXMpO1xuICAgICAgc2Vzc2lvbl91dGlsLnJlbW92ZUZlZWREaWN0aW9uYXJ5Tm9kZXNGcm9tRXZhbHVhdGlvblNldChmZWVkLCBub2Rlcyk7XG4gICAgICBzZXNzaW9uX3V0aWwudGhyb3dFcnJvcklmRXZhbHVhdGlvblNldENvbnRhaW5zUGxhY2Vob2xkZXJOb2Rlcyhub2Rlcyk7XG4gICAgICBjb25zdCBvcGVyYXRpb25zID0gb3BlcmF0aW9uX2VtaXR0ZXIuZW1pdEZyb21HcmFwaE5vZGVzKG5vZGVzKTtcbiAgICAgIHJ1bnRpbWUgPSB7bm9kZXMsIG9wZXJhdGlvbnN9O1xuICAgICAgdGhpcy5ydW50aW1lQ2FjaGVba2V5XSA9IHJ1bnRpbWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJ1bnRpbWU7XG4gIH1cblxuICBwcml2YXRlIG1ha2VSdW50aW1lQ2FjaGVLZXkodGVuc29yczogVGVuc29yW10sIGZlZWQ6IEZlZWREaWN0aW9uYXJ5KTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGVuc29ycy5tYXAoeCA9PiB4LmlkKS5zb3J0KCkuam9pbignXycpICsgJ19fJyArXG4gICAgICAgIE9iamVjdC5rZXlzKGZlZWQuZGljdCkuc29ydCgpLmpvaW4oJ18nKTtcbiAgfVxuXG4gIC8qKiBNYXBzIGVhY2ggb3V0cHV0IHRlbnNvciBvZiB0aGUgZ3JhcGggdG8gaXRzIGFjdGl2YXRpb24gdmFsdWUuICovXG4gIGFjdGl2YXRpb25BcnJheU1hcCA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICAvKiogTWFwcyBlYWNoIHRlbnNvciBvZiB0aGUgZ3JhcGggdG8gaXRzIGRlcml2YXRpdmUgd3J0IHRoZSBjb3N0IGZ1bmN0aW9uLiAqL1xuICBncmFkaWVudEFycmF5TWFwID0gbmV3IFRlbnNvckFycmF5TWFwKCk7XG4gIHByaXZhdGUgcnVudGltZUNhY2hlOiB7W2tleTogc3RyaW5nXTogU2Vzc2lvblJ1bnRpbWV9ID0ge307XG4gIC8qKiBCYXRjaCBzaXplIG9mIHRoZSBwcmV2aW91cyB0cmFpbigpIGNhbGwuICovXG4gIHByaXZhdGUgcHJldkJhdGNoU2l6ZTogbnVtYmVyO1xuICBwcml2YXRlIGJhdGNoU2l6ZVNjYWxhcjogU2NhbGFyO1xuICBwcml2YXRlIG9uZVNjYWxhciA9IFNjYWxhci5uZXcoMSk7XG59XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgdHlwZSBTZXNzaW9uUnVudGltZSA9IHtcbiAgbm9kZXM6IE5vZGVbXTsgb3BlcmF0aW9uczogT3BlcmF0aW9uW107XG59O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0NvbnN0YW50Tm9kZSwgTm9kZSwgUGxhY2Vob2xkZXJOb2RlLCBTcGxpdE5vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge0ZlZWREaWN0aW9uYXJ5fSBmcm9tICcuL3Nlc3Npb24nO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuLyoqXG4gKiBDcmVhdGVzIGFuIGFycmF5IG9mIGdyYXBoIG5vZGVzIHRoYXQgc3RvcCB0cmF2ZXJzYWwsIGJhc2VkIG9uIHRoZSBjb250ZW50c1xuICogb2YgdGhlIHByb3ZpZGVkIEZlZWREaWN0aW9uYXJ5LiBUaGlzIGlzIGEgc2ltcGxlIDE6MSBleHRyYWN0aW9uIG9mIG5vZGVzIGZyb21cbiAqIHRoZSBGZWVkRGljdGlvbmFyeS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIEZlZWREaWN0aW9uYXJ5IHRvIHNjYW4gZm9yIHRlcm1pbmF0aW9uIG5vZGVzLlxuICogQHJldHVybiBhbiBhcnJheSBvZiBOb2RlcyB3aGljaCBoYWx0IHRyYXZlcnNhbCB3aGVuIHZpc2l0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUZXJtaW5hdGluZ05vZGVzRnJvbUZlZWREaWN0aW9uYXJ5KFxuICAgIGZlZWREaWN0aW9uYXJ5OiBGZWVkRGljdGlvbmFyeSk6IE5vZGVbXSB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhmZWVkRGljdGlvbmFyeS5kaWN0KVxuICAgICAgLm1hcCh0ZW5zb3JJRCA9PiBmZWVkRGljdGlvbmFyeS5kaWN0Wyt0ZW5zb3JJRF0udGVuc29yLm5vZGUpO1xufVxuXG4vKipcbiAqIEdpdmVuIGEgdGVuc29yIGFuZCBhIGZlZWQgZGljdGlvbmFyeSwgY29tcHV0ZXMgdGhlIHNldCBvZiBub2RlcyB0aGF0IG5lZWQgdG9cbiAqIGJlIGV2YWx1YXRlZCB0byBwZXJmb3JtIGluZmVyZW5jZS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbFRlbnNvcnMgVGhlIGxpc3Qgb2YgdGVuc29ycyB0byBldmVudHVhbGx5IGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBmZWVkRGljdGlvbmFyeSBUaGUgcG9wdWxhdGVkIGZlZWQgZGljdGlvbmFyeS5cbiAqIEByZXR1cm4gVGhlIHNldCBvZiBub2RlcyB0byBldmFsdWF0ZSwgaW4gZXZhbHVhdGlvbiBvcmRlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE9yZGVyZWRFdmFsdWF0aW9uU2V0RnJvbUV2YWxUZW5zb3IoXG4gICAgZXZhbFRlbnNvcnM6IFRlbnNvcltdLCBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpOiBOb2RlW10ge1xuICBjb25zdCB0ZXJtaW5hdGluZ05vZGVzID1cbiAgICAgIGdldFRlcm1pbmF0aW5nTm9kZXNGcm9tRmVlZERpY3Rpb25hcnkoZmVlZERpY3Rpb25hcnkpO1xuICBjb25zdCBldmFsTm9kZXMgPSBldmFsVGVuc29ycy5tYXAoeCA9PiB4Lm5vZGUpO1xuICBjb25zdCB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0ID1cbiAgICAgIGdyYXBoX3V0aWwuZ2V0VW5vcmRlcmVkRXZhbHVhdGlvblNldChldmFsTm9kZXMsIHRlcm1pbmF0aW5nTm9kZXMpO1xuICBjb25zdCBvcmRlcmVkRXZhbHVhdGlvblNldCA9XG4gICAgICBncmFwaF91dGlsLmdldE9yZGVyZWRFdmFsdWF0aW9uU2V0KHVub3JkZXJlZEV2YWx1YXRpb25TZXQpO1xuICByZXR1cm4gb3JkZXJlZEV2YWx1YXRpb25TZXQ7XG59XG5cbi8qKlxuICogVHJhdmVyc2VzIHRoZSBwcm92aWRlZCBub2RlIGFycmF5IGFuZCBhZGRzIGFsbCBwZXJzaXN0ZW50IG5vZGUgTkRBcnJheXMgdG9cbiAqIHRoZSBwcm92aWRlZCBUZW5zb3JBcnJheU1hcC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gc2Nhbi5cbiAqIEBwYXJhbSB0ZW5zb3JBcnJheU1hcCBUaGUgbWFwIHRoYXQgcmVjZWl2ZXMgdGhlIE5EQXJyYXlzIGZyb20gcGVyc2lzdGVudFxuICogbm9kZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZGRQZXJzaXN0ZW50QXJyYXlzVG9UZW5zb3JBcnJheU1hcChcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIHRlbnNvckFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKG5vZGUgaW5zdGFuY2VvZiBWYXJpYWJsZU5vZGUgfHwgbm9kZSBpbnN0YW5jZW9mIENvbnN0YW50Tm9kZSkge1xuICAgICAgdGVuc29yQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBub2RlLmRhdGEpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VmFyaWFibGVOb2Rlc0Zyb21FdmFsdWF0aW9uU2V0KGV2YWx1YXRpb25TZXQ6IE5vZGVbXSk6XG4gICAgVmFyaWFibGVOb2RlW10ge1xuICBjb25zdCBub2RlczogVmFyaWFibGVOb2RlW10gPSBbXTtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgVmFyaWFibGVOb2RlKSB7XG4gICAgICBub2Rlcy5wdXNoKG5vZGUpO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiBub2Rlcztcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0aHJvd0lmRmVlZERpY3Rpb25hcnlDb250YWluc05EQXJyYXlzKFxuICAgIGZlZWREaWN0aW9uYXJ5OiBGZWVkRGljdGlvbmFyeSkge1xuICBPYmplY3Qua2V5cyhmZWVkRGljdGlvbmFyeS5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICBpZiAoZmVlZERpY3Rpb25hcnkuZGljdFsrdGVuc29ySURdLmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ3RyYWluaW5nIHJlcXVpcmVzIEZlZWREaWN0aW9uYXJ5IGVudHJpZXMgdG8gYmUgSW5wdXRQcm92aWRlcnMnICtcbiAgICAgICAgICAnYW5kIG5vdCBOREFycmF5cy4nKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgIGJhdGNoRmVlZDogRmVlZERpY3Rpb25hcnksIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCwgbWF0aDogTkRBcnJheU1hdGgpIHtcbiAgT2JqZWN0LmtleXMoYmF0Y2hGZWVkLmRpY3QpLmZvckVhY2godGVuc29ySUQgPT4ge1xuICAgIGNvbnN0IGZlZWRFbnRyeSA9IGJhdGNoRmVlZC5kaWN0Wyt0ZW5zb3JJRF07XG5cbiAgICBsZXQgZGF0YTogTkRBcnJheTtcbiAgICBpZiAoZmVlZEVudHJ5LmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICBkYXRhID0gZmVlZEVudHJ5LmRhdGEgYXMgTkRBcnJheTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgcHJvdmlkZXIgPSBmZWVkRW50cnkuZGF0YSBhcyBJbnB1dFByb3ZpZGVyO1xuICAgICAgZGF0YSA9IHByb3ZpZGVyLmdldE5leHRDb3B5KG1hdGgpO1xuICAgIH1cblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKGZlZWRFbnRyeS50ZW5zb3Iuc2hhcGUsIGRhdGEuc2hhcGUpLFxuICAgICAgICBgRXJyb3IgbG9hZGluZyBGZWVkRW50cnk6IGZlZWRpbmcgTkRBcnJheSBvZiBzaGFwZSAke2RhdGEuc2hhcGV9IGAgK1xuICAgICAgICAgICAgYGRvZXMgbm90IG1hdGNoIFRlbnNvciAoaWQ6ICR7ZmVlZEVudHJ5LnRlbnNvci5pZH0pIHNoYXBlOiBgICtcbiAgICAgICAgICAgIGAke2ZlZWRFbnRyeS50ZW5zb3Iuc2hhcGV9LmApO1xuICAgIGFjdGl2YXRpb25zLnNldChmZWVkRW50cnkudGVuc29yLCBkYXRhKTtcbiAgfSk7XG59XG5cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWxlYXNlRmVlZERpY3Rpb25hcnlJbnB1dHNGcm9tVGVuc29yQXJyYXlNYXAoXG4gICAgYmF0Y2hGZWVkOiBGZWVkRGljdGlvbmFyeSwgYWN0aXZhdGlvbnM6IFRlbnNvckFycmF5TWFwLCBtYXRoOiBOREFycmF5TWF0aCkge1xuICBPYmplY3Qua2V5cyhiYXRjaEZlZWQuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgY29uc3QgZmVlZEVudHJ5ID0gYmF0Y2hGZWVkLmRpY3RbK3RlbnNvcklEXTtcblxuICAgIGlmICghKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkpIHtcbiAgICAgIGNvbnN0IHByb3ZpZGVyID0gZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcjtcblxuICAgICAgY29uc3QgZmVlZEVudHJ5QXJyYXkgPSBhY3RpdmF0aW9ucy5nZXQoZmVlZEVudHJ5LnRlbnNvcik7XG4gICAgICBwcm92aWRlci5kaXNwb3NlQ29weShtYXRoLCBmZWVkRW50cnlBcnJheSk7XG4gICAgfVxuXG4gICAgYWN0aXZhdGlvbnMuZGVsZXRlKGZlZWRFbnRyeS50ZW5zb3IpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBSZW1vdmVzIGFsbCBub2RlcyBmcm9tIHRoZSBwcm92aWRlZCBOb2RlIGFycmF5IHdob3NlIG91dHB1dCB0ZW5zb3JzIGV4aXN0IGluXG4gKiB0aGUgcHJvdmlkZWQgZmVlZCBkaWN0aW9uYXJ5LiBBZnRlciBjYWxsaW5nIHRoaXMsIHRoZSBOb2RlIGFycmF5IHNob3VsZFxuICogY29udGFpbiB6ZXJvIFBsYWNlaG9sZGVyIG5vZGVzLCBvciB0aGUgdXNlciBoYXMgZmFpbGVkIHRvIHByb3ZpZGUgYSBmZWVkIGZvclxuICogYSBQbGFjZWhvbGRlciBub2RlLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBmZWVkRGljdGlvbmFyeSBUaGUgRmVlZERpY3Rpb25hcnkgdG8gcHJvY2Vzcy5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBhcnJheSBvZiBub2RlcyB0byByZW1vdmUgaW5wdXQgbm9kZXMgZnJvbS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZUZlZWREaWN0aW9uYXJ5Tm9kZXNGcm9tRXZhbHVhdGlvblNldChcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnksIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSkge1xuICBsZXQgaSA9IDA7XG4gIHdoaWxlIChpIDwgZXZhbHVhdGlvblNldC5sZW5ndGgpIHtcbiAgICBjb25zdCBub2RlID0gZXZhbHVhdGlvblNldFtpXTtcbiAgICBpZiAoZmVlZERpY3Rpb25hcnkuZGljdFtub2RlLm91dHB1dC5pZF0gIT0gbnVsbCkge1xuICAgICAgZXZhbHVhdGlvblNldC5zcGxpY2UoaSwgMSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICsraTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBEaXNwb3NlcyBhbnkgTkRBcnJheXMgb24gdGhlIHRlbnNvckFycmF5TWFwIGZyb20gb3BlcmF0aW9uIG91dHB1dHMgYW5kIHNldHNcbiAqIHRoZSB2YWx1ZSB0byBudWxsLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBzZXQgb2Ygbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIHRlbnNvckFycmF5TWFwIFRoZSBtYXAgdG8gZGlzcG9zZSBhbmQgaW5pdGlhbGl6ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIHRlbnNvckFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKCFncmFwaF91dGlsLmlzSW5wdXROb2RlKG5vZGUpKSB7XG4gICAgICBpZiAoIWdyYXBoX3V0aWwuaXNQYXNzdGhyb3VnaE5vZGUobm9kZSwgdGVuc29yQXJyYXlNYXApKSB7XG4gICAgICAgIHRlbnNvckFycmF5TWFwLmRpc3Bvc2VBcnJheShub2RlLm91dHB1dCk7XG4gICAgICB9XG4gICAgICB0ZW5zb3JBcnJheU1hcC5zZXQobm9kZS5vdXRwdXQsIG51bGwpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogRGlzcG9zZXMgYW55IE5EQXJyYXlzIG9uIHRoZSB0ZW5zb3JBcnJheU1hcCBmcm9tIGRlcml2YXRpdmVzIG9mIG9wZXJhdGlvblxuICogaW5wdXRzIGFuZCBzZXRzIHRoZSB2YWx1ZSB0byBudWxsLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBzZXQgb2Ygbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIGdyYWRpZW50cyBUaGUgZ3JhZGllbnQgbWFwIHRvIGRpc3Bvc2UgYW5kIGluaXRpYWxpemUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbklucHV0R3JhZGllbnRzKFxuICAgIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSwgZ3JhZGllbnRzOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpLmZvckVhY2goaW5wdXROYW1lID0+IHtcbiAgICAgIGNvbnN0IGlucHV0ID0gbm9kZS5pbnB1dHNbaW5wdXROYW1lXTtcbiAgICAgIGlmIChncmFkaWVudHMuZ2V0KGlucHV0LCB0cnVlKSAhPT0gZ3JhZGllbnRzLmdldChub2RlLm91dHB1dCwgdHJ1ZSkpIHtcbiAgICAgICAgZ3JhZGllbnRzLmRpc3Bvc2VBcnJheShpbnB1dCk7XG4gICAgICB9XG4gICAgICBncmFkaWVudHMuc2V0KGlucHV0LCBudWxsKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cblxuLyoqXG4gKiBDYWxscyB1bmRlcmx5aW5nIG9wZXJhdGlvbiBkaXNwb3NlVHJhbnNpZW50QXJyYXlzIG1ldGhvZHMgd2hpY2ggY2xlYW4gdXAgYW55XG4gKiBOREFycmF5cyB3aGljaCBvcGVyYXRpb25zIG1heSBoYXZlIGNyZWF0ZWQgZHVyaW5nIGEgcnVuLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBvcGVyYXRpb25Ob2RlcyBUaGUgYXJyYXkgb2YgTm9kZXMgdG8gdHJhdmVyc2UuXG4gKiBAcGFyYW0gb3V0cHV0VGVuc29yIFRoZSB0ZW5zb3IgYmVpbmcgZXZhbHVhdGVkLlxuICogQHBhcmFtIG1hcCBUaGUgVGVuc29yQXJyYXlNYXAgdG8gb3BlcmF0ZSBvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3Bvc2VUcmFuc2llbnRPcGVyYXRpb25BcnJheXMoXG4gICAgb3BlcmF0aW9uczogT3BlcmF0aW9uW10sIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCxcbiAgICBncmFkaWVudHM6IFRlbnNvckFycmF5TWFwKSB7XG4gIG9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5kaXNwb3NlVHJhbnNpZW50QXJyYXlzKGFjdGl2YXRpb25zLCBncmFkaWVudHMpKTtcbn1cblxuLyoqXG4gKiBJdGVyYXRlcyB0aGUgcHJvdmlkZWQgTm9kZSBhcnJheSBhbmQgdGhyb3dzIGFuIGV4Y2VwdGlvbiBpZiB0aGVyZSBhcmUgYW55XG4gKiBQbGFjZWhvbGRlciBub2RlcyBwcmVzZW50LiBDYWxsIGFmdGVyIHRoZSBldmFsdWF0aW9uIHNldCBoYXMgYmVlbiBwcnVuZWQgd2l0aFxuICogdGhlIGFjY29tcGFueWluZyBGZWVkRGljdGlvbmFyeSB0byBlbnN1cmUgdGhhdCBhbGwgaW5wdXRzIGhhdmUgYmVlbiByZXNvbHZlZC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gc2Nhbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRocm93RXJyb3JJZkV2YWx1YXRpb25TZXRDb250YWluc1BsYWNlaG9sZGVyTm9kZXMoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdKSB7XG4gIGV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHtcbiAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFBsYWNlaG9sZGVyTm9kZSkge1xuICAgICAgY29uc3Qgc2hhcGUgPSAnWycgKyBub2RlLm91dHB1dC5zaGFwZS5qb2luKCcsICcpICsgJ10nO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdQbGFjZWhvbGRlciBub2RlIFwiJyArIG5vZGUubmFtZSArICdcIiAnICsgc2hhcGUgK1xuICAgICAgICAgICcgbm90IHByZXNlbnQgaW4gZmVlZCBkaWN0aW9uYXJ5LicpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogSW5qZWN0cyBzcGxpdHMgbm9kZXMgYWZ0ZXIgZXZlcnkgbm9kZSB0aGF0IGhhcyBtdWx0aXBsZSBjb25zdW1lcnMuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIG5vZGVzIFRoZSBub2RlIGxpc3QgaW4gZXZhbHVhdGlvbiBvcmRlci5cbiAqIEByZXR1cm4gVGhlIG5vZGUgbGlzdCB3aXRoIHNwbGl0IG5vZGVzIGluamVjdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYWRkU3BsaXROb2Rlcyhub2RlczogTm9kZVtdKTogTm9kZVtdIHtcbiAgY29uc3Qgbm9kZUlkVG9OdW1Db25zdW1lcnM6IG51bWJlcltdID0gW107XG4gIGNvbnN0IG5vZGVJZFRvU3BsaXROb2RlOiB7W25vZGVJZDogbnVtYmVyXTogU3BsaXROb2RlfSA9IHt9O1xuXG4gIC8vIEZpbmQgbm9kZXMgdGhhdCBoYXZlIG11bHRpcGxlIGNvbnN1bWVycy5cbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IHtcbiAgICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpO1xuICAgIGtleXMuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgY29uc3QgaW5wdXRUZW5zb3IgPSBub2RlLmlucHV0c1trZXldO1xuICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dFRlbnNvci5ub2RlO1xuICAgICAgaWYgKG5vZGVJZFRvTnVtQ29uc3VtZXJzW2lucHV0LmlkXSA9PSBudWxsKSB7XG4gICAgICAgIG5vZGVJZFRvTnVtQ29uc3VtZXJzW2lucHV0LmlkXSA9IDA7XG4gICAgICB9XG4gICAgICBub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0rKztcbiAgICAgIGlmIChub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPiAxICYmXG4gICAgICAgICAgbm9kZUlkVG9TcGxpdE5vZGVbaW5wdXQuaWRdID09IG51bGwpIHtcbiAgICAgICAgbm9kZUlkVG9TcGxpdE5vZGVbaW5wdXQuaWRdID0gbmV3IFNwbGl0Tm9kZShpbnB1dC5ncmFwaCwgaW5wdXRUZW5zb3IpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcblxuICAvLyBJbmplY3QgYSBzcGxpdCBub2RlIGFmdGVyIGVhY2ggbm9kZSB0aGF0IGhhcyBtdWx0aXBsZSBjb25zdW1lcnMgYW5kXG4gIC8vIHJld2lyZSB0aGUgaW5wdXRzIG9mIHRoZSBjb25zdW1lcnMgdG8gY29uc3VtZSB0aGUgb3V0cHV0IHRlbnNvcnMgb2YgdGhlXG4gIC8vIHNwbGl0IG5vZGUgaW5zdGVhZCBvZiB0aGUgb3JpZ2luYWwgbm9kZS4gRWFjaCBjb25zdW1lciBjb25zdW1lcyBhXG4gIC8vIGRpZmZlcmVudCBvdXRwdXQgdGVuc29yIHNvIHRoYXQgZGVyaXZhdGl2ZXMgYXJlIG5vdCBvdmVyd3JpdHRlbi5cbiAgLy8geC0tPnkgIGJlY29tZXMgeC0tPnMtLT55ICAgd2hlcmUgeSBjb25zdW1lcyB0aGUgMXN0IG91dHB1dCB0ZW5zb3Igb2Ygc1xuICAvLyB8LS0+eiAgICAgICAgICAgICAgfC0tPnogICAgIGFuZCB6IGNvbnN1bWVzIHRoZSAybmQgb3V0cHV0IHRlbnNvciBvZiBzXG4gIGNvbnN0IG5ld05vZGVzOiBOb2RlW10gPSBbXTtcbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IHtcbiAgICBuZXdOb2Rlcy5wdXNoKG5vZGUpO1xuICAgIGlmIChub2RlLmlkIGluIG5vZGVJZFRvU3BsaXROb2RlKSB7XG4gICAgICBjb25zdCBzcGxpdE5vZGUgPSBub2RlSWRUb1NwbGl0Tm9kZVtub2RlLmlkXTtcbiAgICAgIG5ld05vZGVzLnB1c2goc3BsaXROb2RlKTtcbiAgICB9XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKTtcbiAgICBrZXlzLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VGVuc29yID0gbm9kZS5pbnB1dHNba2V5XTtcbiAgICAgIGNvbnN0IGlucHV0SWQgPSBpbnB1dFRlbnNvci5ub2RlLmlkO1xuICAgICAgaWYgKGlucHV0SWQgaW4gbm9kZUlkVG9TcGxpdE5vZGUpIHtcbiAgICAgICAgbm9kZS5pbnB1dHNba2V5XSA9IG5vZGVJZFRvU3BsaXROb2RlW2lucHV0SWRdLmdldE5ld091dHB1dFRlbnNvcigpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbiAgcmV0dXJuIG5ld05vZGVzO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge09wdGltaXplcn0gZnJvbSAnLi9vcHRpbWl6ZXInO1xuaW1wb3J0IHtTZXNzaW9uUnVudGltZX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCAqIGFzIHNlc3Npb25fdXRpbCBmcm9tICcuL3Nlc3Npb25fdXRpbCc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5leHBvcnQgY2xhc3MgU0dET3B0aW1pemVyIGV4dGVuZHMgT3B0aW1pemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBsZWFybmluZ1JhdGU6IG51bWJlciwgc3BlY2lmaWVkVmFyaWFibGVMaXN0PzogTm9kZVtdKSB7XG4gICAgc3VwZXIoc3BlY2lmaWVkVmFyaWFibGVMaXN0KTtcbiAgfVxuXG4gIGJlZm9yZUJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgdGhpcy52YXJpYWJsZU5vZGVzID0gdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzID09IG51bGwgP1xuICAgICAgICBzZXNzaW9uX3V0aWwuZ2V0VmFyaWFibGVOb2Rlc0Zyb21FdmFsdWF0aW9uU2V0KHJ1bnRpbWUubm9kZXMpIDpcbiAgICAgICAgdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzO1xuICAgIGlmIChiYXRjaFNpemUgIT09IHRoaXMucHJldkJhdGNoU2l6ZSkge1xuICAgICAgdGhpcy5wcmV2QmF0Y2hTaXplID0gYmF0Y2hTaXplO1xuICAgICAgdGhpcy5jID0gU2NhbGFyLm5ldygtdGhpcy5sZWFybmluZ1JhdGUgLyBiYXRjaFNpemUpO1xuICAgIH1cbiAgICB0aGlzLnZhcmlhYmxlTm9kZXMuZm9yRWFjaChcbiAgICAgICAgbm9kZSA9PiB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLnNldChcbiAgICAgICAgICAgIG5vZGUub3V0cHV0LCBOREFycmF5Lnplcm9zKG5vZGUub3V0cHV0LnNoYXBlKSkpO1xuICB9XG5cbiAgYWZ0ZXJFeGFtcGxlKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICB0aGlzLnZhcmlhYmxlTm9kZXMhLmZvckVhY2gobm9kZSA9PiB7XG4gICAgICAgIGNvbnN0IGdyYWRpZW50ID0gZ3JhZGllbnRBcnJheU1hcC5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICBjb25zdCBhY2N1bXVsYXRlZEdyYWRpZW50ID0gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLnNldChcbiAgICAgICAgICAgIG5vZGUub3V0cHV0LCBrZWVwKG1hdGguYWRkKGdyYWRpZW50LCBhY2N1bXVsYXRlZEdyYWRpZW50KSkpO1xuICAgICAgICBhY2N1bXVsYXRlZEdyYWRpZW50LmRpc3Bvc2UoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgYWZ0ZXJCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIHRoaXMudmFyaWFibGVOb2RlcyEuZm9yRWFjaChub2RlID0+IHtcbiAgICAgICAgY29uc3Qgb2xkVmFyaWFibGUgPSBhY3RpdmF0aW9uQXJyYXlNYXAuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgY29uc3QgZ3JhZGllbnQgPSB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLmdldChub2RlLm91dHB1dCk7XG4gICAgICAgIGNvbnN0IHZhcmlhYmxlID1cbiAgICAgICAgICAgIG1hdGguc2NhbGVkQXJyYXlBZGQodGhpcy5jISwgZ3JhZGllbnQsIHRoaXMub25lISwgb2xkVmFyaWFibGUpO1xuICAgICAgICBhY3RpdmF0aW9uQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBrZWVwKHZhcmlhYmxlKSk7XG4gICAgICAgIG5vZGUuZGF0YSA9IHZhcmlhYmxlO1xuXG4gICAgICAgIG9sZFZhcmlhYmxlLmRpc3Bvc2UoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cy5kaXNwb3NlKCk7XG4gICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cyA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5jICE9IG51bGwpIHtcbiAgICAgIHRoaXMuYy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHRoaXMub25lLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIHNldExlYXJuaW5nUmF0ZShsZWFybmluZ1JhdGU6IG51bWJlcikge1xuICAgIHRoaXMubGVhcm5pbmdSYXRlID0gbGVhcm5pbmdSYXRlO1xuICB9XG5cbiAgcHJpdmF0ZSB2YXJpYWJsZUdyYWRpZW50cyA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICBwcml2YXRlIHByZXZCYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBvbmUgPSBTY2FsYXIubmV3KDEpO1xuICBwcml2YXRlIGM6IFNjYWxhcjtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogVGVuc29yQXJyYXlNYXAgaXMgYW4gaW50ZXJuYWwgbWFwIGZyb20gVGVuc29yIElEcyB0byBOREFycmF5cy4gU2luY2UgTkRBcnJheXNcbiAqIGNhbiBiZSBiYWNrZWQgYnkgV2ViR0wgdGV4dHVyZXMsIHRoZSBUZW5zb3JBcnJheU1hcCBpcyBvbmx5IHVzZWQgaW5zaWRlIG9mIGFcbiAqIFNlc3Npb24uXG4gKi9cbmV4cG9ydCBjbGFzcyBUZW5zb3JBcnJheU1hcCB7XG4gIC8qKlxuICAgKiBBZGQgb3IgcmVwbGFjZSBhbiBlbnRyeSBpbiB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcGFyYW0gYXJyYXkgVGhlIE5EQXJyYXkgdmFsdWUsIGNhbiBiZSBudWxsLlxuICAgKi9cbiAgc2V0KHRlbnNvcjogVGVuc29yLCBhcnJheTogTkRBcnJheXxudWxsKSB7XG4gICAgdGhpcy5kaWN0W3RlbnNvci5pZF0gPSBhcnJheTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBOREFycmF5IGFzc29jaWF0ZWQgd2l0aCB0aGUgcHJvdmlkZWQgdGVuc29yLiBXaWxsIHRocm93IGFuXG4gICAqIGV4Y2VwdGlvbiBpZiB0aGUgdGVuc29yIGlzIG5vdCBhIGtleSBpbiB0aGUgbWFwLCBvciBpZiB0aGUgYXNzb2NpYXRlZFxuICAgKiBOREFycmF5IGlzIG51bGwuXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciBrZXkuXG4gICAqIEBwYXJhbSBza2lwQ2hlY2tzIEZhbHNlIGJ5IGRlZmF1bHQuIElmIHRydWUgd2lsbCBza2lwIGFsbCBjaGVja3MuXG4gICAqIEByZXR1cm4gVGhlIE5EQXJyYXkgYXNzb2NpYXRlZCB3aXRoIHRoZSB0ZW5zb3IuXG4gICAqL1xuICBnZXQodGVuc29yOiBUZW5zb3IsIHNraXBDaGVja3MgPSBmYWxzZSk6IE5EQXJyYXkge1xuICAgIGlmICghc2tpcENoZWNrcyAmJiB0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RlbnNvciAnICsgdGVuc29yLmlkICsgJyBub3QgaW4gYXJyYXkgbWFwLicpO1xuICAgIH1cbiAgICBjb25zdCBuZGEgPSB0aGlzLmRpY3RbdGVuc29yLmlkXTtcbiAgICBpZiAoIXNraXBDaGVja3MgJiYgbmRhID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RlbnNvciAnICsgdGVuc29yLmlkICsgJyBoYXMgbnVsbCBhcnJheS4nKTtcbiAgICB9XG4gICAgcmV0dXJuIG5kYSE7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRlbnNvci9OREFycmF5IHBhaXIgZnJvbSB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKi9cbiAgZGVsZXRlKHRlbnNvcjogVGVuc29yKSB7XG4gICAgZGVsZXRlIHRoaXMuZGljdFt0ZW5zb3IuaWRdO1xuICB9XG5cbiAgZGlzcG9zZUFycmF5KHRlbnNvcjogVGVuc29yKSB7XG4gICAgaWYgKHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgbmRhID0gdGhpcy5kaWN0W3RlbnNvci5pZF07XG4gICAgaWYgKG5kYSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBuZGEuZGlzcG9zZSgpO1xuICAgIHRoaXMuZGljdFt0ZW5zb3IuaWRdID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIFRoZSBudW1iZXIgb2YgdGVuc29yL05EQXJyYXkgcGFpcnMgaW4gdGhlIG1hcC5cbiAgICovXG4gIHNpemUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5kaWN0KS5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogSXRlcmF0ZSBvdmVyIGFsbCBjb250YWluZWQgTkRBcnJheSB2YWx1ZXMgYW5kIGRpc3Bvc2UgdGhlbS5cbiAgICovXG4gIGRpc3Bvc2UoKSB7XG4gICAgT2JqZWN0LmtleXModGhpcy5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICAgIGNvbnN0IG5kYSA9IHRoaXMuZGljdFsrdGVuc29ySURdO1xuICAgICAgaWYgKG5kYSkge1xuICAgICAgICBuZGEuZGlzcG9zZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuZGljdCA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRlc3RzIHRvIHNlZSBpZiBhIHRlbnNvciBoYXMgYSBudWxsIGFzc29jaWF0ZWQgd2l0aCBpdC4gVGhyb3dzXG4gICAqIGlmIHRoZSB0ZW5zb3IgaXMgbm90IGEga2V5IGluIHRoZSBtYXAuXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciBrZXkuXG4gICAqIEByZXR1cm4gVHJ1ZSBpZiB0aGUgYXNzb2NpYXRlZCBOREFycmF5IGlzIG51bGwsIGVsc2UgRmFsc2UuXG4gICAqL1xuICBoYXNOdWxsQXJyYXkodGVuc29yOiBUZW5zb3IpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5kaWN0W3RlbnNvci5pZF0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgbm90IGluIGFycmF5IG1hcC4nKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSBudWxsO1xuICB9XG5cbiAgcHJpdmF0ZSBkaWN0OiB7W3RlbnNvcklEOiBudW1iZXJdOiBOREFycmF5IHwgbnVsbH0gPSB7fTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IHR5cGUgVmVjdG9yID0gbnVtYmVyW10gfCBGbG9hdDY0QXJyYXkgfCBGbG9hdDMyQXJyYXkgfCBJbnQzMkFycmF5IHxcbiAgICBJbnQ4QXJyYXkgfCBJbnQxNkFycmF5O1xuXG4vKiogU2h1ZmZsZXMgdGhlIGFycmF5IHVzaW5nIEZpc2hlci1ZYXRlcyBhbGdvcml0aG0uICovXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gc2h1ZmZsZShhcnJheTogYW55W118VWludDMyQXJyYXl8SW50MzJBcnJheXxcbiAgICAgICAgICAgICAgICAgICAgICAgIEZsb2F0MzJBcnJheSk6IHZvaWQge1xuICBsZXQgY291bnRlciA9IGFycmF5Lmxlbmd0aDtcbiAgbGV0IHRlbXAgPSAwO1xuICBsZXQgaW5kZXggPSAwO1xuICAvLyBXaGlsZSB0aGVyZSBhcmUgZWxlbWVudHMgaW4gdGhlIGFycmF5XG4gIHdoaWxlIChjb3VudGVyID4gMCkge1xuICAgIC8vIFBpY2sgYSByYW5kb20gaW5kZXhcbiAgICBpbmRleCA9IChNYXRoLnJhbmRvbSgpICogY291bnRlcikgfCAwO1xuICAgIC8vIERlY3JlYXNlIGNvdW50ZXIgYnkgMVxuICAgIGNvdW50ZXItLTtcbiAgICAvLyBBbmQgc3dhcCB0aGUgbGFzdCBlbGVtZW50IHdpdGggaXRcbiAgICB0ZW1wID0gYXJyYXlbY291bnRlcl07XG4gICAgYXJyYXlbY291bnRlcl0gPSBhcnJheVtpbmRleF07XG4gICAgYXJyYXlbaW5kZXhdID0gdGVtcDtcbiAgfVxufVxuXG4vKiogQ2xhbXBzIGEgdmFsdWUgdG8gYSBzcGVjaWZpZWQgcmFuZ2UuICovXG5leHBvcnQgZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIHg6IG51bWJlciwgbWF4OiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbih4LCBtYXgpKTtcbn1cblxuLyoqIFJldHVybnMgYSBzYW1wbGUgZnJvbSBhIHVuaWZvcm0gW2EsIGJdIGRpc3RyaWJ1dGlvbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kVW5pZm9ybShhOiBudW1iZXIsIGI6IG51bWJlcikge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKSAqIChiIC0gYSkgKyBhO1xufVxuXG4vKipcbiAqIFNhbXBsZXMgZnJvbSBhIGdhdXNzaWFuIGRpc3RyaWJ1dGlvbi5cbiAqXG4gKiBAcGFyYW0gbWVhbiBUaGUgbWVhbi4gRGVmYXVsdCBpcyAwLlxuICogQHBhcmFtIHN0ZERldiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uLiBEZWZhdWx0IGlzIDEuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kR2F1c3MobWVhbiA9IDAsIHN0ZERldiA9IDEsIHRydW5jYXRlZCA9IGZhbHNlKTogbnVtYmVyIHtcbiAgbGV0IHYxOiBudW1iZXIsIHYyOiBudW1iZXIsIHM6IG51bWJlcjtcbiAgZG8ge1xuICAgIHYxID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHYyID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHMgPSB2MSAqIHYxICsgdjIgKiB2MjtcbiAgfSB3aGlsZSAocyA+IDEpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IE1hdGguc3FydCgtMiAqIE1hdGgubG9nKHMpIC8gcykgKiB2MTtcbiAgaWYgKHRydW5jYXRlZCAmJiByZXN1bHQgPiAyKSB7XG4gICAgcmV0dXJuIHJhbmRHYXVzcyhtZWFuLCBzdGREZXYsIHRydWUpO1xuICB9XG4gIHJldHVybiBtZWFuICsgc3RkRGV2ICogcmVzdWx0O1xufVxuXG4vKiogUmV0dXJucyBzcXVhcmVkIGV1Y2xlZGlhbiBkaXN0YW5jZSBiZXR3ZWVuIHR3byB2ZWN0b3JzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3RTcXVhcmVkKGE6IFZlY3RvciwgYjogVmVjdG9yKTogbnVtYmVyIHtcbiAgbGV0IHJlc3VsdCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYS5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGRpZmYgPSBhW2ldIC0gYltpXTtcbiAgICByZXN1bHQgKz0gZGlmZiAqIGRpZmY7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydChleHByOiBib29sZWFuLCBtc2c6IHN0cmluZykge1xuICBpZiAoIWV4cHIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgc2hhcGVBOiBudW1iZXJbXSwgc2hhcGVCOiBudW1iZXJbXSwgZXJyb3JNZXNzYWdlUHJlZml4ID0gJycpOiB2b2lkIHtcbiAgYXNzZXJ0KFxuICAgICAgYXJyYXlzRXF1YWwoc2hhcGVBLCBzaGFwZUIpLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgYFNoYXBlcyAke3NoYXBlQX0gYW5kICR7c2hhcGVCfSBtdXN0IG1hdGNoYCk7XG59XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKGFycjogYW55W10sIHJldD86IG51bWJlcltdKTogbnVtYmVyW10ge1xuICByZXQgPSAocmV0ID09PSB1bmRlZmluZWQgPyBbXSA6IHJldCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgKytpKSB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoYXJyW2ldKSkge1xuICAgICAgZmxhdHRlbihhcnJbaV0sIHJldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldC5wdXNoKGFycltpXSk7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQ7XG59XG5cbmV4cG9ydCB0eXBlIEFycmF5RGF0YSA9IG51bWJlcnxudW1iZXJbXXxudW1iZXJbXVtdfG51bWJlcltdW11bXXxudW1iZXJbXVtdW11bXTtcblxuZXhwb3J0IGZ1bmN0aW9uIGluZmVyU2hhcGUoYXJyOiBBcnJheURhdGEpOiBudW1iZXJbXSB7XG4gIGNvbnN0IHNoYXBlOiBudW1iZXJbXSA9IFtdO1xuICB3aGlsZSAoYXJyIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICBzaGFwZS5wdXNoKGFyci5sZW5ndGgpO1xuICAgIGFyciA9IGFyclswXTtcbiAgfVxuICByZXR1cm4gc2hhcGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaXplRnJvbVNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IG51bWJlciB7XG4gIGlmIChzaGFwZS5sZW5ndGggPT09IDApIHtcbiAgICAvLyBTY2FsYXIuXG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgbGV0IHNpemUgPSBzaGFwZVswXTtcbiAgZm9yIChsZXQgaSA9IDE7IGkgPCBzaGFwZS5sZW5ndGg7IGkrKykge1xuICAgIHNpemUgKj0gc2hhcGVbaV07XG4gIH1cbiAgcmV0dXJuIHNpemU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1NjYWxhclNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IGJvb2xlYW4ge1xuICByZXR1cm4gc2hhcGUubGVuZ3RoID09PSAwO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gYXJyYXlzRXF1YWwobjE6IGFueVtdfEZsb2F0MzJBcnJheSwgbjI6IGFueVtdfEZsb2F0MzJBcnJheSkge1xuICBpZiAobjEubGVuZ3RoICE9PSBuMi5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuMS5sZW5ndGg7IGkrKykge1xuICAgIGlmIChuMVtpXSAhPT0gbjJbaV0pIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0ludChhOiBudW1iZXIpOiBib29sZWFuIHtcbiAgcmV0dXJuIGEgJSAxID09PSAwO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdGFuaCh4OiBudW1iZXIpOiBudW1iZXIge1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIGlmICgoTWF0aCBhcyBhbnkpLnRhbmggIT0gbnVsbCkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKE1hdGggYXMgYW55KS50YW5oKHgpO1xuICB9XG4gIGlmICh4ID09PSBJbmZpbml0eSkge1xuICAgIHJldHVybiAxO1xuICB9IGVsc2UgaWYgKHggPT09IC1JbmZpbml0eSkge1xuICAgIHJldHVybiAtMTtcbiAgfSBlbHNlIHtcbiAgICBjb25zdCBlMnggPSBNYXRoLmV4cCgyICogeCk7XG4gICAgcmV0dXJuIChlMnggLSAxKSAvIChlMnggKyAxKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2l6ZVRvU3F1YXJpc2hTaGFwZShzaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgZm9yIChsZXQgYSA9IE1hdGguZmxvb3IoTWF0aC5zcXJ0KHNpemUpKTsgYSA+IDE7IC0tYSkge1xuICAgIGlmIChzaXplICUgYSA9PT0gMCkge1xuICAgICAgcmV0dXJuIFthLCBzaXplIC8gYV07XG4gICAgfVxuICB9XG4gIHJldHVybiBbMSwgc2l6ZV07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTaHVmZmxlZEluZGljZXMobjogbnVtYmVyKTogVWludDMyQXJyYXkge1xuICBjb25zdCBzaHVmZmxlZEluZGljZXMgPSBuZXcgVWludDMyQXJyYXkobik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgc2h1ZmZsZWRJbmRpY2VzW2ldID0gaTtcbiAgfVxuICBzaHVmZmxlKHNodWZmbGVkSW5kaWNlcyk7XG4gIHJldHVybiBzaHVmZmxlZEluZGljZXM7XG59XG4iXX0= diff --git a/demos/imagenet/imagenet-demo.html b/demos/imagenet/imagenet-demo.html new file mode 100644 index 0000000000..77b7029bb4 --- /dev/null +++ b/demos/imagenet/imagenet-demo.html @@ -0,0 +1,49 @@ + + + + + + + + + + LearnJS Imagenet Demo + + + + + + + + + + diff --git a/demos/imagenet/imagenet-demo.ts b/demos/imagenet/imagenet-demo.ts new file mode 100644 index 0000000000..57d59bca03 --- /dev/null +++ b/demos/imagenet/imagenet-demo.ts @@ -0,0 +1,220 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import '../demo-header'; +import '../demo-footer'; +import {Array3D, gpgpu_util, GPGPUContext, NDArray, NDArrayMathCPU, NDArrayMathGPU} from '../deeplearnjs'; +import * as imagenet_classes from '../models/imagenet_classes'; +import * as imagenet_util from '../models/imagenet_util'; +import {SqueezeNet} from '../models/squeezenet'; +// tslint:disable-next-line:no-unused-variable +import {PolymerElement, PolymerHTMLElement} from '../polymer-spec'; + +// tslint:disable-next-line:variable-name +export const ImagenetDemoPolymer = PolymerElement({ + is: 'imagenet-demo', + properties: { + layerNames: Array, + selectedLayerName: String, + inputNames: Array, + selectedInputName: String + } +}); + +/** + * NOTE: To use the webcam without SSL, use the chrome flag: + * --unsafely-treat-insecure-origin-as-secure=\ + * http://localhost:5432 + */ + +const NUM_CLASSES = 1000; +const IMAGE_SIZE = 227; +const TOP_K_CLASSES = 5; + +const INPUT_NAMES = ['cat', 'dog1', 'dog2', 'beerbottle', 'piano', 'saxophone']; +export class ImagenetDemo extends ImagenetDemoPolymer { + private variables: {[varName: string]: NDArray}; + + private math: NDArrayMathGPU; + private mathCPU: NDArrayMathCPU; + private gl: WebGLRenderingContext; + private gpgpu: GPGPUContext; + private renderGrayscaleChannelsCollageShader: WebGLShader; + + private squeezeNet: SqueezeNet; + + private webcamVideoElement: HTMLVideoElement; + private staticImgElement: HTMLImageElement; + + private layerNames: string[]; + private selectedLayerName: string; + private inputNames: string[]; + private selectedInputName: string; + + private inferenceCanvas: HTMLCanvasElement; + + ready() { + this.inferenceCanvas = + this.querySelector('#inference-canvas') as HTMLCanvasElement; + this.staticImgElement = + this.querySelector('#staticImg') as HTMLImageElement; + this.webcamVideoElement = + this.querySelector('#webcamVideo') as HTMLVideoElement; + + this.layerNames = []; + this.selectedLayerName = 'conv_1'; + + const inputDropdown = this.querySelector('#input-dropdown')!; + // tslint:disable-next-line:no-any + inputDropdown.addEventListener('iron-activate', (event: any) => { + const selectedInputName = event.detail.selected; + if (selectedInputName === 'webcam') { + this.webcamVideoElement.style.display = ''; + this.staticImgElement.style.display = 'none'; + } else { + this.webcamVideoElement.style.display = 'none'; + this.staticImgElement.style.display = ''; + } + this.staticImgElement.src = 'images/' + event.detail.selected + '.jpg'; + }); + + if (navigator.getUserMedia) { + navigator.getUserMedia( + {video: true}, + (stream) => { + this.webcamVideoElement.src = window.URL.createObjectURL(stream); + this.initWithWebcam(); + }, + (error) => { + console.log(error); + this.initWithoutWebcam(); + }); + } else { + this.initWithoutWebcam(); + } + + this.gl = gpgpu_util.createWebGLContext(this.inferenceCanvas); + this.gpgpu = new GPGPUContext(this.gl); + this.math = new NDArrayMathGPU(this.gpgpu); + this.mathCPU = new NDArrayMathCPU(); + + this.squeezeNet = new SqueezeNet(this.gpgpu, this.math); + this.squeezeNet.loadVariables().then(() => { + requestAnimationFrame(() => this.animate()); + }); + + this.renderGrayscaleChannelsCollageShader = + imagenet_util.getRenderGrayscaleChannelsCollageShader(this.gpgpu); + } + + private initWithoutWebcam() { + this.inputNames = INPUT_NAMES; + this.selectedInputName = 'cat'; + this.staticImgElement.src = 'images/cat.jpg'; + this.webcamVideoElement.style.display = 'none'; + this.staticImgElement.style.display = ''; + + if (location.protocol !== 'https:') { + (this.querySelector('#ssl-message') as HTMLElement).style.display = + 'block'; + } + + (this.querySelector('#webcam-message') as HTMLElement).style.display = + 'block'; + } + + private initWithWebcam() { + const inputNames = INPUT_NAMES.slice(); + inputNames.unshift('webcam'); + this.inputNames = inputNames; + this.selectedInputName = 'webcam'; + } + + private animate() { + const startTime = performance.now(); + + const isWebcam = this.selectedInputName === 'webcam'; + + const canvasTextureShape: [number, number] = [IMAGE_SIZE, IMAGE_SIZE]; + const canvasTexture = + this.math.getTextureManager().acquireTexture(canvasTextureShape); + + const element = isWebcam ? this.webcamVideoElement : this.staticImgElement; + this.gpgpu.uploadPixelDataToTexture(canvasTexture, element); + + this.math.scope((keep, track) => { + const preprocessedInput = + track(this.squeezeNet.preprocessColorTextureToArray3D( + canvasTexture, canvasTextureShape)); + + const inferenceResult = this.squeezeNet.infer(preprocessedInput); + const namedActivations = inferenceResult.namedActivations; + + this.layerNames = Object.keys(namedActivations); + this.layerNames.forEach(layerName => track(namedActivations[layerName])); + + const topClassesToProbability = + this.squeezeNet.getTopKClasses(inferenceResult.logits, TOP_K_CLASSES); + + let count = 0; + for (const className in topClassesToProbability) { + if (!(className in topClassesToProbability)) { + continue; + } + document.getElementById('class' + count)!.innerHTML = className; + document.getElementById('prob' + count)!.innerHTML = + '' + Math.floor(1000 * topClassesToProbability[className]) / 1000; + count++; + } + + const endTime = performance.now(); + + (this.querySelector('#totalTime') as HTMLDivElement).innerHTML = + 'last inference time: ' + + Math.floor(1000 * (endTime - startTime)) / 1000 + 'ms'; + + // Render activations. + const activationNDArray = namedActivations[this.selectedLayerName]; + + // Compute max and min per channel for normalization. + const maxValues = this.math.maxPool( + activationNDArray, activationNDArray.shape[1], + activationNDArray.shape[1], 0); + const minValues = this.math.minPool( + activationNDArray, activationNDArray.shape[1], + activationNDArray.shape[1], 0); + + // Logically resize the rendering canvas. The displayed width is fixed. + const imagesPerRow = Math.ceil(Math.sqrt(activationNDArray.shape[2])); + const numRows = Math.ceil(activationNDArray.shape[2] / imagesPerRow); + this.inferenceCanvas.width = imagesPerRow * activationNDArray.shape[0]; + this.inferenceCanvas.height = numRows * activationNDArray.shape[0]; + + imagenet_util.renderGrayscaleChannelsCollage( + this.gpgpu, this.renderGrayscaleChannelsCollageShader, + activationNDArray.getTexture(), minValues.getTexture(), + maxValues.getTexture(), activationNDArray.getTextureShapeRC(), + activationNDArray.shape[0], activationNDArray.shape[2], + this.inferenceCanvas.width, numRows); + }); + + this.math.getTextureManager().releaseTexture( + canvasTexture, canvasTextureShape); + + requestAnimationFrame(() => this.animate()); + } +} + +document.registerElement(ImagenetDemo.prototype.is, ImagenetDemo); diff --git a/demos/imagenet/imagenet.html b/demos/imagenet/imagenet.html new file mode 100644 index 0000000000..9e9edc623e --- /dev/null +++ b/demos/imagenet/imagenet.html @@ -0,0 +1,114 @@ + + + + + + + + + + diff --git a/demos/imagenet/images/beerbottle.jpg b/demos/imagenet/images/beerbottle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..08e64feaa3e731e3b62660af6ddf390137e259f7 GIT binary patch literal 4257 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 F{Xe|m%K-oY literal 0 HcmV?d00001 diff --git a/demos/imagenet/images/cat.jpg b/demos/imagenet/images/cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b506747111f9c7d35851aa8b3f1eb437b03a1358 GIT binary patch literal 24557 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 literal 0 HcmV?d00001 diff --git a/demos/imagenet/images/dog1.jpg b/demos/imagenet/images/dog1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eef50c9263dac05f2d377520ab09a64983a95138 GIT binary patch literal 12279 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 A0{{R3 literal 0 HcmV?d00001 diff --git a/demos/imagenet/images/dog2.jpg b/demos/imagenet/images/dog2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..41d3262748293de7deac6cbba1fe27cf32a1337b GIT binary patch literal 14872 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 ZrAmX7Ts3=2fwNv(l_~{l@|7wN|Jgi=)}a6Z literal 0 HcmV?d00001 diff --git a/demos/images/benchmark.png b/demos/images/benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..1527f1eefab78dec927c6b7f930d02950b6270a8 GIT binary patch literal 47293 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 literal 0 HcmV?d00001 diff --git a/demos/images/font-embedding.png b/demos/images/font-embedding.png new file mode 100644 index 0000000000000000000000000000000000000000..08e5b8092ea31299199890bb2ba102f8f4d988b0 GIT binary patch literal 57566 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%0DJSeEI`CqU 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%0A7D!kKZDoP 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 z≠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

Hu$Olup+|lV{v%{sAz1*R|ZI;u*ZGCzS4^P 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 z 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%0AB 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 ~J*b^JD8Ya~Y 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 literal 0 HcmV?d00001 diff --git a/demos/mnist/fully_connected_feed.py b/demos/mnist/fully_connected_feed.py new file mode 100644 index 0000000000..3a67796681 --- /dev/null +++ b/demos/mnist/fully_connected_feed.py @@ -0,0 +1,253 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Trains and Evaluates the MNIST network using a feed dictionary.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# pylint: disable=missing-docstring +import argparse +import os +import sys +import time + +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow as tf + +from tensorflow.examples.tutorials.mnist import input_data +from tensorflow.examples.tutorials.mnist import mnist + +# Basic model parameters as external flags. +FLAGS = None + + +def placeholder_inputs(batch_size): + """Generate placeholder variables to represent the input tensors. + + These placeholders are used as inputs by the rest of the model building + code and will be fed from the downloaded data in the .run() loop, below. + + Args: + batch_size: The batch size will be baked into both placeholders. + + Returns: + images_placeholder: Images placeholder. + labels_placeholder: Labels placeholder. + """ + # Note that the shapes of the placeholders match the shapes of the full + # image and label tensors, except the first dimension is now batch_size + # rather than the full size of the train or test data sets. + images_placeholder = tf.placeholder( + tf.float32, shape=(batch_size, mnist.IMAGE_PIXELS)) + labels_placeholder = tf.placeholder(tf.int32, shape=(batch_size)) + return images_placeholder, labels_placeholder + + +def fill_feed_dict(data_set, images_pl, labels_pl): + """Fills the feed_dict for training the given step. + + A feed_dict takes the form of: + feed_dict = { + : , + .... + } + + Args: + data_set: The set of images and labels, from input_data.read_data_sets() + images_pl: The images placeholder, from placeholder_inputs(). + labels_pl: The labels placeholder, from placeholder_inputs(). + + Returns: + feed_dict: The feed dictionary mapping from placeholders to values. + """ + # Create the feed_dict for the placeholders filled with the next + # `batch size` examples. + images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size, + FLAGS.fake_data) + feed_dict = { + images_pl: images_feed, + labels_pl: labels_feed, + } + return feed_dict + + +def do_eval(sess, eval_correct, images_placeholder, labels_placeholder, + data_set): + """Runs one evaluation against the full epoch of data. + + Args: + sess: The session in which the model has been trained. + eval_correct: The Tensor that returns the number of correct predictions. + images_placeholder: The images placeholder. + labels_placeholder: The labels placeholder. + data_set: The set of images and labels to evaluate, from + input_data.read_data_sets(). + """ + # And run one epoch of eval. + true_count = 0 # Counts the number of correct predictions. + steps_per_epoch = data_set.num_examples // FLAGS.batch_size + num_examples = steps_per_epoch * FLAGS.batch_size + for step in xrange(steps_per_epoch): + feed_dict = fill_feed_dict(data_set, images_placeholder, labels_placeholder) + true_count += sess.run(eval_correct, feed_dict=feed_dict) + precision = float(true_count) / num_examples + print(' Num examples: %d Num correct: %d Precision @ 1: %0.04f' % + (num_examples, true_count, precision)) + + +def run_training(): + """Train MNIST for a number of steps.""" + # Get the sets of images and labels for training, validation, and + # test on MNIST. + data_sets = input_data.read_data_sets(FLAGS.input_data_dir, FLAGS.fake_data) + + # Tell TensorFlow that the model will be built into the default Graph. + with tf.Graph().as_default(): + # Generate placeholders for the images and labels. + images_placeholder, labels_placeholder = placeholder_inputs( + FLAGS.batch_size) + + # Build a Graph that computes predictions from the inference model. + logits = mnist.inference(images_placeholder, FLAGS.hidden1, FLAGS.hidden2) + + # Add to the Graph the Ops for loss calculation. + loss = mnist.loss(logits, labels_placeholder) + + # Add to the Graph the Ops that calculate and apply gradients. + train_op = mnist.training(loss, FLAGS.learning_rate) + + # Add the Op to compare the logits to the labels during evaluation. + eval_correct = mnist.evaluation(logits, labels_placeholder) + + # Build the summary Tensor based on the TF collection of Summaries. + summary = tf.summary.merge_all() + + # Add the variable initializer Op. + init = tf.global_variables_initializer() + + # Create a saver for writing training checkpoints. + saver = tf.train.Saver() + + # Create a session for running Ops on the Graph. + sess = tf.Session() + + # Instantiate a SummaryWriter to output summaries and the Graph. + summary_writer = tf.summary.FileWriter(FLAGS.log_dir, sess.graph) + + # And then after everything is built: + + # Run the Op to initialize the variables. + sess.run(init) + + # Start the training loop. + for step in xrange(FLAGS.max_steps): + start_time = time.time() + + # Fill a feed dictionary with the actual set of images and labels + # for this particular training step. + feed_dict = fill_feed_dict(data_sets.train, images_placeholder, + labels_placeholder) + + # Run one step of the model. The return values are the activations + # from the `train_op` (which is discarded) and the `loss` Op. To + # inspect the values of your Ops or variables, you may include them + # in the list passed to sess.run() and the value tensors will be + # returned in the tuple from the call. + _, loss_value = sess.run([train_op, loss], feed_dict=feed_dict) + + duration = time.time() - start_time + + # Write the summaries and print an overview fairly often. + if step % 100 == 0: + # Print status to stdout. + print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration)) + # Update the events file. + summary_str = sess.run(summary, feed_dict=feed_dict) + summary_writer.add_summary(summary_str, step) + summary_writer.flush() + + # Save a checkpoint and evaluate the model periodically. + if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps: + checkpoint_file = os.path.join(FLAGS.log_dir, 'model.ckpt') + saver.save(sess, checkpoint_file, global_step=step) + # Evaluate against the training set. + print('Training Data Eval:') + do_eval(sess, eval_correct, images_placeholder, labels_placeholder, + data_sets.train) + # Evaluate against the validation set. + print('Validation Data Eval:') + do_eval(sess, eval_correct, images_placeholder, labels_placeholder, + data_sets.validation) + # Evaluate against the test set. + print('Test Data Eval:') + do_eval(sess, eval_correct, images_placeholder, labels_placeholder, + data_sets.test) + + +def main(_): + if tf.gfile.Exists(FLAGS.log_dir): + tf.gfile.DeleteRecursively(FLAGS.log_dir) + tf.gfile.MakeDirs(FLAGS.log_dir) + run_training() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--learning_rate', + type=float, + default=0.01, + help='Initial learning rate.') + parser.add_argument( + '--max_steps', + type=int, + default=2000, + help='Number of steps to run trainer.') + parser.add_argument( + '--hidden1', + type=int, + default=128, + help='Number of units in hidden layer 1.') + parser.add_argument( + '--hidden2', + type=int, + default=32, + help='Number of units in hidden layer 2.') + parser.add_argument( + '--batch_size', + type=int, + default=100, + help='Batch size. Must divide evenly into the dataset sizes.') + parser.add_argument( + '--input_data_dir', + type=str, + default=os.path.join( + os.getenv('TEST_TMPDIR', '/tmp'), 'tensorflow/mnist/input_data'), + help='Directory to put the input data.') + parser.add_argument( + '--log_dir', + type=str, + default=os.path.join( + os.getenv('TEST_TMPDIR', '/tmp'), + 'tensorflow/mnist/logs/fully_connected_feed'), + help='Directory to put the log data.') + parser.add_argument( + '--fake_data', + default=False, + help='If true, uses fake data for unit testing.', + action='store_true') + + FLAGS, unparsed = parser.parse_known_args() + tf.app.run(main=main, argv=[sys.argv[0]] + unparsed) diff --git a/demos/mnist/hidden1_biases b/demos/mnist/hidden1_biases new file mode 100644 index 0000000000..93296ad8d3 --- /dev/null +++ b/demos/mnist/hidden1_biases @@ -0,0 +1,3 @@ +ÞžI¼ð*Ò<ú ;Ÿ?9'¤=»Ç÷ ;X„÷<ªþ=ÇH:=#ñ ;ùYè<ºy6=Òú=±Ô¸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

gj+na*6&WW}cyuxDZ^Y<8IlcYPeV=t=p&<{sf{ zPX461BYx10t^x2SJcHDg2Vwi9i7=zCltx`z!|Jn|#i!B?(4D(LbN7YgN}W79l3hb% zg7u;Qcn;rCg!t=)IgMRn4QmxuxIB+Cs9cZ<^TK?g*VKcOCf$a=TWzTQygr*g^C;Ka ze-uI`1qP_9E-M-p3yw*vaO1%$x;D%l%bQMe>P5knQq%~qnyuKM2csD09E)o{sInGd61mk^|Azf{7ioCK5 zf`cAW>qt}PRcgyzuK2TA4@_vkMLvbRHDkU;nxH8BfBn4_3w{So=#qPsJHTc|6t6B)s;@<~wLvrDAk($Ckua6~n?mE4Te^XODp8i=o|(j8xGmUn$U9Z#FV zjbQztVKSL*cxc1J1@WjWpSlaB<-;K0G}2OhSSZyutj$vU0f#x zAr8&l!UhMn&R>GY@A<_&HCQN$J1xgey{?08Z8KoX1qr6A`xCZnpQ49D*TM+C6>gZi z;{CequxVwuNT%f>zdI-pdqd>KiuW#2{gnxrsj>^yWZJm%j#bdIDHvCEaBND@Aa*hN z4DmZ&LuGa%dti8ppCvM)3*UyYjlw*By)%wQeJU5NHc`ZRyTT~jW-_R%iOB7i1+zad z;+AjnWHV#`XS!zUQ^r9OH=$3Xg~t+5!wyey0xO9c zXiB*RN!QasI`x}4R(~KXS~7#5^6wH@=S>!^TRxTbxOBmPSrRzUD3<~ge}delIzIey zH>qTYvj{nNED?NhV`rZS182coc0>zrP2R;H@7qb5_m9DPp*5jAwg9p|yrd0kjoeV1 zRz5jBmW7tyrs>`OnEXNtrNTI@IRMP$+il=}E#=MjuHwEO*}x<(-=~*L&w%{4RCM_j zMc*t}(sWZPYW-`Ke!%jwHg}Xjm{dg+uTR#~8K6nXJyT`NGT~)NPu8mHo zgs|O}hxi{jlo@1wg~pHLQLc0ZQ$Bx*yEDI(pZ9VL9Xn!7S>|c%jlelowD;rB{T#}o zE+@jq{v59PoFi+MS|WHPox$mP70HfErRf~emiv<2&-o^zGvLh9twx~U=S(s4nZWKu z`Z0|a!_n=?S9-f=1pU2vkEWVE0Uxn1t8sM@dg>h0Tm2Ze7;J}M_6E#RaFCx%F~B2W z%d(Oy02_aDh9WhLc$&asZG)K2)@)`O-UnqS$<$|ud{T@r8=B#Y1GD!)7x$6xESJV@ zZkp`Z{hQ+3uU+{@Srw*#-i{6Uy`Pi*o=NFWx%_rx6Wl91lYLP;z%9|g3YY6bQEI0- zO02X&`~H0VFIpb@w{p-hb2h#RE2kKnh49d%l6$Ap0;xy;fNx0&j9$6`re~Hy*ik8# z?P>$RjW6@PkM2>u>q;~X+C?Xx#1T8tON$rX<&KG*&4G& z_-nTTUJ?EV6J~V7vT15eM&b{xZhj1l3MN2|_9NQ+ZI9pvR>zF8*Hkj^j(F$pfo#u< zD4`3W2`L`moOC40L^ZQrc)17-_-6Q)S2zq%X(sSli$}wrk(W7$%cA${o;a)FCe*AB z;sbY^;VFYas+gpM>Gw85OF<^@RO-k&Is;+S#|HXOb~c~;sGMrow$bq?FS+MS^jNOI ztm^9;hsvMd)5ED2>}=*8$UbU~(U*Qx)!0mMvoJ-!{$cF)4t-X6!;d=m6p4}-rqYYK z!c0+~LLY82v6r1K91%JN)$9e#5tI=tWvb|-`V?68sl};nW*{tC?~3na!^zfFhqZgG zWh0;WK=!?0{>jPP+)$%oxS`LKHND#fOYL zGXD8E7Pr>5g6yn?Y=P1s`g$!Nx2%)G(Q?8aDnS)@svSnZ^eDFF3derVc+9hl->D*9 z62rSrLeQ;iG~iqVuX4H;lpPD8((?u9CQ(eUTNkp*+I$!p_1o#5yFST>so+232fR^{ zz{C2L3V&plVayCUTARBMmhBf9AE!D+hDn~ZTA7F2>Uzw4(=N`-ZYha-H?ar075w-^ z+xZPzSycV~mssb?VZOj{49YbeqLqK-VM9Oycm$TPcFUn?{H7DmTFz!%WCazhQsuV| zeMvvgk7hf3Y@lbnH`=ZcviDDpf?AOzJGr}!ira1ZvlITK;*|s0ckLL4y2)U(z#Pv^ zZlvkLZ6W{tHONsCJPT^W@x9oaEp|_X#k&)6M#o+5hrl`Z_!vvg%bmgAQUSxprE%_| zYV2Iz67m@w4b3~P+0-%f*iI!0Y|0d|wpG5gFK#C5lk*WgHs@fy-FGOLOJW&k1@423 z3OiwGM(!<5v}EplI^(Dgo;6mWsPD$y+6Sc{dZ2~q!Dj-TY`Q5mI|H0Qz&v* z9-nW0hYp@f;nTio<1Tk!?sB9YIBkw)de28Q9lI?o!TO+h(Re+!O{>yLiPyua;`e}^ z!_j%`MBc!6Fu8uoV|UpOHf5VFcz(XhiMEF@1#ux+{a(dVx6Gn;tA2h$g(^2G)0X(d z=J+b=FpN;nfjJ(t;cVd`_B%?Rz4Nz2^}~%|{~?uXq&>K)F-~mXkrt>q+{CZ$R)aq$ z;+VpwdUBR5qH&=$++N2_nAQ9aLY}{cJBxbwksF3#)h27s|BxIz^T!U$o`z7YWgWD> zx=bfV-l1TJe{@FY5bB?IXE*zfkX1zx|5I}n1gIOKq_~uG9(a^<72F|>m&RbQj4|H! znTXHNufpno4zRg!9CugXuq{h?Mr)Fl;JZ;jcY5Unm^OJQ&E+jnJ%0qQT$By7T{hxM zYb|yp>M3XPLXC|vxx_8}tIGxk_y~zwE4;OM73^(SqEax#U!r!-utAq@U6zHBRm)lF z*gY82Ep*n~3gF<(HSG8WJ!YWi4>fji=#;Ps2Nc<{XDx?N*TbEQirYu)PF|&%rDo*1 zP=<-t#L|T}##J@gvn`9usm&(@ub&QN$CP4O)Tl)Cf1OBIdo|eS>)Yw+?9Jryu!J`? zH^zgG(^*y8an3Kdn!mZBhX0}TfpP*%&?4b5_i*|sT<^Aw0?rGZv8DZ7dxN>?;t>T* z{?7=~yt}y0m+2Ii=#FJETbc1F0~VW9%kNv8Ld$w}SdWk^`fe!6qHMmAY*0NNf6+_b zHa493s%RQo`VJm`xCaXVts?g(d;0Lb0Q?kHSjL)vBt3UCd!+S-t7whl2RAvRbcG_5 z4K={v8=Lu5VJC2E-zfH-OjyCwVJvQMDi@(mR2$;X$!AZe;Sbi()~#ifa&8J33-9@7 zm4N1Xv_svJT%jkl2&>1~Alp9*B}d%gI_i)?!)RP~wS`iG-oT1>T`2B4Ov|6h3iq{i zy7fUHmQR|;EF5wmE^e79DC zSTbt}Yy4AyFEtg>Ml+l4logPE#7DTiIKQ&saT+`tvkTIMIp%TNB5w3#BO%ACf|dIZ zlZE9|-s^P&(|xcBUrtxX@=C`4({f`n&wOFy%07OV;Jng0dH{DF`$+k=k3layfHiRP z+~V>5bmdhnr=#zS`DLYW{k%L})3;|suPvtrbilA`8}N2BhdJ82Y0_F9VulK!ZJ-04 ze^aryaXK8S>V+wJez>}F1RW?#qUs4 zeR}=nB+0JY3HFj#De5pMaLkd-n(>FW+-#ww@J?8|w39!!PXe93oB~q~;eL547H+@a zgX=;EVe9Eo+*pxAEi?Pz?CnU93K)n+57UJEr9GSBsDW!gdsB?(5t`bc25=KU+PC9i6d`ZK~3t-4B$}bH*2erkNJ`yp zv_UL|N*4_A&%Z^O8mh*gUd{%0?Rt`&w&k zt3Sc5&3+5BUkQBZLRap0E{9PGR_sc)H^{am(TB0EAaijTO8Yy}sYV-tRrr+HNliR_ zc?^2x#A0uz1k|=Jrv!ujG^2Dc{Z}P$MyAO@@ym)hzGdMe@!YDBgLokgxjh zIUU-mewhRw;D`o619)5k1B&D@K(!yX1fJ(^EndRbosJ`Kp>v==_Z2%NB2i)r+btLL|!I}Phb z=Yu#z1C$QB!PnoKY@nweS+!LN{ zLDmI%Hm%Q`Sw74HSa6vCuWlzyZ~g`E4w_PS!AdxP`Z<5x^BZ(;I}4Ab6<9>PG+E5t zMUTsGLP7E@=GwRwEsSF;C95+?f4VO2i6l~~IS%tamr!X)16OnPps-PW3nn?!*}ZQ$ zV#R(#p;P!2222h`jjVncII|HZ3w`~sE0VZhs zz~{#q(C|G6Zn=G(3N3Fk-W{`AA?;=?dV`4wp8HPG)r)3EBXKQtudQ_4pjR(nqk zX6O$gec5zgequczWO5V2COeVK^r5UOQiQXmCEy)-oh$N@!EZ$wEXZIN^^8oQ zPhpPCS2aOob9uuqMOs^R2g2UVup{@41=f5vaE1!3 zdWH==m9G;Se8>=0YXyUbZ#}%4sKIJ(9Vds+x!f76NZ$VEUPyAb#HC(3!k3RO#(vGm zFLvtuTjk}T6`Lq}_hUA$-m?p{@7|<2f7igKaaU+#{6{+Lki}92ANsKC8Ej(1IKkVY zzy>;ogJSCr9D4pZ{95^0tf%RMFC?^C$hcDPOLz~Wo>X?;W;R6RAC#(W?^jH zUNW1WNS?U{!u!@kejVFMD;K3g`hkZOwcsvkO&!ZtXI8`F^Ln6u@+7E9mJ>HUi=v~N z!7s=i=KWiN<1H1~!>l~kzvl=%U)&5T7AMe5aJbm-_Y&rbaL9i#inzpVKJ>~GIuD|1cBdvO>!`0k{Qx)b?e&vO)Pa~C|fTcM}v zF}&nukA|fSS%bMe3#dxr+Eel=cBkNL4v42c-v$fsuh!V=u>v;yv7^S^5hy=7omI3X z3C`nMSboS}xN$FHkwF94u=zGPOUsqK9xlT_<;qZc*$4CYz60ejTk_EH6qd@>r1ul?ol+jL>}bP^mdgU(QI>TMdgc0T?n?B`n(=5yW^rot?50%ht! zbQuvEGj3D{W<2B!98M53U^k`;r3hCZD7u zUUF!W?8^!ZoncLy5p<|aagJ?^spOOyHtcu++$2_FB4=pOU?pMU2Yud%5+EYl^ z=?0X}tK+}tn4?rkW97C%xsb*&kXSe$edPu2Rr)pf5;YFq53PnknH4zIa5St6I0B11 zyWqE}7TY=gJB2h%!6~X?7}UE1H_y^X6(2@rf>$x%?;47e4`lvl_VAy_8nXh8U8p-- zT4b54j_E39xGOFd1oJvNKRCy~xo5|II=tlFo*6Pv=1DXW*Ur$1+)eC2)8x%z;wd7{B2g>1dyz6hvuuB4X*M?~5^_3)D43u{JP zg|P6OoaU2q*wnrfzHPC>3dd$!U>O~l6j+X?|{JHRDwF6JIQ z$+-%d_%(NH;DloyY|PhUo0bdN@LnYfeNhiuKYoZDm$qx4mq!-IUwb0#uYNJivU6lzpp7BxjJfxS?1+_t59s5a~s7{B(XF4w1& zvib$wd_RyTymG*avo=GEq6apm>ap^x({MxbUIt@lqT-t;BzT%IxL)wqWf(BO!wT%= zS6A4l;0S6iv#@xJ1#9tnDH>+m2;ZK@vi=3ajVJdyR?QmEQXJDEXk8~{j@{1gb+4kc zn>8tQa~t&*F9PF$QW~^89z|hK`LDuG@aFH`;NZVmaJyQuU+t=Nt3jGnAIyO^>k@ut zb}1Z;2^2gH(@{LB2o}dx;K0et*s$*dAm@k%rmM%ZP0hkRXMQY%+?m0)7KU)YcQ?~s z?*<(9RvL|$4ucEji?}(OLou`Z0>pl+q>>I7=+@1{Q3;@PiO=<79#=12VCWJjfemB8}XF1Hk1W`!{O*;t$^ zf0}tcI?mK%&tO`cGRuFEMEi#|;8Ra6mbhsQicGU;_#|)0HPB?~V7Ff|P7Vg}yZe#XS$b5e|x`lcUUZPg{!!XCZg?HO{8sz8o(67H; zu+MKQr#X5wU;ptaNp8Cc5ij&{ufr{>J?BYAb#l;baEW_Y@v*XAcMnD1n+HB;Jg{qx z3iftZ^YT4~koIpPbBo|%yICkZ+1f9Ti57vcZVgBt64UV-5oC?e<1=03xSsR(iCsyA zDcU}4-+~{a1dXFKxjqCYzFy2_m4w5`(sfL?OL!;Ua*;kunc%zvD?Tu`4&o%QLHO6D zoZ-&<+#v@`+}b}DN{nfs+~_-|7dd@#lF7rwsPkJBzkXFrpWoZ-xB$0+4)dO2_iM zAmR2;J~%9zCeN}4S1o}XBQWMzuHdMXZwLQ*df20}80UZX!Wv5%2j>ks6!+#CY|VTM z(E+bXGQoq6AI=^35=>Nsj#; zHc(u!;y7*l*g^##(=kWO9~<)f_)hc1IQi&8Zg5}(^)D0Nr;_#I^Rm0VwEtRGbiR!@ z86VHW2G3=YO|$TM*L;zgdKI_%)_7*0Be3uX*rG~+BCmAoI$bvJrhOM=*}9wwaP2t< zd))>4X@kHn82*C)@of$~mkEUzabi-Ok__{HZh`&LBGj&0$z-dS%xwyocZ zd4ks>Y{q3!Daa%DqlW~JS^+#Xk3*f|4ovDxE?YM-1|8H3`J8o@U}B=oDrzplWRH^& z`Z9wb`!)m1-x!v~yr7|4-LUks1}Zxwa*db*@jA!&{XIV%-p>i9 zpGtFZ`qwy=?{i}@nToV{o-$Nl>*XRRJHr+;ft_<`BF#Seov(5(=HqX7()c1(9Az*L z7A@b%Hx6EhLycUpW>f;TSIM%?*FCULxJy>53LM?-mTdi4BQVwr<{t{Xm^W>}I#>zl z95oTTkl)BgTj=?QRls#EaJ1_hf%f*n@LXXuCI+Zb*MK1OvGfoPJQ2lg7ZEM&`w9z$ z9@4w7rr7NBg?vqlAv<;(Svl0g*|UnI)FE`i>Xj%nK}Yng>=L!z)MsyE_i=wsf76J! zCOG4TE?ZQV$Of0}#Vun-5|+%MCyzHk$`~iO&@~jp0`ocPx@izrF2~O63`TKaEUoq{ zMDsdd^0E5@Ln{hk;> zIYg@VF0lRGCGP(G0W>B;o4FY?!O7c1@tq}tt1Xs1A1{IJSHgKu;g)^idKQ&S^IXj8 zvCK9420wL20?lvG!Ug^sbD@)H z%Qm%~g&wcl{Igw`gk#xN7^pUvOuN2-^=3Uzg117+CHc_2YbAT~{2jzhDu%gg@&b1( zj(%$P3NCCP7M^$-3ZE`Qj}sH=^`{Kjvr3Ddh;A41f$`!y0+Z&bkQuHOv#ZFf`1HO7kNs&ond)xjt??gWgl zz9DpBWmwAlG??}+f)8G-%(ew-3t5HDaOJTcd)*)lQFkr*pb3SfH&7R6)(jE_iVu_B zwFzwTh7@5pxr$PBZP}Zz`p~?|4TqQ~h}Ycfhl97x!FG-$XP0(QFeoL&p|xP7B0(#T2gf&@tFuvJln`nM)C|2sh5{K-*o~{Nf9J4)?du z!+@iYfDfA|P8Ims7K`t|f<_av8DY&rHreCBnfhq*DIc%7ZKB5u7qc+ut+2dc2pgT4 z1Y?_sH(s6yNzd{ia(W(@S;e5Vz>2cFi{Y@4?KZ6XK_0i&nCEMOCtPdbzZdx3k7L30fHUrM*e6QO|37BMqh1U{`Fcq z=5>~~eaoW5rvHJg;0W9sJ{+%Zv8By_g}k8GOg^zW46B8Ivy|Kz3b^fud1-rTXRjs0 zd|`LZ6oc|NY1rNcYn{%k_qXOra(+8j5+=Ki4SdcL^gvL#as9~TMTljT0$(&dZ8(mf5 z+!;%DA>ur&g~e$4=`LS)X%+1fjb;wV??THbWqJbff*Zn_tY6*dL|T`z(12w+EG_sN!2Pipt|mOITTX+XaKGBt<;DeV%z-CE zbx6HF9$p?Chu|HsQ_*U{a zkVB<3;ohFS7t)0}+4$lq@Y5R0jQl2Y=MzT5ql-eed&BK0qgPh^MjCBlth!` z%-Ok2IkshhAqyyRV^37}iG$ns(;DA>wCkQVTl`0sra2h1-66|F4pFmd&3+Rc=|2Nv zCM@Nu7Hr1i$-DWRpZ37N`f`Z%O{$z)tjvm>y>X&zGHhRaiTb6c(4t{^WYM9Gzjwc( zEd!*m?G1-@C-1=4u@kV!n|re5UY0F1l1p@?2LgcZ+cvUb!esY zXH#a=5B?c-dI(&yp(b`8FqkWH)ICUG7 zyi*20x8$|-ce9#V}(V=Kv+C;j) zPD1?VdvxKpA`4nzgr^SA=ZYrkk)6~bQrD2?ZUv4b`v7-=XSJ2<{};kWcxbUu_g+5l zkOoev-OVMQ-j0tC>;jS20@m~Du6VtB2%6X{(fid$oIbaCFv;zsSltY3mST>y^%|q9 zZNV(G!fxu3nqLPZ*piB zjq}&Qc`M#S#AQRgekYI}TCI$3`vsnHt32NNF$M;@KC1M6x(@1ohho!Nb-euGEH}U? zg=zdC4Jo^7d`;!&ZXcY<KBx2Kh z&Ow!C8oJk8pz1_3b}(!veC8fQXKonY*u}%kb9X7YwvI}jX7OfgH{;m->3rJ3FZ}OO zpJB{Ud7Q$OXzu_exUDOPH)fxL&kgM`TC$aLEu@4nRuoAw`{-2DPF>uVFs|Cqx<@0sIYzE-?+X%j#5g(^0`3t=S>jdAu0 zH}=VVJ8e@gg2D24Xh`-3zO!*Zspgi_tm0`*6s3%x`UO68t}iz#dMd1NS%@!ZE~lIK zC*mIO2+`4b!|?Psfi*Sv9R&`ppo(Kl@q+gwer?z|_CJcw!=c9ii{qs!4GnEY6j~Cg z?sHB>Dk@5$QYfo3B9sU%ZIzW7k?bN3bf5E~Y*B+X%W% zZCKVlO_YT((7ZTX$cdljP{@|O|G}VGDF)^*(|{hY960~%4=IME^TS6NW7rmDv_Fx? zuZig4*NzxWX6J(N@rV{MiWz|LL5mw`?#1qI7>E3(l`L|(ABB%hqQf$a*{Z2d@V2Z8 zekm+rxqgG$&Xx|kJn1BLceFx7ofb~En}xj#6LiGIin8D8S*$1cG- z$tbw)`VAIM5$;+;*N}Le9@D?0igRieG4XLaf8S4q@kt;kexzp_r|fZ zkwdxgh3PD{J)W1}y_5@&l|=cS-+4(NS%Bd_EW5g%WDe}2&c~|EV_+vOU9AFxeH`y23g(O`P~ zY907B9srk$de8`64C9_1=H`WREZArgjXgaUm$$BB+hxe5pX{dPlT5gs zX*2lW{vTl3y~(USsE;05Rq%b4({O2BGF@nR&G&CtBk#!NaOQa|i>VMgfIr&d&4rDi zP(7StU!5R%i(2uk%K~Tj@OhD`y8@dvWf*!24C=YF%gJi@X!L4V;uTHhxWD3GG_J>v zc~`uGf7@@tnW1OMTi6Nly(P3#dJFvO(_=2SKI~Z63@$x)Dyx8I!CS3Dwmk!>T*e3e zSLB1`JLKN@FQ>k3!`P2u+o2>ml8qF44c?Nvd~51&8aVM6bvZfXzir+amUf2to+`Mi zTuQywKl$^v^Nx$@e8cC8wM-^C;q)r8^ zMrg4ZJ{3at-lHYwm7y;}hq^va<$EnR3ZCy%Aa0YxHv23Hf2#*p_W(z3xCzU~Tf(h7 zTcOWy2|l29l0R z0Jpt8P2}@vA#MmL1MwZ<|EfKlt?u7P3$|xI|lFD_W35|21Zni9|ZfN3;&$&p;CwRc6&V#^5yRw<1z4(8DmaJ)QB7GaSke!mh z2J`ORr#Uivp>=Kn_q5jr+gl#M*bifDv>bx+L6OWRc^m~Q$}z)x3|hAvvwOprz!~`*JbY;` zzP4OL);kT+reirXT`wuT^D0Cq3U^TStc`T}b|y8o4P{w>1I7A3^r@lNl{tL!;^xWt z^S-x^(4*aMta}*GHE&u+zDGsi^?E$c|DcIi(&8caz8_y1CV{fQ=MX8c1q-Vtaujj| z?7vwwZL2p+-x$nt)@g7zKI=ol0)JdP?6YX%X?IZG{)F1Md(iS_*33y^v)F6J6r5{+ zntS@qhHm;Fgy&DEvchB!Y&c>_FJ=kZyv2K=d$$?-DKzqR)l*3Gj5oQuc%aRo7H-yZ zHJlMC%jD};z(mb%x)GfSi@OfOpLKcgBc+}Hyz&_L#iSHepI+eMQ40;~t>i_IrBNyI zEUnq(M2j{BQp>d_%2*!5AKVcKTh=b1*VYUoz6mbfcL}gou9R}}ELpw5M0WT^IYsP! z2<8p9xw8KKg5O|1yYxjCCodZWmq(4ptH;&Z#ymfC-nIdZ-uP3{gjBZY!!Xu;^ALY< z!3}c!mi8Z!`&rQ=y}A@(%2XeL zQ=A5kHP7ZMqW6jlvmbM<1s_RTE}I@y&PJ(Vd5oDS%nnlbP~g;M?BvwFqSwcp;Xvk0 z8ZshXtn2j;3T01o*#-8j#JP`D1tU@Mf*+!h+lh{@+0XBp?+0>rS`fFr3Z^`>#%U6oI0}{=KpTw3n!|PkLgI}Ar;R8#S0;_ZYNllBym!cx4{4z1spQmoqo*E0rQKh z82alpXYFSTV=g=borp(V=dKva6}{m?OWh!^zl6K0WPlBA``}zl3hNuag{|0~Mw%La zBrSBI2b`;*29sR!T)Pj##-Acl<3OfbaS=8-?}F{>^5m$q71rG97Um|);Oj#b=AQNv zmL8kJO0VpN;g1d3+xZPJZ_+No6SNXCO0GiWob9lL)Uf2h9>`HHrV-y=!FqNrXKO7% z=7l$@>GU(fV;MlRq6VY0&w7+-ND|8)+s8dKUB^EU7>l}c@+_-Dhn@PXz{(`_utsAt z9+z~%A(`h%UaXCaHY?D^-5Vh#cav~FmcfP>^3bgyhu$AMV4i3oKG?RO%(BO^mbpL4 z?Mf4^9d;0k+m6!SeHTd4zmh+;-x}i=p62rp%fNT#bM`yqyGhd53WtnR!33cfcDu%c zHIIp77n7ZFoQf@udi~t){MZ#}`s+IxeUcTQ-u#H4Q`!la4ozg6C$54o=X3b80dkx? zUWdoiz3m?<7J<&xB^2N)!`8m9qEp|59G7DQKlFVZ=jM|F3uHIoz|-5AQQ=A~(v`zn z$$XNSo*&Cs0EiEpz;?L~VBLb>Fm{nXP6>KRr^9?9)jpam_G*w*S_oQ6 z{@^snO7mHZ)al17B@FpH5cZw84?8^Wa+|*Fr=Z?&rgd*3MnC9=RkkD0?1&ksR(6%% z7F~mJRa*4#_ZWuh4y+?@I7`^$#NK?d$2aOi|M<8WJh^ItzXm+wX4Gz@_*==8_uLxK zuTp0hahJ*&%@&RjTOD^$Z#V7133+ zJx7-Xg!!-+DWOcM(}mTiI55o%ZlbM`!aX@~CUlHFLT(=ivbBRb(ofcBpUx)H>ls@3 zp4}h;vB8Q*6v2nRUHmm;FLc>m4$o67IL*GJwQqMnnkNr{MouNj61cBY46wrdrn;fLKR%wUIR`e+Nn~{ zl$DL0&i~jy%zpWx!+dU)8h)Cmz`jY|rl7hw9IF;e3ZihTU1G*Q3i+xyjX=7Yo`5X} zBbiB`7Fr(uNS(@^6y}{w4m0dw`-OUXFU)Q2Ci{s#$VkE6Cvx~He-Jy;p@Q3X4Z!eP zO%j!r!tcdlY)(g#*xX?-cXFQ%v$;fk+Mie$Ds(ckF4~}y@LZkGErvzCJGq^&LrE>K z0@%s`SnEEKRn!Kt_1e$ixnVDWgf>o(KvwbdD0fO*9>%Hdq__%CnBu?P-asjjpQ5PB zopS{GT^5diEpAb`+CRu1_8Qig4Z(n|VI*ES8s+-mLg}0w_$r@8^yLMW&r*QBgAYLA zgiScMY6h?WI}H9c9N^v*BtXHcBARw&0&;x=aq77`u1KW~R;-d|Vb&i=0^2xE2TfMA zRh!K$Zi5ZaZ}7?k>~PW7f83wCY5bi7nH21r3tlcMG_*p~@ zMu~L_9imHzd@yrj1P@obxO}Zze$5(1YK+?nN77u_f|}bD{dNhHce=#g4B9~h|9+rb z+U{tc>H~rBihtIhNy{ACXv^QVXz*?@1znB>pCz5NcdV)4Lo#D`|0b~sePNhC_CMGq z_(r-X$};x>YVc$G9KOV66t(H?q3rQ#P|=daiF+;B)|)rQ56?F6qt^xEa*+WQyN<<= zl`pAfcsi^R_(H`m&+!A#*~0XdquBm;Ic(?!E!Hz*9&?Vf zcV|OV+EEgxDAT$%k@RIiCXR5Qzy|nvGUb3VEOtN}w;;uj9!8k6Ka+Lv*phg*9G2h^ zWnUEY>h!&;3$CROBYyWBdb6t!-WA5uXkBwQ>ryc-7d$&v`Drv&coy3xKBZ#$KyJOk zdEm}mqN{C};H&0MPH)dbsO;Ru^WPrR-UEZ#b(#~ATlG*&Oe0J$DT4DS=EAqNt=vGlwd~EGFm~)^ zAAF1(2r{~}(V{Dc9T_OY-JaHCudCb-o&q+qcSs%RM;(GE-ib`#HIXI$FrtP#ANb?U z2V1g#lg`xy__Of}7=4~B){yK2Ox3}dWwXJ8t>w=Z3}t&%vslRa>zwSB za=NZtRC#-)JZ0_u%!iQ~_O(dBwjLXts3ZyH$FGCCZv;*??*PRF(aFCQ*swj^0X*Ez~Ga)5O+9HID-ElxIYVIj3`;4dzr#f>f`R*oZ+x5F{^ zlR9bnoT8inGn%y16X1L%?A?ExdMd9%tNR2ReWCzrjgr~tlk${T{2eSRhCfhyK zmc2OD4OZg|A*vu7Q}WlbfvXIe|M+0gavO!Og!}%Z+MQgHPBzF!8nUYyCaiG%8vc&n z7}^_F$o=0xJ1IhzZQRiSse0z<5!*mBS~IxAHR~uj&WjoT6#DO{{jhR=GrwxZcPIrX zbS`km0rRpz^@rfDbiX5HB(upSas$5jr+`(4_esjj4>p7fd+yPZxc9m|R9rP-?UOky zIJ8`d(6-W=1vS)qEgX(S2jSeB^CT7R4!OtvgURA5=$$_ZPvjZlqu0l&=ZPAM-s-aR zy{!UAz5s><+u&^96ehdepNc{z;FZTSu(4+vhHJ#)si))FH{f8@)o>;~;T(P2*Uou9 zISyL$;t19~g3||%(t@~dn&@WF9>WJnX94$8fXNvkhM<6Bj4!pA=>TKzx%76@*;u%iz$Lw*$b7A)c!|A(0qrku3DeiZe z21d3$AX#e7c8eBXeu|DkzM?IY zJ=Z+MMSc-+*9NS@HFuBj1+l8^?i54T9M!;OcW#GVtM`0!;9ctZWQ3F{oll^_dn)Bq^0oZ8)f`H@hYc zt1QqzE_kKo$#AJ7Q%a0rhhIh0-x1?s-#Y`WzOM}$PQN(w@M5leybbew7{=_!=<}&> z9hgzUYlt2n#@27x$KM`i%e)eol7gQt`%!<8>b?nVFxO*z#5gzfoOg`7^6j;2`(nZ!4F&|KcncMi+K4=R1MW10mX9=H@d4s{CgXIb{PFN|Fe8&Amt zN~pCejt(2yqU~)%(an43xYOg(Kx4uyEmL=R`rNpCXU8U z-z(`=ks{UkE3m(Z>cw@ZI>iV7JA>~prqK8=qp>#VE?3kV&C9i!Ql{$?ZlatQ8r*M( z`8$hA*XS2%nZ|POl>?#b`9Ks|*s|l(N8*$CRcyROJRGTT!TNx&R50QtOidmKx*_4X z^h7^IR~wSW#cEpnS&FKr3uoc4LA0bz3YU2ug8CW}InPnSe}!*o^6L)%{nJe>_VXFI z8T*}56~5548DjqM2H}i;mBII|T~PX8I1~BB+1s<~+s8Pi6%_(mC!a2a@u-AU)|h=Wy*2c~2_i?^i10=EwRNh?fwMO@0_V)}BcBQExT8Dhcis`!o-$RqN1LLXRu@%C9U!TU33zRN7n%O^#!`Q6 zGMlP}n|cFiN|Ygvk;}rZlWx$tNh6t0yBmAD*NMNUDq`i+0-)UBEoXTBDs9kN16o&7 z?81|@$X7TE_uA)kZDn=zJw1Tkzdsw_FBBYcExV~d>3c1hedB#;#NtGHwn`fv)=$F@ z&uhYrQvpP8bJ$}~Z{B2m1!%`tm|^*vua3eWcP`FZTtasNA8f9(SDdzbqC_?R^fp$59r2}>y-3; zKh4(ar#mOd5q_@(@l0bhKW$7Mu|wJ7pcydxhrrlZ>8JfePw`zde({U$<*<{IZ$#&w zuV#zKx5D>`7%s~y6Xcs+sd$yQPTMXP3ag$?xq;_G;jR<#U+j zSzGp|V?R1W0jS$_knZ+rC=Odm+|&?skDW)4Vr!svwg(L!;=-(!Cc?hZmwc1fC1Ec2 z7~Xo{r}N*=^MPAsnRHz@jC0uyR{1qt%vv>;azC0y>o@YB-!H~IMQwE>SjF#Zl)ur%BQ#X|A26IgfS*+!s zG=?O9h7p6S=sbUvI(cW9K0%W8H+j(#Arl|$JA>bTE*0k)>xo-pbWq;W4qg^J;2Xmx zTHiQ{Ds4hhckwU!R$~o63{>cku>-vAhtn{tY&g>5$e*8C$FI-d2)?r< zu_AaM%@xi(8}12R@^{|s?cYI6)y4uX1|Pz^Dg${xgHrgsWi1<1{E}XO8p^bt=i=)9 z0hktOgKs7CvCLtxz`#t$9k*w*wlCpqdx<%ERv6%VsWdpgy#=Oni>U#11xMEk)YDqZ zPW8Iu-Sdu^w)hWk{6m{Pd25bckpjMc;_-wv(A00h*2xLH}sY^SaHy%~8cKC%yp1 zcwpc%B~(vSVa~0l%rVB6jT|x`%?}A4rE{kt+{}b&&;Bc3Ugudku_u;tzW9@b-!hEh zv~bzJf1va+*?vKQH7O_Y;Nopgx5oN1%j9&5UOF5%1rNrKtU~CB?SyQ-mmq6%Ol-$r z=3SBnPh9aK;2$M{OPeG!l=51)Q`ymL_Map;e%qc-;JY-t2rH|05ub zZE@eiaWm|2iIEfS-#847e;F~^_*wjZiIZ^EB$$*V)WF4cFr_<%lU-g3*bEts28Qx% z^SeYGEH{|mhDc(UStVWGPS#5iQr9)%|`WX&Lqb6dta&Z~gMW?QlJM+a#u4q^=< zHDo&7i<>wy1t)DcCbd{eD1KT*+OdIru0$ptl{my*NcaePDWkDR@gu)tSvkDSjD!m_ z{6y8u4tb0ve*9bk^-2U6G-oj?43$rRD9 zuZhg+&UN~hZ39-j#;^t_S9l}zob_WWA3UI=ROH_cm{2B|}qT=9iPSw{sF-v;HzHnU1J` zD3Ylky+BLP{U^S#U=Etx-vG|46PeF@pe;(zz;HC6Hn(12UhvQaxLJI5ZsyNLh1K3B?VemNNC~UNFX4eYC`P+`h zIQ)1Ucu7ctGj5>w=dZw*gjjs7sf(67BB1S_B*tYAXP5F#Y3LllDE`vRI4euKU%b$q;`;9NQU99D!?(bLUB2K8?%mCen^NfQ)U$KFzi_a03l z4pFrEsfcV16iKJ^CUu=Y!o8^~gp;}4IxtG*7 zUV{t2g}#!RAKzV^ji=-VR_fJwxOuDyx{uA_$|E1ZE8`zvu3Ap4d^uMrX--qNZ9s9; zF#K}T3MZZto+$;eF(Loa@65ZrOu~A0e)M=D_c0O7CBn%1p2f;mY^TldPM}e- zBYuDB35#C60nw#)Si4&Vub+!#{vOBR@0Yc_iA5If=SpE~S{M}+X@Ti-OXi)Gk5`AP zv%Qve^lQpJI@xoCJNxD++qC&EbY;u2i{sytyq*G=-q=eIqLI8utUxo(qu{z!3zd_u z&^Di+WUZdgbw=f)^M_SzL(e({2sF%I_n4FLb9OV5h*yfYXgYKM00oIYe478 zMflpXlLfb{;K`e|Fl?IwtfK7Vl4GdTn~G7j*)(WF3EIe!l#>?IZJH~_Oa{)T~E2l<<0a*V|U?uySjpQUA4xx zIja2rRC~}J7|s?L7~s9bGuVf#J~VP*6t{S_54=!TWCw>1W*@7K@XFn%)Oe?d{=^>O zZ|x`po2>yX+(Sy(jeUSo>N2d=Uz7duaA8AP4D|@R&!g7b%w9eMmy0dIXT(U*R}4p^ zDN*R3t-z)UEC`Y>s8lxI2BMyaq-AdkD;_4G1)Bh7j}D{AeinQH9f~_U#J+M@6n?c= z!&1Er^!@(0i^1n#bqUPfIH}CZUc8m!}B5 zAamwkY{mcApUeIYx(3fq?}KfM_o&G557>E^f!5D_He+QX_`LeTMTBX3x2B268fG$NzQo+dwS;C@4u{5H?Et*oqmlFYj`S#mS)^-=Aor#L3)4h}GuaQ-sTE^ZUujW3Fk7BNS9iYSID}>H@0*elm;-(+Turw@# zj8_gs>3juvl=766t@rb}YR9>k+10#Z&H?`MUs;jIxij3J*+@NG zQ0Ux;^HJ6Yhj})n74m%7bW2p6-3ablU6AHE59=rPa0e&>PaZSmuWB;7)!!tF{Zh(j+}{rd z)`0>!Qv-M3aKZRB>zVDpAj*DN&H2A^;m>ss#v(rloUiqWZ~r!s8Q+e>xhnb8v^xb& zo?GE1Cr7mO3xW&tu9Hz?E%#n^FtQQT$XH*3rVjo?D{AI&Ad`;4j!1=BXZT|Y5g-@o z!NsLZ(Yv3MF(B$ZX_zd*tE)IDtKI=$e=p}=^n2ic65@l?80kJ3!9 z!STX}q~v@UcPS6!%WkMsq_AJKy_}9`wG3F~!{w-FGXUKm&g6E=7SR+(D}?BL)K!|w ziZ$}-lbT;;zc5!$)St=XesG|pdlH_8S}@hb3(z}sG1ZTC$Gxjm=-KO4IMp=>t6!M1 zj~jO)Nt^)dl@`o1?-ozqZrG1ERHLEetH3=K?!B2=3jBz~?Qrzzeh7Xr6ki?DptVNZp}%M+{LRXS!E-jC zeM}#Z@mc(d(v?)Yxr~=Gy2g*+@|N@^?o(;h3Tzyyz%FbHq_wAHQEH6_mV64KE3Y)z zt8G&3ZQ57R4mpLxYKPMG^Z_g`N*jNkx(g3$Gg!S}DdkoPdIsSuo4AtevaF@)A~(1y6R74W<~Wp}-(A8Jb6P6;cVRRzY)PA!oe(C=97A zfkUsx^GkQ{0{g+~oXtWnc28#*<`3TwItPj=Zsl-1u=YLw#5w`Jv9{1ODp2+mU244r!h&3ba?g2l@D}Xflhhd{7%O} z3|1I`?R}wm{9F>=op%@H18mv2`9;uE{vOJo4TJo_hv~R|7;9AOtXwH%YIY7RrIPEy z4!!FnFeZzkPZ;0mD#ujL&c}bdr*rzs@!~f+)4~1rAt-tCNW=t}&96;*%--%M)h`gS zocsw~OIax;jhTv@oc_UxJ6>#-vMaV9v0*)b$I|ClIi&06#M_NIj)hgzSjpaDY@jz# zznVG)_K9#_;W*kMdmWd5+S#pMkkWMR7|S z;@GHn*)&CEH#$~7gX-iru&(j5kc^XsZqmT{&!^L!uZs9-zXFp~Y2!1lt;U+-1ANct zi=<0GMXlYB3G!r!(^JLvki~c@LSTD5*vIdAeG4* zha|~?xOHI@m7uqf9lb<_*(0$d--M=jj)ZZ0M&s*x9tulBSg%i(NJdeLWb|)<@=|rC zotlO6zn_UxH%37F0|lD$x|AE&Cvb7^?S_qOj)=RCr|>!n8t~%tEpFFR1N@x6K-9P} zn7ea(9xU}Yz{qD4SwYxQk}LcI>9LEc?A#rY`JH;{=^=9p+}#Tejoo6sG40e9z+nj4 z!VIIG^nZ`*`Ty^eTgOA7mpZO`eU-klVX!KI2X6Hl{>t1Pbn<~BMxM50o|i3X$S)nv zFk~)7#Gm16Y>GJb@oJ=0XUCH64!~-)i+o-9Cf>pIBE>5ua66zGjHWChNk4f?jSFQ~ zMjib1?`v3J;|&@Iq1d=|3^q*~1@G(4KiAg<*lS^ zkqXDPweY;vYKZb(g+-2C+`ro&C?g_6l+B0pIn^2TYsp|1llF!Fd)UB7J3GMZb+1LE z&pf26_E`ce?l{F&nc#|9;ar1^C5<1J3P;y%rS_f>ma5G}|2>mr{{$Cr^4~tLv>_We zJ-kRUpMQhOf@Da1=gxFY??QHiJ!-|Nvtb42Xn~px_E|iGMJFbZVeDxdz4|Y}S4qww zN(MJib|8O=OqljF75gk5VB@2yWc%STwCwE@vJbYbdsn^PZP)=vz6|8EzvaW^0~PS# z-W|~s`78EEQYXXrlgBy9enmEYj5mC^e*+?7uEM&Aa5UJVhTn?|A)S*3S<_t3Jg^Qb zr**(D<7t@lKwzDJSw-%dbrf2N?CsCNuqw?8-L`!fM|*Qn{OqUbzj-G_=2IU)!+IC? z;kG^3|ECF}OeIM6uP&SRDhE6|P2jCU0!XIq07s?mG{s{Zx5PGppY(MM&Mw;tHTzcJ zrTjg7&ZuZIN)^u1pWC^SM)j0&brJ1Q>V{#a|3uUnNaNlwWg`A3XS;qX{ca8cYnLVH zcf17py%Wgq*f##@`kA!1jE5Ia7wF$kHMCQ)#KO5LylG7c-M0*aX)lC(Td^M(Z0k!F zZ6X|yPVmz2lUPgI4LsES=l4)Vq9Xd1)o{e?E%c;fnK9!9LWEDLPAaGnr_K^u2IX0Ocm+auK%IqVr zO{wfl0wc9no{x1YgoO4HEc)XEe)FAl=4`hf`Dyl?ZAdGqReNC4+Dw*WuL;Qy|3Ra~ zF0qBlVew2fA`?U$jxKIRP(^5js7gGsz=f z0XL4;Vi~Q2SfjN*?SG(7?+q%smU<7&;O>I0K^)}V-v!UhO)<4s=pWg7qe1>!(eaW5 zl=$L}e}sJM$(Ok}@sF6s?|dcd*>)11U%Z66Yk$z`BQAIZ$Dr-IINVwh&y?00v1SPw zN^9{z&5?1i&nXxKN7}N75xQ*FHo-UXAO?^7{-y64Rm7fLfgmZNyIc7HDr8>qicht$ zT<{g-_q)*5$!hG~wRaHon)q}Khubg5kk8puH2>fia7(++1!n97gU%%QFKHs%wELa- zS&QI^?9L$FlYN{{K@n9D1wtQ5pWXf)=J-C8N$3sM@%ViblWS zT%1>9Xw*7plwHnE$xmWFF12vce=`&t&OzI?sg<7xW^!|XRly^HFB<*A9{bnc6f2BN zU{Mj}m41y9_;!fkGnaM3%|e_@@k%yai@Yw{;eSrtnmrU8UAKe$!ve}uxJq|F`!Fqk zA^*L#3erPU#IstxS!jqd7O7hc{$0UOyyB_Y`JboYMo%P#-Wqz|*aP~vMw9e^!Sw5x z6Dx$H0akPlle^5q7Ml1KkBYuK%OGBCehk*?)ez?G+up#R-De#Px-lINoF z#1}vKq3;IinrYDWVmGb*D2Y8WM~Rau;Zy3L^Bd}=NblrPu!-uW!5Q^5tZxcb47(3A zH;=}9h?3{ z#T&ixm!l4B#F@fOF`ZRQ8{=L@T^4D#2>$e}#Fm~^{{F%~UgKmu&W4?ENGcngWgpqw z2${bR+RErR?J6BPDEN`SJOMZx>}A_8|K=j{L%bPOP4#hKXRy-{n?P0j-E%rmC`<<(H$ zQ$UL+9^u>%w9yVbOVXStbiRLhvG79`ywre$H2-flbq-%86|s-2rVUW!Z8`3m@WAkIs4Bjv(0X1;$6Wt#q? zpUFO)cU}$eHASCEPH&^&+cLO9q>E-n_qn%0W~h>#414{oaKyeOSn+8X+SXq=q`_Gcf3?Br^y~W+N5_b1Zo% z|HJt!X}1fR_|=KT|LrDMXF1$tcAwLjHjw597Qw!6LOyWUF)*o?LaCP%xT!TiVRdsP zR(ZSwqbK9J(D?UsTRH>g{~`YVNg2#5vIh0iBXlb<6=o>?BB>*?m@&!@XVqpviT8CX zAMzh%Cq>f$4KsXvN(DFY4xs<;8SP9^=cx2yxl^*poq+A*;{+ zm`dWBS9@XR!e8Q$4HMWr1K~OKJ5jf+1a{niNQJ_FHb-?KlXA&~i>g*ob0rCa)3h;X z$rN_ypE5cXd(+AIdM^B)B$JO&!-={|;2ZG_V$Mdu-5+wOI?w{Y9vIEOUo?afC9%x1 zT!aDpmD!WhYp_k|2zu^+$Y*U*Vr^aybToeq6?`nFL}#Fm-sM#HAdxBOEM%HyGto~; zaGRf7NN$BTDBmf=)Qj#w(BR*cqmm2%%!BcdNjmll{=Q&&8Myz%9v^t*z|tf3An%?? zDuQ2lWHg|C>J_d$%9+|J3Hgdzn76Z(dru3XM);nLP`(Ni%H!aG!BYIRqg~Wf@tLm_ zX0MuoVH6yl0CBQD=sn~HOns<}9kX6h*dcR}mlz83S~$8P@b?z}GQgNJQwq{^5=Y^uCX8yrmE4E`Nbt^Q<8} z(Vls3Nfx`v?G)F93B2t{d6a9s0QK)qKw)D7+>zF!@9x`Z@ltE_8ux*FGUhNC{_w!3 z**8SSfzn)Wry|?@Bn^*`c4OhEj?x@XU0iG?c$B)VU~Q--9%-$iD9wjp{rwGiIc>t{ z+mhhnpjc9v0Bp>GEK1%#7LE=a$9`-TQ~!mzZ2Sl%l=D0a30CfC9F~dRZL0j8j7EAg z0bqpCXY$Ng!!+~cnM8dSZab#Os)kM@x_pe&3H(O^O@AnHi94I|Oa|JQf1$4KXv*uY zrANPN#WkJt*oXn=_+5P*&f2$(cO9&VlgAGL&rGE@;i$S1JH^T*<1n|i)JCxCal@rXb1bOo6^{iNXH z2izdR6J>u>mEDkT;sR@V2$d7tbt`-$@!3#Vt1k3;#~ZQLzc;a|pWeWZR}w5vaUMQY zj0TCd&5*ZMFx$RRK&wRqn1=8-%zhrq4$YcCdVlrd=;++aNC{ylaW9R<{T+nQgk1Wp zuL28s{ts9Z;EV0k_KPABW}^~Iz`AhvX$iyPvorj?1fC92G}rO1!WdbrRz4+aPa8usC9H6Uc6Hco+ZW@ zv;GeVuvGqcMJD;l9N}*)86mzjIu&j56xiuC{qXlGqQ$~AG6}V!VmC+J6C%t-l97AK>r9Zpgj(lwSMi z(lz%4JSumUnp?t{?|y$4AEORwj|7JCODCM%wvA@(8o&znZe&eE8~EqjKJuU5MR1~J zgN2{&b8y>cIC;N{#zX6*(fVGxz|}IqbqTj%!gIftxpl10eF0n?Ba;vIo0+ox^o_n$jYC;xpW)nC$En)j)V)&7h zcHqOG-yzH5G^zjfN9oPFXdanI>n;e)&}19@3^}N+oy<-IPQ*^1A#7#uSZ2Cm2+Mf> zl8!I=LWMpVFfD5~&ibH%)g#y8TeVYEcQ74(J=bP!bItjACTq}Xp(GmH&LpK#au{kk zhbar3Sv}WloZ_J%aoB%VTomU?bFUxftob@H*WSY0EI&fGKkVn93VRjxfh7VXVLI6u zM`QMl_fWFHiC)KRQTHEp*8M|+M$Ql7zaB6JtMjwqe0>T3;_xfp#Ogg)cc26+#7eZj zUIN>{o~1eSHF%@Gu~2sL3b!M3oZz_nO;UQDBHfcA0z2+4cPMKM+h8;WbY0aLzvCy2 zxOIn=ClAFK`GI&$x(uS;1#+{^OwhXqSlRr^D6emb8v>0mA)t}|EmMZFJ{8=RkwzoJ zCUE{e2>=PB=<+%VICr*$Ur=5T^!0 zki3osS;r5>`p1)L+f)lws?35aAy;lP!HL`RrUmK-gtB$jY0S9@yhi#B$MW9M)f<-d zPCl8PTTltS{7ty)F;+ZZC57VujKGF_Az)mu3#)>=`OM-S%&=wOWtp53?#ct6KbP?s z-c*VCWudECR?~O>vtcaScE9KKmgYmB&wPk5C@0bLZNj;DJ+?LWaQnNCUs_CC*)lsr zrsi@HdYz@&oJ|t!ag{Ec&g($t^+5ukxx`*aW)!b{b3V-qUBg^nY=zL=T<+pdGcp`m z0mf%nvw$XBX00mBhc9Wuk-lSn?TS!7#4U`Dq z^Zf_Z+nmh!ZqTS>Xmx()*48#>H9RuKd!4(Xf9*$d)-l3YKBu^ov06|cwwRqgW{P81|DvX2gJ3~aG;64; zfbSRHk|<1*EmC;P=`0g^mH{QOy=hp@FmQ^&ZMF#4SSE6z3k4qFyt6bzCIe=xK6KeV-cn%k zO{G0{x@1)*?2Kld<~p;6(uS`6)Hrr3J07UX&fd99XB-A#tDKOTtC)+gz28Cd9dk^! zdICQp?ovzd2Z#-|Vf=V4a9!UIeXyE)J3b5cJXdDUA01K1nIF>H&p3){i@uyryeyk4y$ z%u1_;y1l<>bI@WI`Sb*>+&&-L2l}v!jxqSAAO*w5q+;8T8R&7vQCw=OPGK!PjE^bh z%6AHLmzX$Ow$PG|8Qjm+ozX(Szh2Zc$(kw2?8f*tuc2ePCMpk9Wwm8Kn7wckDQpg5 zb1hQoRJb9oaS^iN`_1t`wI1>kyb9kJ>mUR+^O~zK(9j|y7Ol1bD`Kii@1Pl9`{*lV z_U@t*%YlMpXe(2ez>9jIpG9d0ZMa=mL4yqm7OZ=lHz=9-dG0n6({n} zsvVSd^#^ICO0roe5Axt56ZSyW5)KWO3A}-w26FcK z5LhvR_neYNJDx?edUH*7dT}GSOPmDTHmj14;&Hg*P)RW%W7#IXZ`7DDll(WHrau=~ zu=DDJuywRM&dXni_xP=B`>j~Iy?8IzprXQLo+@F&;_qN!DZ{EwG}+9?Xk0vF1q)J| zLt&|H5Fohxj&>iQu(DWuGh!2jP1?dO8Zk=zt@k+T%vNEeA87FX=_afvw4Pj@%ve^9 z6+1pAmK@ZQ=%-5-iM8wL*F7z460*hCF&eD%(j9vo z;B#H0jFnTiQE%B9(HU=5>N{mZm%A=d$OU)a>0<&nanT6&f_>m>B9z3(CZwb7lLT6D zcL5$6_=tu1DC&=_LI^6x!%RynK4Ew8vuUGv?sNv5BZ zQ$2=GOQqSLWr^e(H;1Z+AXgX-i0pg>PjihU33~Y{wFZR-)Ym5zsZ>TsDo3<&xc{J zg?amGLx|bB1*Wb@=N8>t4HrEZih8xjVt$PdC_XX=t-G>#;`X}Q2SEPdPoou-I z-IU$y^oHEY(=hXlz{-uWByQO#_F$Y1(^VJ(zA6dJr?5 zJ`-*BOkrg{60Fa30M;#C%`|2oqK{`kf%g}4epFlql~p+L#f$QU%x5uFCclM(yMbJ4 z<$N@p`4N^_oV?<;AsaTN{|3939KnI$#WLEI@o`fOi0gK;)xWl5->{vuD|?pU#Ytz6 zxS@P&U^(AkR?B;&wt(`b!8mhN5seOzW}8Nz0mrPnq@ui&DENnRv;g147M=AtQbW&ggqdCWex@`N=?($EBpDmTkYZ6<{p9p zsua@1f#?+pzM^Y%yyZ+gdFzFCv-~m6 zb{wAC*CZrqAY>sbGi8wbUkh0jJgVVgL)Te9quar z5*9~(&4=jv*zqhaO9c6=4DqktD(}tznn7Aqrl$RdmvmUz7}7% zRdK6lzvcbpGAU%-Ntf0;3OLR69QWeaL4Mq(Rj~2LNH)zd5^N@KhOFbOFniNt5+&;M zMhb6uvrVU9VxK!*zLZUp`Ha7E@H^kscMCpB9pmFSjzF(|S8PeJU>64_bN-hmF!RA1 z*oa-TvFdU*%QIU={OSOdqEr~LITY*GiYWSz3N+-02%z|LBGrU*{MYfj>4Vj4^60jw zzxtZ2Anz=QueI`4zjvc`{BUgidlg2;U8W*MUh@`-s2#JT34IjDF%jNOUvp#Oc=44Xd;7_A0Iw3OXCpTix3XIkq zjxXgKVH=vTQhQ@IvT7#YF+pDPpbo3*uAo$%a>yPzo2Jbj#&#Su<#|~ZR2V&m$xKft zt53nWyx_PvbF(B{SJlfWDkq6eb3eI!=qchQJr(hMgD1Ty6}lX1HT=m(?ezQk7&T}yBr}?*GnG2&4N7cV8Rd>>GX*lW{ran`8&C{I^Ck$><*BX zol9TCBIx&6Zw$QuiWeW!qMGDBSZ^9rAu;be7=D(AxphB5eNCN9vEm5aJ(GcP#Sz-l zrido@V=>ok1nx3u7p=STo)o4{#nu1ia%|El=4ZZ}GE#s|3HmMe?bXAuBZ6D#q#S-- zt;6OWqt$R@Wp_%(0 z{FA?tYy)zehS5u@VDYJY7sdsd>X)ylo-iPYhMi}DVs?;*AjnyDxu!` zRMe$;Sn%{NNd~L4NzZacoeM^>#)*V+Gm~(}0ZWXKi*`xUHe*FEUUIt~&$-;FIfnyW zLa<6>5mrCE2I-4u^7{9TDI;|h1YaMJN;# za-JXcQipC1j--$0ngv>d6jo+`qbc*u#VZ;Y3cLbo?6Dcl%$EmJZ8s1Q#xe-6db znZj&&Cds^6PiwWd(6t_EbhdjB7sckN?WuuNx;~PROEm<0sR*6Ssi4vK%M1HzjWGV)B+#2Nh2k<( z#UH(|gE&15&bY_nwN7Pxf7Xw=R&S%A?tb2_bQ75PJ)kccvB2%jpx=TgbIA}#mSeeu z4VpTYk3V{!>c@{A{o_gKo;t#|%*^A-E+TT#!p}@UykEbPztl?|j67X>^gNcz^ zIIKsLCA)3`=@(~(?5I4uKTwj*eJx3gLe@}`P9YW))bni#KPX)2N51}83SI@~I3(*5 zJv_Kq6t8=e^p_o?)(UB`$%=uEJM@{3>Oz?EC<|_AG8L#1!pSRA5I(G|IzcV79K zK3~7((&48~VlRF6ZQONe+?7a)KlPX-=YVXVKC`aOhv#>;fJ9^&wF}9^g;#D1-0l}J z?}s;wi%(z!KF`6<>_wdBDq$!4=Ra^6Va97a+EIT)8is72Oc;8Sb}Zk)CywOM;n;qt z3aX?1vkGChY!oG7^AdJ z{P~(3j&2Et4~4Q~M^C|rczFz5*ONl+M}t|xh;4LM#(^!hOeHl%Asbh&$;{>EvJdZ- zaL~^|Oj?k7$DJ;qpMo1ZP5vWFUYErQE^K%w_=krTT;wXAzl2hWi6Xm0B~bjY z9d?YKL|Lb=(|*}mIQ8lx(mHmNb3QYeWxFNNWP@H%)5Syl zhofwN3k=K;h9xCmNq^1)s_^;E-#;~x$_*ZalJ$I+d3GO-Ts8xhitMrcSS49at^xb4 zfW}t?@lD!hHZ=4rY<;+rJ9SMD8 zET76=&AAI3TT`It(+Aq4KY&e?@TGb^SGs>^1U7o7<4U_>G;}~LuGF7^jfpu>^vMvO zmEIQ*_MJen8tRzoEW?Z??sM1UEd-{{1w3wJiv6Di2IA?tOu5|}`&Eyz-BQB)LFif! zxc8lwPhKs)_eo%j4!MQOhD)h;MI5=jS%_1XN#cf2a#$0siBoRhgj@xE)cPmM?v8Kb zWt#uN(42I3cw;T(H42|wQ9d|23G4?N$u!oyq0%xb82&91nny1Z=L_>8&-vqNTxufo z`*Dn7hNQCb6>d<~dYBID*9!NWw_qZ799jk?@utx`p>@hT3JB66>noD@SJX@g42Gir zXbVXD9D|w*7UO++1mN$u1Bb3e#hse@;WyefwcfVWW6q zP7t$Op^UR{97BcMRonx!7H*l}E3!U4fqgq?jdH&~avF04Cwa|RIytfebB<18<9sT4 z-&zOM@CaZlUJ9<65>=4mM`O!_J+Pd|tn``rYtl?m=CF~q*I2UMF| z1>fhm!rpa)Gc=%+U#M{&Pmd_WoXTuEoMg{NpH9HXPtIe!%W?Wubp*nPhp^0P+rcro zgPSm@mSW@o($`7H*}`iAcime8>$BA$Luw@bwfAG)3!0!)AH z{q>i4ZWce0bVb5Byfm5ShU9ZMGrr(y-D>LZ_JQ@`-}&CIV)9rM3bk_-$*5fcpC1~) z+J>9p<1RV;G{6a~t>Zyg=m-q?>dr@792a+U6EIzluUM;gRAkia!L7 zlV?q2TV8RHzA#91zonUrzpxZ9xIKlbW6n|QXD?26oH98ozJ{=nBYckEW@h_x1*Z6@ zq0gTAxOI9qr7q#cbFQ7BGM#LEy(6BP%00kIeJAz~k!zZ}|K|bU68q98^=-ri#jniDv{yCErQCkL=+^yDhq%y~RIr)`3WyTD;Na z1+|4WbWW{^yu>T{D=$*$z4Bn@Z0$+8$`$nGR|?a(wF;BpKZT!9CA8Jk(fAk8kdy zjCE$vwrDoJY6_(<+ToOJdj!seB;$q&Pf)vgI;B1GV84?GV*M6jzP~I({8UPXHd;O- z34sSUv_ym5cHPCybdjlb4rY~NAN=on1fG{qg(TF+TP|vBvBY#PSyy0?NlU{-ZxOfm zwFdO}*<-`RYxH)k3K@>O2#vEM*|iz|Sh9Yzz+^IJ(e1Oq!@UuLB(8zoZ6$o!1#G#j zGntNF#SS%J!2|bGSYOEm>|_Fa?P3Nq|2zRK-5&Du+nZpmUJ6sYRS#qDGK%^9lXuJZ zhE^pNynEpo%b2=`p4j!!vT^$$GSVMymS>|&;R^A>fNAJ5yppz9t%ve6DNL*M0UvF5 zhO0TU0;f0$-UaE$bSQKV9FV)ltAcPw-!AanC#v9%sI};IISPw1j^jtWrL6hkI2u$U z!#-UL261IFw*B&8H6Kr5sD3SjYodli;ifs{{<#$<+!AIILSM@(+lV5*1NcrzaMB5W050MbIB)qH zR+Ra$SN#(qVe>E+;ZwjAo@cXhZ<}1!ZHhn@xtSRBDIA<9p2Cl(N?5)34lw;a0Bt8! zLQwZ|R{6n*8`fG$dsoM>hY##<#~rB;$Zt*oUH+O+weMa)P2xx#-q25;LLGSQ z0>S0Fd^gO^*#l``+qnJeDp(xQCLWw1iRYY8aa;5!P(b+&u2eIVHC)g`;Z%+fhUBuQ zQRnDZ_97CykAm@O^KtWfIqsrk7|n?H0V}6q79bbTl zL_$`p^C&H^O9aj!8m_%}W2X#X!;-eu_)y@d9vAxW-*OT;zr)&?Q$CfqsFh}h$AzrP zHba=Z-kFvvx}xR!eCp%2)AsG-pm#?e73z!?W(Wd5y+f6YQ9HmTAM@ih-^Jmk^{+$> zGT+}BhQoM*TWz#uZPN-NGf%|1gF># z$otbr^HqfNZpe6?`uMTHnp9wAL#=RxZU*)S_k*mR6_`~efVgWedzh|3zvI-f#3%~Q zCyvAa^nXzS%HqhFR2DtRmlST4@f#!?gu+x*aLIel&u#kv<_;)x1)#f z&8jRuYWjaLH{m+{%r2w@d6(eLlmvc7;9HlE-7oonZ6UPXKL9t3PlCiPgV?X();RQ( z5xa48BbnA{;a-S?CKFj^C_M(=>k4c`vTwM>t-%*-x46>6ltt$r}xv zg#WyU!SOGlxN6HXSl;;vidRe%hlef`)xM8quB#@|zY#H1^;C(UBDzU$?28~e)*kB~ zRe+jJ5k)q+ASDK}{R-!?PWCDv8mG)ELp9i74LuY!PG<4?2?U#0a>K(0V}PG0@s|?e z`%4K>)O-UsH{{a7m$PV!uOrKRyaV?1Z-Bflis(GZh#3cjW1#v$+&=z3s5yDy*&F(7 zYWXERIZ6*(n&yE^aRE$SUqG8LTd)N`*SkDkJsBSS^~UZ#ACef5kH zu6BjNjrx0Vb>v}uk+vVMUie6_?S|vbCJ*G@MzLjH)pRFL#PUs4Nh8q$TjmLTi4aQ` zIpiFTOw?wblAj>;AJ408=P>kiGbQX=#=0$Tao-Cha9gfFyt;dvzv5tqfxR*K>hE%T z;O2p&euh%=^E9lPFU9|mAB$5%mD#X8j$|rpfDc}m@~0lIg(o*aZ&SH&lhSl}gg)W&*@Tc~%_&|3bN$={SWs>UnL-d#r zZZo8sLR+TuVi}&^HJLG&-B22^0*)N`kE?bWh_iQ{6?ct%L#{uXz*~JaJzcyHS7x}g zrOH>h+tYHfLisT4j}@{t<5vo^&DA1hcPqB)h81gS1~_onicM+$40#J)QE2R2vJrY^ zQO3vkU%!9C3xQdav2nQIOk64&|MVgsM8aOiCJ83jH;Yp&7^H-^K!5mAp>Hk8avU{r zn42^7`Np#(@0-xwe+bm}sNpHi<@ol}ZT?!+Wg53}KRjpqxsVCIOj2PV49nnH+_Iar zXUtfpr162XbOW4Tq>n8z0^{xDQQq9UlUz$GZ`8F2CJbCxYU`W(Y_^o^(7nJbt{0?+H)-b<}&z2ceoVSMsc&l zd2WBX3IFSy6^gspphH^&CwHlyzFQ4u-Zq~oddy?e(;kBPw#u|~sz~g1;RNkDWDNU; zJcgZHCD`t$!Om?O))@Un7JlD(4WkAq;SotYYBVb3MiwuJlohx5l>+tq0Hww`)Huss+$P$HBi^dg`Jact2V~KzU6K8a z&cl?#-CV?KUHmemhFiC25}p>AGmZz%*ncOAc&+9?@KJI#J6EX9*2TMXXFA+)PK7x* zozSN|8#%Os}LppLbBz2suc)?1uKY|GB)mHV2g!`hbpUE9lxAu$J1VJYPBs z2Xke#WVAAqpS*(C`-5oOo<#rj-Pp$g^VyG^+fYkI7LWJzLu>3wtpB3M7R{7kKkj^@ zhY!BMxsq7+(CG)nn{8y>roXwreF>txk5iysWe(`&OQ4LV;0l*W7P{rc~q$ z#KBs_F?Y!uT09hufg;@zi@YQQ^YL;^~K%CUeO|>J>;_?gw?FlVvA4I zQCD>x-)2$3*+l6xsRJ)Tu45p)jFx6?1Mh)i+g(!nypDMtFNQy6-mL#pFb%sM4|eT; zK+D(*f+o1LUB}wQ*VNKsUS=PsU%edEmm>xm`0Dc{-j}$#i=ST#wlkR%vMRnVn1c3DdaxB7$tHGFMpx8IukJKv?BME z{|Bdr3e2UT1Mu(UYp%vHl)~pLpz$(KoE{s1(o;^;67xNL$G(wdP*p~ru{*@Nzrw-I zejeK~E0YVk9nJLmKTws0BhB=8|;u_%hR|Eyc)670>5T=a|VoYWV zi@LReMm`w}@23^;eJ9?6>2V``crXFDf+KjWU^A=z)k@PFW!T1p0vA;>gSQ-E#k2ejmCVE?{B{(ek4iE>A`eV<)XgV|-Q+ z%B;$Plt67>W0nt?yioyD>)~w8Mps-tsf6Sb&ryby9vd1`2v_g9lS|`nY+3gdj8}ef z`EPY04gI^7PT%U{yg%&1ypfjd%R5DOEzk{B?g^ciTU~I-%mZ#+VK8x=8J)jv1$nt6 znN61ro{88(L6eVA_o7w!GAu}N%NgVMfCYFqg2Q^{B~-dSi>&m%kjq0|e#fI9psTq- zG-1yW-nruvRo_--#X%Ze<;OoHXY4{(VvMk9svq~?qjV_QKbzlQC_LAK+u@N_1B`dM z19<|2FL*~Mm^VJAyLEEhGHxxsoj!-%p|E<6&3R1n%D2vsARn zQY`VWT=)&0D@ege7jsoAQ30z9Z z2~o%9L@Yyjj9-_`j8n#dh3*zoyxB}6UoPPG=52?4LO0~*SlrH6{FbWSl=jx2t-St*o@qRzZTFMd&A@sncHMzR6%x4i;St`9 zTgG3=*2K~Oy13)d&XQNiQuf_9fzO+zNcQy!ENagNn>hLwz{f%Ia_iV>q^G zoF2BnmB(l)fj@Wu9F-)8QQ*S8@T1ciw2gAv&$SbHCHL?AVEf7VGJOUtn4Zo#zI5b5 z)2G8zqw)BpSQ3{;FG52h+o7c~nF<$+$#?8C_`mBw<9G)78@iD5J`>(HHJKewTgL}B z_wq3sW7zV^J4N}&(%7L>cW7IhJr2)QW`X=S(I2}Iw6`p#Z$fVJMc4||9BWGV&;RFQ z(mRa)d-IQUo5b|u}p!3`V^kd*aC<*Qc{cA3)8&)n_Bz?- z`7sNL43@gt43hue5Li=h>FD&4%&bzDweG%6R&&HK-Om%H@>7}4(~+pO+Ym0E`65c0 z;J`~(J?H1VNM^eRO{SUyPRy(>6&$&E>^%E}SMzbBn;s9jxkZ<0{N=-ZT(C8Jv#SL3 zgpRA(?QPVvH46T|j>HiyhV1;fSn_>r#uj>;xJ}Qj+3!XhuHE@50+uVu2 zKb@6tzD-$aS6#dw#^A?CY2xG8HCgN*fk`zLF+gfJA3iqUW#{^KTKd&cBwx)_T=`@s z;m|`nmOiIjJyBS4(tw?*d?VgDe+tMiU5eM6joG@rU9di6B#t}o%1vt4U};B#plhWB ziy8felrEnE*Llx4i_h1%nT=xJE;$sA@7WImWeJ9hCF!*O5Ij>gkN$AdOfM=L{)QHD z!`vdF(=QE9bG5wHW(l^g#RivE8bXAz5>20z1IuD<;bn{`won#kr%B@8<_XMto+h1j znT(QZwQ#NS8-3$`a<}JA<_^@k)24r3pg+j~7M}HC$+t%foqByVI=w*f103hQLnHau zQ+g<4k}1ZC&%y}30ZcJ`G|W=Dz_tB-KuQTY49g*M&gzCKa}` zZ#KDWJ)za}O{hmr=mE)J0qf_=dgih!VW$F z*|h#Bdgc9|?`>1Xx!_2i7TcKLYYh^~uHhChzXx|0JSIg0OB}c?2)xUWyT~4~#ehZW zG-zfp-re+uuU2%vc|A2I%i7IBvcS;{4T4 zQ2C!G{%>PCu3HfW;bYX<5N{K*9hCyg_5bLY(<^EjVn%sST!kGC55C1KDWD@5=Z?vt z7y~6%9;(R)OdJJa{T|H7HJH9FOyjl)yF>Z%4$ffj0M_Si!6w+=h1*idO{tAwg?+== ziA4-bhBx)usW*r{6ZOzC zV>mPIKSAj)b_3@t?Cj)c(n7C`6y?#xy*_A!iq+3~-eDjbe-+^uftAv&Fq_*GdXLwd zK11+(?|{Hr6X<#5Vt)9u7GeH91!@;Nu^WyF^w#Sla5WBHCznQTS9OTg^Oyn&VnJ)f+AIZg9d&YJmA4LI5gLqDW6+FHCFw6%q?3?@4wEqteeVWzV4(y+1hwjejwuyMpA_`1FyTI z;fm6HX0>l3JdwOd3#&C@(A^fey+Vf#ed|qE&A-y$?RP2V^AcR`SW8PjIpDXb@tC_? zojgUo*6*XH*E>o2zAon8dd_bgoDAAEhs7gxDurFqR&?6z26llyXsTaE6QXpOUrsfo zcBiA6RRXAgX#iP|er`{=9Xw1pAb5{GdFk3|7@`$UzdB~YiqdR)VUq$|TaCxX6o4-?1;4WrBvdcDTz93?i4u$-x+wIgi)C}b`!syKMe{g#4 zLL9h(Aa*U_za1M{s!ay{J#tsX-Mj&b1y0Pwz8&Ta&m)OZLN3Ps3-!rcQMCDBRFVEj z%`WG}^Zv-A1|J7beLUZgDs=o^C$X?~6Pb=>EA`xS#bp~qsqL^Kd+#KJT4S}b_3T)7D1m2A__J*yb>Oa?rBB*>`ES10prUd-)jl7A&WDw8=jWIF$Y;K| zf29U@<8dm;{E%YbK1^p_(+c4F=Fb&1U*+ISk|MzSqj22cn6vI5$UI{$>FfQ=AidrS zC%Zg`?(6D&Rmdmt+3sVot|Jv!tu+O;0W~nV>j9L^2n6wcF<95{<{qbpu^VZIc&PLr zm7Tu;ZDXY1^UZWhJhKbUJ-X;|(F~Y=GaY819nMVD!gsO#ky)(tL@nJIJdf9jM_Q4?F?Y%NFyP>7 z>^xJ+NnLfWI5G4#@hQ5@s5OGDJB#_Ir_Ru>zK_g4XRtF`{#4N`4|xUypvzzncTXXU zl5WM}#*Zw|o$FAmTn!!+ji+?5@47cy=Q!KnOL;QP$2qmM7_Fyg5V=yAKq zMMvP-PqxJ1);9 zvvAmF1!nQRjTW?pGbISb0Nq76aJ3n8StKyN@B8DD{)f(%63^-8G%ov}N5z!b1hz@*<8t+NM)@SF4bvS;x`gT2d7{xEG}(`R41( z@!$K!;Bu={+_giNO*RdEWAn<4?Oh1QUN%bMFQN@90Y57VOio-}<)I=83FUDPK0&0RYc zSl1WgZ!``e$4~#k24{j?FJCMiod`FzXTtN|1lYVzmks%ugjFSLSc^_OXZX8|tKr8& z*gGk9x^^mivd4k-uYSq*{Adv!KIVk+i*G=y?;?u%8%L+M2zlSHGjRCM_w=XX6l{f8 zAj(eE!7xYVL35 zGgv;li0@7A0pUx_H1)^uN*3!x`E_TxbKZSa?<{b2-MbfI0Q#>r@R-{@ehKS{=4)hVWe9FciZMIgohV6*z4$ z2^CFp`F8~dJojri9sh6{E}t-9uZ&i+d+#M_+lde?S~i91es+SSTRv14cXDkaS!NTc z4sR9*(-cvIcw}V~tQ;dR>bV^aRb^H*bxSops5nE9&-HNQ&+jJK#?#r|W%PE*9Pk{z ziv4b?q=lZ5P{*uUzKXGEt!)zQab5y7J&CmR9x}7FmE7dNrkt_)N#0-nCYM$CoaX=9 zf}X4HWldBD?B) znTqGk!OuH}vWp*YQCsIQ8rCMyP97Refo0+FP@0G8Y3(pb+K4p04^yE+6P(HNrL{d` z((ULLdW^Z$=k0>FMiwkjc@r+~>Z1`~b4Y!=6#J!P1r_V&@rR6K>5qdfX}mNb+8hoh zq1w!MJfr7V+d2CVJ!bW1I*FfavOKhAnF|0n1qVQg>q5>#Hwl#bR~e)>Y=tHF=TPpl;uva zKuz<{qO)>QbluzvOjEDHmBC|irG_I$yg5OSd+za`0w-s$o->Q($MQzrmat*&L#Uas z!Nt+~C7f(3q3mb7*sFGb+-sYLruGcH7muMkO27EOrB!^1!6{l*U+ChKq==7$yvXb7 zK)h8iiCe7dC?NI%EwYFsy&(#`RQ)yhv_W75d0pay{Rc5O!MXXV$&ZBynI~QO3_hI? z!bpv^K)I(NUC2EfR0go7mPj1dc$#uHyK;wJ1_>R~9grY+yykBC3**Pc^Qr?L^Es-w zK(A{s#Qod=B6CarYSsy=bCajpi3^!z&n0*%t&1XucQ93Z6Z83-1E)tRQ=)VT$S5?5 z?s++2@Ay?P{*E-ec%X`IZfJljiz8V3;UxGu%8gyU7=@E%e!}Pjc@^vHcA!RpKIc>G z$A-Os!AVzIN&NUZlxQOW&Xs_cXAV++$4FI1C(SqnEe};&si*xU|&^lk?YOBqOIq9N&mDWjvZ4CW+y+=Ru5S! zaF(ZIO1|7pX>W>rEDL$>eqWmRWd&1l))UWbvt;jOGQ?ju+@{m^Vu-jh4s&}~@SkVe zgWYrm{IyB=-v;hrIZ291D@1_yKs&rDX925sT!4IOfq|K*0fQ>Wv2!tsOjkP! z=ksN7Ibi{Olr|UGLJz0~Iz@6egXmA3CS?sDf|0BI@N!BEEO=qcR@|}UnaKivhucTq z^XWX&YO`RSbsp60_LbhsDA1XdHWvEl zgW1`I!kuQ(IV$8*U`y%-KB?FO_b4TSdGI}WZoLXO&78`76vyCye}0SR?p%&Lj+-(i zLvQ%p+(IW*meOPCdeMSvJ5I}V5_=(Vh#z0*rF&{6^x(#JGHLT<*WP}hZxOcz#^QNO z%|8c{>2m1oaUDvk_lsWC2rkJVZZPMK7b`QZgR+M8)U{?K#U7Kz>q1VtRNEi92!Hx9 zb`sgOYxAi`r(nGe;!MkKDxDvU*W&wWen&5{Uvkti=?uTASr3e|0m4s7cLm@1 zou3)Uxi|Y^-JWt@?zkT8S!D&IyMm$8;xV~q_Hffn=CO;d9=z1D5{OKxr91yJDSojj z?$~PtpX?HZysGw^_{B!)X~a)gRsPD z4!ara%N%dpgVfG`k}guA6B%9bBDjbK2wcfHPjm8~dY;=IFqPhw4rf}oF7WKkBBGGH zd{ov%Xb_&`oP#0THY$)A*?KVZ4L>1QC?tneNV58E-vt-OHo93i5oHF3;V$3bpg6&g z6u;ahi3X48q%%;giv)^6=_#W+0##?FOf@%k{ z70FP^fqZJcZA5CS>A0wPAWbY!gM|Z^QQl5zCiy||f;B4PoZZ3f%wP+4@TWSn(HRU>M24d{K zKb%zi847%>C$3tNjmMr1!!b1mZ1_!IeE8~%Sozx;RBxDx8Xp&-M^rAo+O1BXCBBi# zvwu(_uwCi~ctFFPB$Q4vV$4y6Irm304YMzNfhdahzZ}ASpAVQ&u^(rI&%((6hVZYg zXR#YAc3^S7A8NHo<7LrSG|&QU4Y>flD?%}9Xfj?pydM3AUIg`hE~Mjho<@B-MrC#3 zE;Wvp*p?>jdUmaJ3iCV&E=^i4XR>edBLpX4{?)DY{-rfb|CEBOcV|Gcr7XH<*n*|) zUi>*nis?_+qpv%q*sbiH^tQi`WCA?!4>ybggKvtY?ij+gwt?tTb`7fLWkO2NRNge~ zI1CE?z#FLhf$XvrT6Swc?9z**xRP2*PLgD!-n&4|v>bM@bv`_^&%xrkk}Te$5_Y-j z^W9DPw5F+>OjlfkI6G-H(lY1tr;yYgJ+^%Q6 zF!Y}+=os7Ljffe{Yvl=A{l^N!2$d*gV!RN9-0iiXNc>bb9rq^J-jBSjiW zgi;cHDbe21lpSRzlAilIWEBdLWRHkYvWf7!fB*GQ^?E(yoO562^LfA5z^fb5?BnJF zj2n53pS&~;0{ia6euo3}dEQp;eYYIIlb~g9A1atL&%iZc!rodU=A`fm3`>}aEyL)#fRw|}~iI)-%V8fV&Po&VcA7`KROMq)J8+Zqb}%gc4JmL z)~s-oD+?OE9o#;=qN{sv2-#FE_WhV4rkXqf*%^{}!FM#Sh!jKI!X>Qkz%*0ooa-eKE4!Op79X-s0Z3Y_wd^fOu~aN+xgl1 zZSeU&dm4N74YWvU!=$6vX_om`nD6lwE*)4XIOMfRb-fj9TRnw*Ma_H=PGJh>iOgly z0SXfj#`zhSxWh*m;nbQaatTr4djq9t+67@PeVm7rH_Ksj-*0ezzg#eUi6Q&masKJh zg)B0-ha0@qo(zxYQ`Tl9b~@!Nd1;RY57ZWEq`6>U!9L#fkQu9v_(KzIigA9!7I@P# zg2hR?GqZuSG2s4b(C`}yKkfwZdB--wfQTjhfZ0Rw8u){e&KIb+zCr(myrUBtx5-Oy z6_!U`2E!j6a5_Gi8Rl!Tr`Hl_-NSv*5q1(fWQ2UMY!*y=77Uq*F)X!t7;r0-QMz1- zHER{o6I%n;TO&j3FRq8b!(&8C&MJ`C&qN`|H5H1s4QI8_a>OF9ACMlzL~nGAG4I>U zx?^{?V#t+eAUapZt*HAe{uNL{5%YLbdzvGCHd zoY;?Em!v@B$x&>U#0$FQGlulE63{1YHdo!a7i%`Zqnbr#I5yFdn|aKZy&oh6SeXcq z{sjv>yT{ZNT*Ma5uH-ZHQ`yqz^rA3~wx}+ccJz{YG)KO?4^jhQh=W$b11`4wpdyGo}Y-y3k>iDtP zc!8%u(SxCEUkAOK<;<_Gspn59%VWU6SNz4L85FEGgr;`zqWmXY;GO>h_H|pIsP&&Q zE3WDUt-e0U9 zS1yOZ`(>bW(;SUo_dvk*m3T&T2;dNVT=U44u1av+fbb{0wz?rVX2xFZc`1fICh&Y~ z6j4USon6_RfiH*H;i0}rd=>MNZeBVdxZ1X&lKn0$pB2lFj{gjoljq{Vq3I}F(Mjt~ z%qUTFvq;ZsIp|6{!CG@qDAbb16}-UI(~yT>Vln~N& zs}mT7d4hLu7Fi3v;)QpuX}^ydbIlycnk#0)@)Tv-ulXs9n+QwLu1E^o(F# zpordI{Ly3KR~nTq%-eNy(Z73*z~ndNW3`skDTVuvpUULvvGgAPfOioEM$Dvw?jE@G z@DUb8=W+j@mGS2i9?`8`?`gw)bIxt{8k~`A2}v`{p`CtmWj5(xJ$5WPPR(IM2XWI>DoEOK*t2GdeZ{hrv!JR&nnYpxyeeT#{$#i8@OjrjZ zgIe5S7RVM#3*H_jbM9JHrQ-~>COGiSm)4uAGi}L{*dn;%j!FAKQPy(^m}tV@)`!Ar z1v_|n`Y}k|T!N9O&r;d}A9!8324QX{D;T^0qq7EMNTP!%*2fkD_C$i#&v1T~d?F2* z;f1v)3;y@>c`E8sbg2dSD8V$=Wq~x_ljeyo?eB+We-Su<_WMZ zUnGhi{fM9Q;0t%+hbgSts)0)jlu;_*8cVk(;IkEv=-3=ZjCpDSM*DT~i|bihGd-WK znx=?-8+thXfFn@-;gWb)ayHc)55%CNO!#agg>~wKSn-`snENZ9q~lJ*OqW==ZT<_Q zuF0`Kw$d>0TQ@|hrt|uCVZduWhs-{qQ}ITFiQAfBghU676j&i@$K&vXjTuHYjl|X) zk4Z~99_9^RBY3}pY1RT|hEh6Y@p&?x`|iMNuDuRtj!YI`R$q*Rm)C*EJ|X)tXdf(= z8!Ip`ZFsfJD`37pn>O}XV{!Kd>ay>L3#UKVjpG;4(oP-zdrZ?Qj-jd7E?D>xZwU!@6a5m~~)3X&uX zz)pMR-#(WOivV2T(ccIJ*k4qN^5r4whMB4g|+Tk3V+S7 z-SPXNPf)-04*4rtu=i3=ux6oG+7klSKy} zCg3jjui%xDM>-*v+)m$0d^8{)6Ysu+2b=xy(2HZ7^l&@Y7r~)<@KqQ*e+sMrm&Tk; z$KkTwUT7_3IGlD_GV8P|PVwGoyeM~;5A#~Vn=BKUH2#;h5i6X}k#&^7YwsP+X&;fGH?mQ%@KQNUNjGM#wI<|0 zS_>bp5DBMed$zE+UCj5X;>7wY+Ivs%4Eras0k3t~;!_J9I|lECxw#|Q_@xHaCNjp0 zugwH@*in8(D^b*dKd@*=Da{bFbDXvge(33>Goz&hMpG81JMD&pZXda{s~KPwl!dQV z2B5y!8k_Aa;gfqdjT#jR?WI{Pxh4kvLKUF&>v1$U3&gjp7PG%&A5)vWI#xf4;wuJ3 zLF2aHjya6yUS(v_iY24jikH67IuVc3*}H4O?g!kb(!>ukg1n)eC*& zKO(!4`>AW?BucHeVcGAjDb*mJbyhIkzNr{)O?Yq^@pFm?<2v&-8YlZO&x3&{e)}hrD*HlU;H=K!`QGq1Z;QevqnpS zvlte}t|eB3jN&dn=igZTVe%PH+PP5I2N#-oK$vsC*uh^rC1MH#j9~oD4VboZu_!@v zD9gJLDX{vN;lf#Oc&^`;cGo@T+&`>_sT-o%$o0$E`HXp(FjkU@4+hdfTjA%(SpbsT zp~%mhWo|KGmHP)_iCq@%RN4X-`8Jfi%z*vg^oDYWb?_$lRj_%`Nc{L!_#aV40;|mz zXHJQQ4uN;2VOtGnJZ4edoAnT_zJ(21ssI@-7tr_8cTi8sAj_j>cxQNtMK}nqSAr->!JciYl$z2{GsE+#&ec`Vp&pf6>5CcU}tKy*pLPdJp9^(swRG=PuV6yULXfPyN$!3^5vj4 z%~tFuRmxUAn8jL#*MiJ+Bd}R1BJq|icHl${|L|Bc8XtNEp5EE)x64Mn(B=lqq~o}G zvBje5%=o&pnpO(hycm6aGWn+aj_}Cm756^eNhG@W6LxLy2hCf%;E0C`z7-g5B@bp} zLroWI7VV}F5khxq>_15VwE*GodEoXeglYekQDyUck{D=6Gwt@%!_oHi_72bP#-0HU z*Ci}yrQo){`U9rvnJ|-tWSC=;Bx?Nof+VNe;U32fz41~jAsEYi zuzgcDH5}N2-KvNk(V1MU(IGzHwuCeNQUqTe^!a<9587Ycriy{05;*7 ziL%V6Qx$EbA9MYim09_*g=l?v0>5Em41L@c$5g|euwSMHj*UMG9tXDJ4P+qyuTQk@ zyECc@_tJ~MorhmV(5HPI^8cbqv#kuixZ}cjdb29<=cZ}knmOX`x zqf`RG|=j}NNJ10D%q7#;E@7mMQ zueY3rUYbl|IT`lsi#*8{j)6~_#k@s)9yUo05_@PLr3p>jX&&WaW4Mu|YG9 zo!Uyn>ci-jb2q(d$so--c^vDGSbS{?Qz_iWo!(nTXL6{hg z)B=;_6X@9v0|>EA!I;C6=sK{O#=E&f!I_b4l+-|2&70wBgG%nTO&u7Xxrx`xdvU$h zKsX$DgyrdFVz1pJIQ38qq8BrAlo50 zueBDJdM)PGsf=U~g&9S`$n|9ZTe#=+oW%@-<=F(8PEa}bS6nQ3ldM||Ay)b{td(4E-tOkS5i^t>;qXPFVzJuzSVR)s`71teF1KX=5Av1P1*y)_atuvdb$zc(8 z`mVxSkFyFPa+nWOoH`AE#OtyP(_pDqL7Z<>JBR4)T=_pCfEu9y$LRU^34J+L{!mn=T+fV}~e_`EAkaO=k*T`0O+M#fvzaf5pvs?QjMcLvo0e>0hx{0?J=>psJR&k5Y& zIxCuHoh{^!tkG8c7LC#$&JO!^l8#0K=?Yz=cYjt0Z1_!>q?8AbmCq3K8H4C0!W%?L?5K08L-b>>ouGDBJ ze|dKqMQC-CgX9fxTYL)p6!yUFLO^r|`9R$A&V=o4uZP>~S7TLQ6*qjuI0|oF z$Z&Bjowq*>gHmIF8}OTUYqWDCmR9j@;qT$2ZX?{gvkW!lgn7dE`(h)MW(FJc@xLoh?0d?`j7jD*#HP&BdIi(lyocV0AHe#nZ7}{;E!kJ4 zz>sS?%+X^Nn|LS{gT5c4oi_sTcCZ&k?rZ?pXYQ=JXaHOGdoV5ZaKzZCHW;4J3<(0C z=Uz%X4ZeShEB#=DX>*sc;1zW+x>m%T1{Gp%W(Qq=V~p8rR^a$)i-a!a7?@w&$JJeY z2E*L)IoZ+#aYJ7x6pkN;-Yz$A@Q>@9$_t>0X7gFGYqCiD;0X9nq$FgZydi%20zAGw zoO$Ogr3_ayh}v})HtoAZ+D(FM)X<6jXI?|j7RBV#Dnfo;E2-=L7B3tue6P#@qMYqs zK48Qs+R?C&|7f@q1|4>Vs1bWWEj}M-A16l$FjE{^PngJXt5 zf{=AOs;FX#F5n6HJvLec+3a6+=1{9@4?EcqF zi@*5Oo)0n@mw5zgH)YVGx_a*Q`wWtC-GzJU41Bxd0`xMZh5SJ=TW+~V<}C`u4FEEM_@+L5Vn5kSIDiGV;lAnJULWJmF5g)R_%tW zC=dE?`Cl^dl;_Ls?AU`0ZFE1=4vX&j!kuuUx(#0-v`QA9F6-iloR-Ck;02taj*uCi zv66Gnf5rW&(BVSQ9EQI;OQ5FuD|c?^C$3}8K9O5tJZXNK2+w}F3tjR;2#*x{<8sF6 zm9-l+Y$mf$g<{9tuxm8&e3#ID+RD0A?C?&T0lKfsfJ~aq9e%MBmxTM^MZL{j!Nx2m zRWuf#XP*DY9yxinQPZN$skqr`s?=faGAgNXkq z0f`TGz-t3Hw(~fnG6SLWI4+jXwG>nG-I?s{<*8^?tp(RZ-||CGD`S7|1S+=3gxQ_B z=)F~N9DLCQdk0%K6_hYgxf>+k3E#)41{yv#KpX>#Y*eEmMjg#1@2%%4oBIaV{0_8v zaU9fJhLg#V7%=&+4%3Fp;H@#|KyL1MLYrPlHB&~GzZa|i8PMbzad1BEA2g>5%$Att zln`S~y9$M#)`@H6&@+cMjaO%-KFDMzO-Et1gar>S@yF)$PaQxTE z*+}(*y`2t@ZRr3_e|Nt3zme?geM|W0euYMaO@l?pRne^Yl%xLhX*5Ai2A*m(QQ~+V zoS8EUjc3@PQ-{#6dJqF%f*VL`o)!KIa>t0nGO&Krc~I36af&PNkxAz`=4&;JW{+EX zcGZs(@Es?xE>qVr+j%K)YMm}K?4OGtm)l}dz>x9xj?hbNxN;$P-%}IL4V2EVn{Xlmb+nG*MjnUKrk!fS-gjntR$w6211Pk0rmj zzN~eSVHC`U{p^A%GaV@|bQ1e^Q*a13Hq(8l4G@xF#J8>8f>oB|gid8QY-lsZ3->N? z56h%+d{G)2{pf{flQQ^6K~?nTW*d1M#WRPa`E%dw6edgezRNH zb`aJGIqxq%({PdC_3eL_=@`?giIZdpvCCC{Q1y2T{`1`kbJ~W%!99OqTKx=<~hDAk(p(<%6 zzQ1q@`Z_B3VavCW#70ZHdv_UBOp{DATBs0SKR5F_1#A@q=)eRq+JO{(ADB zGX~IBwLdUhU7ybFdk49Vv+05KEIe?{FGBOLto^fidN~r7pxzG$+ znpe16IipGabtMa|T|$aN7C_-)4J{6f?zOgkyB_YEySak@_@!rgyTpY@MFCE4XwW_PbHE3l~6X?uwq(i<<;#Hm9Bt3myb$^W1pe>fO7M)%;)+9r&gpSau&s!OD@CrXcFTUQOurSJ zTzVl`&Rcvg=Bv2jjTk)UC6IGT9?tls%brcYz#aNCooNO}a(#^jpynP=+y2ai@2BSQ z${KIzoS7f^bWRuYI6*9QRS)PNH^w#YouWtCyTQyvPC!KmoQ)jrgl5|xfS+|BGumCp zuYYR7+$0Ow%(`lRdv_tp*8PSNuXXShTgXDjMl-VoN%(T43jVbOaM3?XAEzDX{a=q} z5fg{7MxhRE)Rsf@lYDfR|qah<7zkWEv>+zpN3_h=oP=9!EmT?gR2JyRjStd<@a9EULyi&*U+Q+O1$ z3#LAmfZ&2saQvPnO1IvM+r5-o>za7>`=JBGrf*}3+f2x0!)Wg0b{G2M@d<2FkMrT( zl5G3(N-(?ilfE1`W_78)tkTjAxksr?csc~V-xloaf4&eaSqzh>ZQ*W4h4KR)D=-yq z3`trNi*mF_p+yuVacUb$I#&CSZNdGaw(^vtC$Gdr5`Vj->U zTm@3k68IE@ZJ@a!Sn!pJ-{EH45&F zGictnc8KarK-2$jz^S2~+^m>zR+6cLy0<#H+~}n^<=tAM0wJ@0V>re)kT8Q@%4F6% z(JA2^)Avn_ZXItYtBgHl7N&!P$2NmfP!3HLxM>$U{4wMEW^{VjP9=K-Io(hBFh=0? zAGN69rxZ)T-Q7~G)Fy*(rR~&z=^%`9JqnMW>H$r0APgOWeL;6P@9*32>QQNuX!N15 z0~0ZSq$E=rv=c_XkAtN$bMa5K9-G&_L^$_Og}%*yx$T`x*m37%>O0*BZ&z>S-W2EZ zYkXI;>Hltvi(O{I`)EB3bT8$i|0OY912wMQzJfb{bvSFx{z8*JrqOlcSg!LM`fx}B zp1-zZOGQT@t;7gh?oMVdeG{;5yDHnPwn~(B`ynlPnFJ4-j={A)4|t#u$o5M%Q{JKF zxIvf!r@ZqNLS#Z8yyy`OHd7!!m16p!K9JVg7Kyy0jDY=o4rX5tzzS#Lhn^ouzSZ_@ zi_#42366vT8~edMy^ETsnxjO&HqO3i!fY;i*Ik>E1tAhy{GkzdXm3yx7-i-`TA&^_ z_9wHriV)_sJpj(RPUE9o1~Hv?@g%&l(79(JZu9sl?wV%6Jb6vF_T4t_M|%mUGR%gx znl+Ncr*X_SZ6sxC453Y}@;H5m71GNNuB9?vd`)sIT{JDGFwd>xH#gPUq8E>Z_?tDh zFT6xoC0%GjiXHp;ORzx7_o z-0&Ps-*<~%SN72HFKh6Ibs2nU4P+A+=JMnFdG64!Tv~K~HrCpnhbBJ-reC^`)AoZ^V>-hO=SL_Aq8e3>FFR-|VyntVMXvTV=PB<;xqH!alU?ST%nx{xxZQSB0D!HF)!M0hnloiRU~tr`3z}$zY5nb$;bxx#vts z({{s6XG&ntN^O!i_)01+%2fF$jlEhjOfEV>f2oGv%R`+~ngz-^6)EU6^MtUA-`qWtraNFQ3(6WkXC! zc4z?oNV-bTgpTUz$6@51Dj{C6O9iCu1~J3OWu$p2pVxisOOuaPLiW(*u+~jji*oAd z${T_A5E6oeE`NZ*E@3c1*APD)F=7KM1n+LVIVj|XqJP>2C^lJ!HUCWpW+#u2j@Ysz zOMwj<|BsU|>=kLQc*0+vIFOPjr?LZE7qQbFv)O~!lLTtD7skpxgp_tk^!X9Z+y|tS z!M74{6&Svw{^i5?dFeQR^e!0x(ux)A8pFP|T;aX1L}0(RIo?>LfB|RCP<8Klnl35i zoHK4x_VhC##r}|7L>W~*K1ZteuE6pWhv1ZP1S;M7sNhR-Lneu5cH!s?jEWC3Wz3 zeoGHKBgKaKF@i^X35yZVq7tSzxyOD*)Y#R>uPPb|+jPCyk;o$Yx2y%eUhd(JOjqGg z@z=Ror}eOQ=^x4&p~{xtzeid7E>i9jJskb&5t(n=4;EjRuuE|!n9>x&;wRYR_IeFk z;6Dy$9hT*zmLI3M1zs#^?p3lVx>eVymk9@6=&`~XW~{g99Q<(i#0k}gXq6z%R;WtQ z?j;F$daEJ8L21N|O32%Uvw4y;SmzS963u17(PKCqD($7#^r^H_`UaOb@F4u@yZ|pURq3*O4w)9dqYIjn zR8X`6>?hQNVfGhp<$N2g6Bx-cPGwNhY{_NbsNw!>$)-b75AbVz9+Oj=CsuD71^w5b zg3*%uqUG*;xwsrJ@yTtfs5j;f1qCc4l~)^};!h%HY|;m}2F!s&n(_RC*TeaCtNHBI z@H`lvK9ISV@8Uf#yK<%x@gmvtJ@iz<5>8ED$W@liqR>}=X$*X&)B{)GlJ$JH`p-*x z)oTh>vNlvQIE-(Z+$B0>`HvgU-jduk4%O~2WG|h4aJ2t6=6x#^eLWuVe?kQMr=1h~ zeAOMF`Lw{i)y?FH)6wX66uw$L6jrGU&l}8+Hs4ey9cv{P5386BQiN^2=Cs(X zTJ&7)o8zv|bey*&9Twm1<~|w@V>v~tLT1(*-@P@ay!9ucZ+|%`Mb<-K_DI-zWE`D5 zK8Dr%7{O2fS$Na#5tUm^rZmTEa9~q9-R^z}8WH>W)z=i*PUgnyyKR}fSt9!>6-he1 zD;({g>#)AH%i*rG4Q}30%uh*N#y%@)(DR@W=EaQ?JeSfS|3DD~>n4)$hgtX`WpMvE zh5Uo;s7<;HtRHoYHs0LD^M;eKFn$i5kSpQOIH<#L(LZj|`e(dWZ8y!jF#&DTTj+CU zKTWyP4ZkbyP@K?RzpM}eQ%?zJz~Kv7ygksG2ZPvf0~=Pd>J}|hs^u$WvUng?k5Ux!p*!s&h-)X&i|0bW<(w0%i7%xiQf+`{{+zz29#qR*qSB^w zT+@aZ@V@^RNQQprW*r~GY)7|2=x_rz(Dy5ad>YM$Ij#ad=LULZ6wUOSRWT@BmLf%k zoSg4){_3YD>dCLCdvDa)&g~8;`X`G$Kc%Qe@gV37&)( zGqJGOlY6Z@j-0|aiFN0ToWGnfr}Pnz)DxC3j7xxcJ<>dmRNBQzD2);ld%Y%LHD3$PY+!i zoq`WdBEYdro4x4j;Gdb_AigH zy|q|wugnE_T7k*-HfWg`1IqLlp83pR4|Nn-tCuxP6FM}@%O}I&y3lg;)3NXaKElAp4CmK{OQ}Fb<#};J`=)v1GU+TxjuLlFT%Fa zGEhD(0j12E&5BrvV=U)VWa@3QIlc<-^_?Q?vNrx#sT%vS+yOr=PUH3^O`^Qc+3-F+ zg;p3Tu~O4e_M>Y)o8=|KiShkhyn_s9eL)Aa)%>yQpciw9)v>2Dk16ePxTE^dK=f8E z;^r8<=TAq-@tP8qAi7uyryZ;)YQ8-_e?JRaPej0iLqX6Y%r|Z8ed**O5qO#2gPiSB zFnYmru1YeIy<4#y-LB_~%riYP;&VRNW>kQ`_6|e4_Hf~IJs!Wm)u-INZrEkF4Hu6| zA-ES$3Ewlhr5}UvlgJe};b#k?cqz= z?UBD|cmt#U2^MV7yf36QvlQ;c72pOXGt#n9#LI`yL;ENd-oEBEr+Y}qcl;NM_Wsgr z+V}62HKmRF^v?kG$`lZMC&6;<9c162iTY6qT&WNa@&DGsRSNFn!70Bu=cEA`nH)_O zJyvK#Di|>?i+Nv;XIGPM04Mz(CQeBb{HEH_nh`}?lrxyBgf7=qoCtrM=i(K${oI-T zTS@buByHr(;ghX3yZ$E+8mks^ux13UT2%t>8~Z?K%MaesTn$#fHKSeChVb^b;4s@F zjl>m+GwgI|$g~?2ySxH?&v~$_#3K}NdoUjmy_a@h)JEH3h<*EJV4|fHS;x&0yN{lW z=9y#IzVlK1KyMNM_gOoC&F(Q>T6G?3b_B99yM|+`5~H}rF#>-@ff*fJ$bWiO3)Ua) z;nj#n-g~VQ8~bK9JE`^@ZuYo9uG)48XdTRqADyC<}5K2~Km|CF72L7D=>1?9Lb8x5l>iP53QV zU@lyW1liO-;ICZ-!A=vR^^n{FfUohroM+l(*iQ|N{5#lb2+ygnN+*Ng5;x{03JuekZN^GjTSg8 zK3}Nd+Bx3*Kng!;&rJ9s^Z?u+OkyK;41$5~y}ac0TxzZJ2d%STVd&;Nu-J4zJe;CS z*+Py_d&Wj|4b+0(_w(SN-(0$BQ|S=z)<)+YtGSfgL>wL*j{*H#Vc(WLJyUL$EI*9XsD+*cVK(?l70nZbyGms#_GIF7 z`nhlvDkwyQ`)z$_)YG8D50ivh_Iii>JVk6>RZN{W7a`NQ8#3n2zzNRkc^B zn61Cy!MzG{PE=!c;rgiLM9}!t946Jpv#|m%yY+enWYbS|(U=(ll~)3c3nisKJvI@a#olZ9g~+ z_Vq}ko6cuinSU3C{!N0{b05Q&b2H%NNM+U^Y7V!+gbWi>AUP=?uKxYZTP~DjF^?`$ zd)y^>n4XHcqt9>|gEdgE$^jDOBG~4iCYYT+gPzF9p>dwjL3{fP7EaToLF*?#!w+c~ zHpd=+h7ALK=ee{$PmvpaE{^jE(59$4YUolR@TNvOac93c(3`(86dG89yVs3mzay^l z!>pAtckB)}Mr;nZix`D;l+}%$@Rj~X_j83e=2GeEp_twgj>$dp_;|A)C<;*GgNX}7 zX~svsvFV1zJ3yo&pkN*$7ot_#vAu z9(U#^ZEn8>+S!Y^No!Am&$EM~jakddHRCS*c|#crRLDBFA+!l@6#YW8Tq5@YZ@yIi2re z`qodBgez#K++_Un^#nJv#Fy#Uy$3giu`E7L2{lUg(E9gdDO#T(lrzBMlXJjr)p``m zM^IV4h?O;&;uOWNu;oiO&5YFGde$7_M+i<)4_{xr(I0`^Q+L1}IbH6}btyLGV-$q0 zJxhscifq+xRZ0~23BL`)uz16La!*(d*F$2t!Hdh7<&m)@HOU-P-?!1R_e=Q)9sy7z zUy5-l1|pkABYZgSo2byIj_z-n1iK}-35=^Wek!;_`JTy;JKLF`|Joa_EnUcdRQ#Xq zPzXP?Qw2}eDR5J7gE6sTA1Fs#Tvku5IBSpMPbz|1QRj~McKBVm5h^<3kKsWN>Q?nkn zysL$zbW=>->C1Mxd%~#Mwv@Xu4Sn4@_>l+ha_ZxTvOqUablJZG8V}E8VU~-*W!M8y z8Ml`EVi?JKo5!%|D?gxIr5*O&Z{=iWx01v1c(N^jMdGkj__n{AE=@eb8(!Q9dW}KQ z+TRF`xnoSefOLpFq?p$+Jg`s?BKr$Cj;h-;Flnzi>HJ0$*I$0{OI? zpt-}3O&jzJwy&`g@=ydr<)d*^mOK_$AA{z}4`}G$B4!{}0*(U9G)6e*Zg_2uqPZJj zq*(}NH71ER7foar?y6vz=OU0XPNJ-d$6&9_HcqkQINa0;W-IRWgK|<3^KH5VuRdDg zsprx#`tt$!@!x0`FJuvCT<)Y-SA26yR)CI&a}j$>xs-u|2FN}dW0L& z@`@Z`x9K2&hHUiuyl2I4Jj6ZekaFf>764A%2et7^ElCOx$I zh8nKCdl&xAdk$>e0+im=19ODgYk6}TW){DO#W(br-{%Kni$E9LFZfQAO$7F_RRMdJ zmWi9U4`7oLWN=M{Ax^1MVWC;6DBCs}_a5n_ElsJUD7J^G_NjPzyD-;l7Cxf`FOuqw zC6u&F@C~nAiRqtGNa}(vmF?RACj@8MxsR1_&txA=kQu}zTJosnr-vYyp58pYb8DqhXS=E3^8~gO&$B0nzNW zbjauxKekG6R~fVm&t(@uv@QeN<<^OZ_PVo@BXyK!iY(Jdi&+M_z(v!$aJ-B0iSP9x zYv3rBHs%mUop{SvCiQaJ?z>^m{S#D?Hx64Y?U}z^F7_z$BvUq&tt^c|XPbD6$sEU2 zQ|$SO2*aup1`-Tp_n*CjmeHb$d}BDV*66Z@xQ!s*ra7n?0Ql(-AR>0 z`2}vw#5>I*MX zM%)2rA*;^v2K*LJQ5_GvZZE`_%z+$Dt7vV)U2^<+kUo|Rx$U_R>A#~w9<6&dX?ZV$ zD`tr}b?+h^+9ieoIGt98(n%G z{zuVyxaIhNalEuuRzs0WDy58~)N`NHRy1UVNNDRDp+pER?I9IXN|K}^sr20ELspbz zZz7SsS15k>?;q&8T%MlJ=bZQZ_4*k*7oSwBqm6<)uDH3FmHC*^(cl^Arx}iYbE>Fj z=O0k^z6YlNWn!eiB92bmCUl^6VC3RJX8uVRlWyzh*fSvw#s1w_teRTHHI|8$nA-2{hfT;>kz{(%8X(e%$tAjt-z+G zDWc)KK3KkSCxjiyflsehnQB8Q3=P;0gD(Ok7ArHG0RkiCj}^05N`{Z#2@tvGDcu-y z3~1G8tk}ZS(NWqEB6o<}TDcZ;2g@*@fxl>x!#LFNH^oQ;VWt`%gaM{0_1-D52GLJ9Xv>oz~5#7Ev9bfNT!z~!>Pq*Y#xv&$$ci8eJ zaK=~IA774!t5)`GDllH+Zt62pLn{4j67nIQBak~kg$?$wM%X5W zU*{Flg@kYLpe_u{YC5gqMk0=LL413`7}vDD5c+rucu2I0&HK8Y4lEx|4_!3y{tOxT zalwlf3=GAnfU$EMaV-*%XBGGv{fo1WJk89t>N1e&Vz{lIr zZR~cG`ECO@MjJy;whkL*cN&JSyvon$6nKd<{4kdTDpgOS`Ev&0wCD_|c+2x|J>6J~ zNfz68Uc@58BIvXG2)vy=l=;~j;bFOWc%?E7JBMh)*O8U%ocmIG^>!Ou^(i0Qgd9Y! z+#Vb^r~r7aPyEcZPn@JvCKs|}5lH(C#1c;f+@*9F3Iq>YdifN*Yn#Yw8ntnZ+8Q|M zV2=9Dt1)zg0sAlLA8oPYKz>~r+q>*2zvGZ_3+=wi+3qkzr=7Ey-H|bDPL(8EYIGYU z565A8yfK)MUBJ0s`bVDc=cDG<=dgR%McD9m0yZRH;y()izC~TLaejj{yfD2F^S6~i z)pJ(!KjO=Wc9dM2A?XN>VvP0;Fny7*f~BRubLrRl=VVIk+t3|?xn;+X-k_wXL^ zHRU{R{oREy_>(WM|1$;4rGLZgkXq1D7|)WPO5nnW!MNN>6@%m3;9Py1$g2GWT@L>V zPc9eWt$kWMBnph|B1Huhwpx0$r-2cae|Im7ZPEGy@ z_fCDJdY5^i8rXrcl9_yEln0CYZwx%R&<=n6EJE0;)lS_!t<@GqgjM>*9Vbox{ohSHv zI!!TEset{qO2L4kBG#$m!5&GNunS6Vu=(pmrgf(lChXD^yioJm=K4tML*+cxFByfK zr%Pdpz$~%~RmNorm+8E(4i;RgheJ7|#qYh=p}nX@6yNtqe6%qMuX_6n3@~%i{{!Fw4 z?(lTO)(3`g;ej;!IcPCvdc~t`=sdD_eL{s_sv!E*f1LQ+V0{1B6#IWoVaxbQV*Sk{ zm`h)#xOr_0jUL{PkvpF7CR)Cvk=;VFxur}#{w%Fp5P`fwFP)3JOEX=z0pGiruAvKNC}`L0iQXnK!6E_D6HmkYP#8QvABZjgf&CV#Dq z`@_)Y>uRtz^+!H^qOgzJjS1>1^t1C6Kf7@|Dm5G7k51zxyX-dBaS=KH>MGwZJx`ka-Q!Z3=FUBpgV_EQp zd|3bMxJce?5@tuu!L&J#@w25W^PCum$Dcfer2CFIkm_;E^PilIxjEAp0P`|Rdj#&b zgV-ux0zT@Lg7QvVDzjPy#kteC;;}loc+F6D=ZP`8m<93)HWNs;%ZOPfEQ51NT5Q3> z$z-BBn4Z+_BKk1~s~l3Xr1KY*)^0(!FezN$ufTFzmQnkU<#iY;A-CtlVP4 zJkCt!B<57`D}HF<$-zQqew!gm_ihBAcO@`vQ4I|;p2GB=$gqyj?zm*GA}gAZiU(1+ zOMevDSeFk`Zf+G`IJ*z5RJ2f>;Eo@q7f|nzjaVdk7SaTUL!L(>-JCv!ZCvGt@88uz zWAkUQ&Qpf@p1Q2GV<eT|^MRT4X={GizxPHd*V;MN@%LZ-pw=|`QuL^Kj|vO3(P6)XIbQ1 zI2uk#owHhym4lPA;>h26JInrH#j0N46Bx-t?(0uBeV3{R^~q(p-1aCA_-Tgceh zoEq-)%Li1mKMfB#IDpr0fkPM{kKSEoxY^7E@0=dTEEjCYo^2+0h)QVgwIvwtdYj_1 z&4j7?KB=m`qML)4f#hFzShQc4-R#w7ny1V$-{&B`T~Wwqbehn**%kOur3`xZ{iA|B z1IX+N;V(wUZ9Et@3i|{7slwgcm`yMgJPE1` za1o9o(VGtVSoRGZtnKlfaz2RO9K>zyi{R}^W4xmJl5A_DnVOfq&ghVT*2vFzVs;W4{w2S5H_3Ccg*LV-`BF?!ou>))61_`s1j=w$g6 zNK1)@(h1v`MyNFS&0c|9s**6my92KJ&Vh(mKVVm>Aq$#XMe9^Tz|3qPzf$QJEFFJZ5QpzEFcrjqjcCVn5FGj7az|k0R14rp?T;EY?7Y=qMzqr%oYXkIWq&l?%^S` zVH@miso~~#Rgi{X8rSz}F0o*1OtyAKb*E1-b@g4^yG{<{jz!QuA}HX9#XuwIexa;3&~+s zbf?RYg?`Kf^@naO^72kP{dFNLZ0q87-!a4*A+ZCVP0l>dQ~kDQQc`2v==hT^xI#%#L1E!uu{XA<=nLGryjn>=t0s-iy4 zE1gb0zuX}vdTNtV`Xr2xwMNyTFYqS%i72$S5_T<^2e)nvWwPbEIPlC&(dhmz?q^jX zxGS^@SqfQHaj&7D`@0eZ<^88cHBn1wvqUbhX=cnEN5|8Ob;6l>$vNI)mKQazHRFa{PXqH# z#*b@ggTG^jv16{<*xb`5+R(CwWHJUoUZpk8PgM~dl2`cncp?Az+z+fS-{;OZMzHZ} zHNyAt43cPnN)J2UlFvpV7gRHziOhYdr$mjWH;!NuIp3iZjM#0VXShXppGYn>7r&?y zW|oz~c&PUTy?Zi`8tFVArJ%vbYsR2HNRfuXhM4_n3M#G=+#4P3kX)*ZH1Ql2Tt5wL z{A8vsba14D{?G;4EE;+43`zcRM87C?C@lI810t@#@Jl6Jiq;4WZj-{F)%(bL`8(QT zBq=V-H-_>3D(EC{0gGS8!TYb;xbJKpzKQJRB7Y466QeRP=+WgJOs&}=>u8*s9Lvle zMWIw(H23b{M%>*chTHe=ab_38c@wj2_%Hed)g7yaUfumP{`oqlX?7n52NvRlu`jvu z^Iv$)J%-HW&LC!0G?d-mSxve=<6(_=9Lef#Vw1J{C|@oGmI*Azz$L%o)$LHYqwOEgm?Vb_eOAOz2_gSUUnu z-RBGZUsHDBtdK#m*^AFg*F*J?sZ@99J^yB5s`#t+B-|zNkrFbEaq`c6lDZcGuRcDf zzc<=xk&!mKRqSV5-A3Y=iwSI#)^1uCJ`%p)Fr-@o57g?uHMxu)#x~|07LRO5~vPE;eSWA=^*o3t}pVTpamS_^iJ3pq4{tH;QohfNfRc4kSreMzYXz|R47I=1V zJ$-DfXGLYYT>EBy_F&pu81F0if!<9OvcooPk9r*KSUwQHmEU6Z-!lw#Z#KfBgXW;wa~zH(8wvY}K(wj+2}|z=GWRKx!hX#b5<&x* z?+`CmxW59I=J`-nUnIRbHWS0Nm%^8bAUL3K3uaAABhB%LVZh%ce79E-hk^;_0t!5- z*ruS=B)GVGC|kH_3;DLEvX9FeKy|5?(D@(F^-dUt-!Dqx=1WiL_aF_HAyLa795kbv zw`WA-uFJ3$&x%2}NZ2R&ZG+G=0r-&_u!eK5C~Nc}z$g`#bI6>`V~jCxneaZG+zdzV zI^*?}Kt|XF-LpHXzZh^rtrKi0N(a|~NSylPKeEnN=8tb$hmB?0j1%siiISc;?(!0T z<3$bHIw68$5`OU<3B*iVdX=;a2cKd~#Do z$V&UL(xG)ix8MrRGmv3-&(6VxH@QNQ3?|lH*m;2+3`VZo73vD4~hBfO-^=5@WkAVBU z3QQN}F^T;lEL-@zkL;1h?I+W?5k>Pz@k_oqM!yYSsSjm6@AWVt*aPnRzNE@2jdaJk zkep`iv3}Ct1$hmTG_xZR4`w^?jRi_{Wz;p=c(H^J)`(*V-X#LfDS|Q2HMrR}HRQ{+ zi`woE77OH6{>;QFX!!6M+?VMf#q;A>!#V|a^_d-JIP4_1p3`JekwQm@wR7>h=V|z- z*EC>+Ejz}`TWA8Ek`Wkx@~1yIcbVV(=);KS+) zt4|8QVe9zOlyUSVjJ~m#h3v9mP4XwG_NgU}jy(-g^LE3tt7Xvb`k3<*-ugGXFNj_H zGpMa^9D6Pmz(>tE0L3BA+?2~lc`v5{?D*C$2sPKq8^hPFpy8V z90Fq@8B~%5FZAIHFg@!UKj?1}@6a=kO1uQFbF#pZeq_hKjvU8+e(ZqP1B2;~;OZUu zcsvR+_S8Qy@dAt``i!`O@fF&yq;T#v;WO3Fl(J6%Cz zO#g6tY3baMuj^rDmLV9~qzm2U4b=MUxOl`gdt4_o2`~Q|$zC4ZO?S_vb6(+nWXJT_ zoCyvzul?G|vrZ2vyjldy0!m?H@CSMyZwod<-9f1&l|L@zooCwx<2|Q!IQ4@(%l}dc z2_j>Xymy<vAPHyD|33M!pB5}YC-ZiFH z{O5x`UQD?I2c2GlLCZnzm%xS(jT+6&KOZE;%Lk!-z&bR%?F|dc`ng9d-B?jWF-g=v zA;nu#SS@*llGfQUZ9NMNI+6iDK4)?+5^bE~Wp}os!4LW-&f)y7E29^7le$SRH~*?0 zs(!Y_H7DY%qi?1&rLJu(b(k7nw;#kJSDu9>G6^&%xdLRngm=D2tH`2kE>zy}WaC5R znRKTyxAw;b7%XW^j+6G&AX_aMB%_2Wzg$o+$biPY_kggu4fIsLnp!y3|CP^w0vPc}YN~%|R;8 zHUrJ^)A+%u0{hl`Gq&~ap%SxrxNF?SOK#1BiMyDf3gO9l|7JEsq)9jaJ)`F5lB5*2 zlbUVa*`Lh0tmtAAn`bnJxT?{h=q3d|>S{2!>jEG0FpBaQJ_Wh%*CH9^H=H@L$yiWKviTfDr#@b)lG3(^%s5WOUj#X+S$#PpZ z<&O=^&2WL`&%#9-?{}~<*?#P;cQ6}q{|<@QXfn@pQ|O(FCUy!{`HRB$+i&6LKMu)( z*7kR_gE{dZj_(BjBWJm>cA2o(Sa3%@$mh&%KA~fKX2F2{*${Or8ApCJMytO@_~Y(A z+T`+{On%7WmHILCdteU@%rAqlQ=55np8`AtF^f7q;q!H*uRY>4(`u zx;dmC)Lz@Ly`}nWN$E6ByI+P`735gWaznOd^?D{F%ixu(4W`!J22;c36#nEmd6Wt7 zWb32i<(-M(dhjX#s$CB!78s!IYIiQTbObHf83-Mlmt)E}5$dmb!DSuFg{r7*R`p*7 z73wOmf6fb-*ZlR&Q*Jl^Cp?ag(VY#m3O2zABQfPySJAr#3PMLk3HJ9~6oqce5MR=D zB0Z+YELQA>J`bVuUXe_F{tEDH%yOFBQp<0%8HG_1XUIfl3j6JQg16VsgCbW?dU56? zeV4r?J~AMK1qnT_r^O1;Qsu*XA@6 zdT#ohKB}CdoGm^Sv*;+yv>DBIZePoWy>~*JvJ`k8+sbSU1-87xBAlIs9^SOjS{AbPe9GK`&{*XBb4c#Nbx_WVVO}J zyCm1m_as=Mmf>Lh?(~*(aPWuFn4_>(_}q1>_K2*k20^%?A}bL1 zTYLjdcPZgZr5oJH1+$p3dJHdrp-S|se-f(N%_N~M8RGF*pl4YWhk(QCNj(!~_j5zm$ODKM!^1>D!w_aMPpRp?Gd;O*RNoW%YO ztapO~%{DYb=SQ&?jihlY-y`V|F5ADb3qBf<674OONWc*gYo= zEIqc8yh)g`lm71r_Qr49KM0z#8*prtx>B;Gz>|&TFSA?dd%_I{VHBl+iPgT6P92bXWs?5IsGy&k5>%3s7=aeYph zwAz9fO&!g3j+o3HwzFkJ93wd8i2V>E*}z8*4;2{5+PLB70+$^8Z~)mUFb(?Ll?$Fs~f4cUdu`P=SkT)lZKPo`vGPiP#b$!Z39QW;CoulsG#D z^i%C%Tg4wT7MuuXKh&xFh8NtM{{mJzq>&)$f;)m2R5toFF^2;1%2q&MGQ;=Mw#;Bt z0olw67ue+kgudc7T4kLqyo<-7&yYd9i&%!1ZCl1F3v?iQ^;&va9>jXRwJ~u;5i~4+ z3f=FAkm1}G=vwPW6OS3OQ!5Y9*vJlO^&AdMElOyr$90OEc$Y3(YOvhad^m6QLmVTx z!JpmQLSAj7m{`cZ8Ew;sTCjd%y+Ke})_cg%4pW*%rIv|4mIgY1=X zTU8Ui@&j1m*}w2E#gXZ2jAJYErs8WgP= z+)0nNFUPr@8aghvWiFC~Sf=M5d>uZ7StJd`Vf%%B@0P8w%C(pOqTCFhvPZI$V`Wk5 z%6=%=e3C{u{DCiOLLV$t4wnAp*u3s7tm#p<(&3}YDjmJV`uM4vaGeaob}UnBt6}mDK5+qJ6ycL z|4o_C^eXq@ftW$m+}6zvT{W4nDSrr72SezRwk37zmQm)y4|MXuMGA0>#2G@DA~ZyW zjhb_nXvaFv;>ik3oe;yg>tSrgMlH+@R$|MmhB4h{4P54}1C2Y2N$*PzM4wM5t$n)W z5xx~Jm?$%ggqa}K+zTm&Lm}1fC|rD##Oj`E@=rfZhSJPbZsCHLqVXqfK*K4J55S%Ue?`(u&QnEtiFi?B zx%KjLUl!TkNXzz)72ooggIAXquy~wqw_T=rP{bC|fZ#S>< zClK>~zU98}n9pq8bn)Rudm(Qr^gIl*#irxt3cNgZEGK*0$1?R^9$Nc{&B zK?c{Ds$#lan$9Z<5K9VkcmmX z?i91+7R}hUUC7zyU|&%QBn$}tg4~`@r-gPBOfr41i1f8+%GXa2?mL)`c_4?GANIiN-b$-(!D(mRFU=AH zcEXInrNS;zhw9u$;l;+gjLn)zS3#DiNMEYcR7Qa^svjQh6S7ON`Q3*&|TXDdxNT1rnI>O*?zM+yol z#le>2@XM@P;B5|baS@9^Q(XgXeD4VN2Lnu!iNpi*J;kpIvtXL(C>A+NiJe|MlO6gN z$V%!Th(aYSN&7=M^v8}S@*GN=r1#S`Peb;^`whfZ_i@Gw@)V+_hTR|J_(spOTt##i zD4W`|`$E4Umpczqo?euo-O68$OB9`m9Ku$O^ANjq<%!-8(}z=Ql5m=o8h#gM%%2Gf zZ1`?3+^|)On1v-fGENoOGAlh^$^H(yyaCDGs!y*_!1GE{mH5JNVEdO*XmtFlmmO$JojiC~f-( zxzB#Wng!m}KWsMz-B=HbUDN19y*+w1YSIvdeqZ;j* zL3;!%l3ofHzKZOu#b|su)|;i?-T`# z=HoMk9&K7T1<@ zYb>1kekmQgAEw3+bHBoEC>z$a!EY%0TcJc#YIGH=w>Ps>7- z+5eqj|3(rwoE5WYI$eSbO&&`&#o@oYU+`_t7*^6&g^L%>pgrkx@v`7me(yS*oy;GF zL1F*F;0QCG`=% z?y-n`e<@Z<$J+NlIXu5>Fn-yo0TJg*u|`1x z)MWPJ&a1DecV8ELUNN3KbySzFbovEty;1l~H<)Bw>hXA178GxYpoI(e!e65{7^z!G z_6E6JMM5MqeS9B6%a^lNyY$$q&q~<;b}%N~4dQ$|^w|7oPv}6&3Vf5H&y}R-LCN-F z%=&y6g1_qEjhfN$b!i&Q)=4GB=l$T~UdQwHJV`oTq?~hc^r%G#GhY6Jr=jcEuI`O& z^8HU_T{)KNjLL=5trDoFVoghz-sa}b%xelM?gEJ<13a}$43qy`hey79;g2RsHhAI* zD!-wP-D8i!#(crc)2Yr{hnQo`jR5i5lZVA0kJUj}tuH@nYddEfxd&tX?I9*gaI0*$ zU?T@?f_0x`so?BBUV6lNFuBzRZ}rOs2H!gxzAF>euPb5uJ!FGgcJjW5I*PF}vxCaOM%BE~Q>$wo!HYRh<#k1J0 zh)UYr`W8Ioc2i%=JvuSPn04K&Z7N&0M&OfA;$)**A!7enoG@IM?XXw@7aOhJ*r_1Mned6291fy~2FXjP5}bC_@#)HYSYX7@Q5DAqt%3o}~8 z^3e6kj$?6p34`U}?REu{f(`~;T! zXo_4U=9!NKI3(4>AJaaV|KbH~xDt$~O@vFl{6lhGx|9v<^roGG*s4_^`vSi=Z&*BQ+cegr6D27sjT8_{A`k z6K=JaNPC2way%0#{>Vdes;Ez`zDM-JiR(OWS!sf2PRr_&qF?YwLLOKS1@3`s-xQJ#Y|Chc0t z@@8Fww<`K{uO*GbFF4_8>1~{s{8Unx{>cZ@5$kh$-Ye#R367H&FLnU`J{LFK8G~!x zZ&AbQkFfcnkl%Q$!F(GvaLD8=2n&zpxRy$8ozMh*QvIAh`Bzh>*LwPF6pjf&-^t`* zEY5jFw8>@_O1+y12D5$O#r+kOvZa9^{ih2w|4v8Q&GLA{@D+(7i}CBhC8*`Ii~2)i z@asY=IB{(VKOwN0q@*LUr6mTkRr|qiWhBnE)@3%1rQj0Da}g_}VG8dCU!J^!xl>kC zUf2!zTr^$qbS_7aI%5_Req2;nT|wWU>eIT0645lFqZ)Nof^M#jX0oq+*n-D-T#jKi zO%dK{BQ$cc_WfRn6J}8Pv-@GK!b%t+p~9r+M+!Z48&A|1}<=j1kD?RL0R4tRS#{aRZ)U#wLzchhhB$Y zv-fe)L+$Xi;MaJcszK-e-X*z9eYEPq7y59~5}sTV*t>Tw!nC`)P)6uq+HX0+y3>x) zRXt05Yw?mEN2v*!VpA40=QeZ{{RGqBj_}<6G>!6UhL%NoctOsP<+{4F>+53Z=c!1=wU49cCgXDP0+ScfAh>hN#sv5;VzbK}tO zz;l}4`IYol5}-lNk(?a8@!a4J8l$)uRJGUOU3C@qc<){ej$O(M6Mjmb@qrK+l- zZ1JaR?u3dhDyxgwuu3)15b{o2LUbATC zzu+N#^0~~F&UFT}rRgLJ`a&wEFX(%27FZlt0*|^{Ix+q{HH;1CZmzvU^RN7}8q>?u ztX12ft@0@x9PvxM>$~7fd@94bJXWy4#(tW+aV`C~ayr)Lj>RsS3H00lhWPyPujH@( zfUZvY&2<*eXW#r{&}g-=k9j(VWi?B)^#bE5?s+q5eQppfPAylY~GgSvOX6QiGejO85~J4D2-SR&lCW?I2Yj|W`sr9}AFn@`2+ z+Td9$jY4UiW%_fhaiAynpl?6N?zqERoB25Mk{WL~=WxTGsj{eDzn+ayui^A3-aGmD zsI`!T_riO*i|F*ZY-r2Z=O0bhVS`fUh~K}KLUYGpc0jftip*})Q0;Jf%KX`*2;p9w@}{ET&2UWb44f8txe1b;kUe`S{yRF8T(SgrLT)k?ZPj3r!`8Fe zcTR}zFObIrTQ<-&LrIi1QOEMics_4q0O%cDK&d&#R4P9lSH}t+{X#pwX7w2S^L{6O z+abbRWy;Lm$epcbaVYvvVDWFeMPm*R!r;xF@b5>H_)p<6zDJ<}4@_?$=X?8k$A_=z z>Y$~VW!f-*M+@dWeYqmDW{w^3v%zhMH@PE$EjNXC^?)phLLB58(`-Y^l;uwgGUE5SH(EsqodLLqmY%FTTjsf zOSsf^1(UP9jJM0Zxne6n8W$%=t`Nv}{al4=bwck7e?qRnEj}H+LSWX8z$0&8^Wr2Q z>gv-%r!8Ahq2?&9=@+^XV?xnb=O+67wno#~qhQzcjlwK9vyj*AU~+jOMoOq~mSdK1 z=b|G~K4TpVYD~i$2|?iZGy&fwZl?v&)%=rhpQ-J*;14Lh1DEToxRIB~gTjb8?38N= zNIh@o)4T4Gk+9qU{0Q*(xUJ~SRZy5+A?P`uXY$cAS!q`tS1@Y@sz$8im7MB9soaD= zuX+TM?^OZ|Ux{}vm(m66$6yG(Ft>Ijr#^WPt`7*n5t>2yRULt8a3qI~LU{1xkO*>8=hS=sioXnY2d^_ETj;s{pM*AaNS;%pmY&#HVj+JIB z`x3=E0V=%DUN!Mib76-x;3v%XyUB(r1W}noJKR6_f=}N5k@^aT;`gcUSTWsS=(Ki` zllLe7wn!H)bf}^0?F7in-++3H_F!b7BRd`%i??5nWp5vy1tSAF*7%->^_L~k`IH+z zwwlc;Y~D@fZ3=>$P#a`(FT>2s2T7D}ChYPQA#%SP_Wf}x_3Pf?Y>2DPgg6#pGDKyGt)Kr?({sAbW?;o zlUE~$)>O9E6i+9PLcdFg==Y7|e3Q`WseiH!zg1?ELiv8&|E-XuJ99|xpDIRG3w~L7 zE9T{CgS&US!dzQ(u2apPX6%0~9w@zvMvhs@X0K|&bMo&fKwuAs9xcOpHp?j0ZX>$x zYlLd0DHwh<2%;TtKy-)&>@vKB-6p+!z{nlUcmEVVKQEDcBKHCQcB!+Qdk3Ri%YGay z{2t#~n@4kB+OzBT%4qbBY>N6}jE`n{z$8;`v^+VP=}mslT{hLlo>Pu^C)Wr1c4v~4 z=~nK7${b95WyXwF_|XfMFLWwq0hezY#OiuxF>Cn(t^<|eME*D0DR_>CUNd1s)=y%k zv9s~|0ZCZAN}c&1dI!tmq`+y}P&%WdF3LA{L(gT~#pRDK!YI!~Zfm*;R`R+yC~*t# z@}h`~ow!qMIU<31c}>I^^%T)uVfJ9dE`Vxz2IUPhqr?%TPc)gj;DWmcLSFqmNt$??W8YWyGCZ zPS8u&037pL2TVRBg7Lc+_!D&i4{e;yW@%4g%xD3-)VL6}KH0G4!pwhuXc0eK={ntc zI*K*ctzx+ntJr^DE6`u=7@P}K0<)}CcGEQ$b|1(BR=Zxf!<_~_?F?EIQbUWTXF=nt z2k@(}8tT8Uz{N+lqi_CMUQhizOo>fp(o5H4`SNzKuJoW^FGjLE*_@4S0p4hZ=>v*vBYzEa+WAWs_iD>Qo9D?s>!r_s^-DR#79$011 zL>@08ZGsH;ySiY?nqvO`)heNxvjx4a)}XQD5B_Pt8GG4WNz#>VBGpbMoOiPXGMb08 zZ;obcX3t2rwCyI`Nl2k3PTm67=LRO5zn}+UR@hK9l}w~>LvO$tto}9?Z#@LF!i(}yT^A>pMvK2G?ROp2392RSuOQ$|cv7;d#{L`EwT6Ojm8N23%-X{W<6Te!aTRK=9)UtlH}xZ4&sD`^(c%S6?0dm#kraubbhuVGPRdT??#R z1Ke*385P+q+@GLGbFXW&jx>1K1b+PrUxe4Vd<}hO{P*BDKyv zG+|CLyKx{3vo?8%*H=$vHg(FvS%03t@;w@-99|3-9f44GvjEM17vQM)ELyk!5?Qo3 zvl_pCetBUuoSbJ!m(&+fh_pHGwD5%u2DK2YTSfYldMLGJHeFEK0ZK2!(DwS3hBbzt zq0nR^>NZY9O+UFtFI;L!vL6AZ+kXA-8UY zhSGC{E`1JP@peD!?_JLPKjs1^)WhFn11aW#CpmlFfw3bZ*_Gqt(f5D}n(X>QGd%C3 z>&L(J@LU!;pCzi@YQ&dV?zcIAA(B>}l%W2hJrGr>h}d(H+>L*dV?rD)iM~l)Im)E^ z?;o|A9VIJ61)T1FnqQj{4YRmV)@NaX?z>Fs&d#Oy;r=#!yd;`cSSc}eOC_)mS_P3# z3aBzjVCr1^4?^#)7ko*JAoD~Vu5s$-H5!F3klE_GsVazp9NlU!P95`^9# zW$@CtnegPR2L3ebBZrkwdDpu!sInxH-nnIQttD-Ifbo4;d@TzG4z_1;v+huc&_A!w zw}oQ00lcr@bhhr#7TWzPkzHME%9dYVjwNeCq0c~;Zua$4nYK3C$iL@(;*v4q#y}Q$ z=Nar6=ZujX17Q61CNdkLDlpl7z~w(xXi@%;o=tp73vy@h`Qz5I(pACuB=0d;9BSh_ zXD?@_ng^)OeGwk|YY%DT&cTg9N#U%v!{bM-$@c0#+*-PoOz;6+v#sO&s|+ylmNg!$ zoX=WU*ueB9`!QwSS~j?SA#?V;MRU1n?0uI8dnI|1h78sw>p*GLyddlfPqm4go=fB3 z+3HlMP{jv*eI$N$yp}7IkYhK@O>pz=8v1fBli&I`67*b#;SwEXDqd{F&P-^g<95LK zy7g?|8#k=}wvR@6r(;X&AhzqZDO+duoGa$Wv0qjv(O8mEk$fz_wQd{5_s)Qexv8R! zo3qIDm@Z^i_=iPDRRwMfJ+MD*i%ER1& zvTRoAZHhYGD!RUU3mf1l3*otgxzD-k{JHlRNO4>RH+y_1<;%ozHjC0}z3mU`S=mj= zTl__Lh5hi=>%N#f)EGO2F0p*J1FlOiBKCY0%LpBeyQ%`{TE8W04%mo2GjGBx(Hxe% zK?<$>-og==v8-T|4vH+A=!ex1`2P+UPL9F*8;7tX`Smnr*B$PYpC^>P@MI-_^}*Dk ziSB>Mq_ZklXoL59Qg(}n(zaMIn=OOt+sEU@?QZzyObiS!8I9m#PW99BMQ`s!!niI~ zm|w1CJ91_V_xizhEE?k|nk{{XBr1H^IvXh*ofb<6+SSH1l`~KYQsrh;Uwv`{nDw@rMdTg{$)er3O;d z%lkC?UVmfjDHEDP>LxqIkkBjre?jKue!uSB1qd`?d0CCz`&!I5wMv_b63OCHw&H_LE5 zwCN7?g=IImIOW63^eGtF5rYd_wtz%%0dxrOOu@z{sJ%{(v+lhw^s5A}tj}TavD`w# zE`Nh{E{^c{T`(7fJIKGY2#s|P(Ee6=%okn>`?OYLcl&hq@6I=y^6FksUx)_$c90)0P<@>d>YT37lSiuKfSnXu7>8B=is0X+9Z)$|lPyt-qr)#I!FYZH8@lKlXa)9z>7oMqHKZImbnKu~ zU+BTqEM)pkZtUs|XWqMD1nvD2jV4E8#IHno*lA%625Hi4wciz5B|-f6Z3E!Jw`At7 z{Dbb-EumuDDzaHQ3b{+~!J}WBnM}M)@gwUfVpt&RMh)io+#e|T@G7Yz(}?{`vT8^v zNDy-B%do>@8C!ibktHlp#^c}n;O3t=wp;v?3|#$j@%k=msGdWP&R1Yfz>S_qb}rAyi4Sl#*w>}$$F=vVf_{9}XI&>xc2+VmVKV?K=CFjQ3k+8vJuxiCGK zGb9;VLoM&}!N$9lAGPooEnJ!djd~3*q^S$?X9r=V-cqD}=}dGbo(>iph&G&$Z0J4X zK?NPd*<{m9^oaP69tVfw;TS=iE@Zm5{>tGOT|W!vPyVA1i4*X;#AW*1e3(l+)5aZ} zd5G%5%xPsq5oNZ26J4L4gnxcWQ=?KoCrYTN@VZ96r%4qrJzdJ)yVN!;-}_B`w6>LU zE>=U>c{fP@kPauqreRCkMi_SZE38huNx{R?_{BnpsrK(fPPum;7*@pLwrx z=(Lm58a2pSn;^_AH^Y#7O*C9~E_=0D1*YRz{Gg--&v+xe*%XEPU9Au+a~)cyYeVxR zL#px6qh^bB7}6sL53GKZ!mB-E?}}LLm6l}@$~RzNwgs(->xY<-0Wi%<2Gf

fn* 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

MNIST demo

+
Test accuracy:
+ diff --git a/demos/mnist/manifest.json b/demos/mnist/manifest.json new file mode 100644 index 0000000000..3a39b577ac --- /dev/null +++ b/demos/mnist/manifest.json @@ -0,0 +1,41 @@ +{ + "hidden1/biases": { + "filename": "hidden1_biases", + "shape": [ + 128 + ] + }, + "hidden1/weights": { + "filename": "hidden1_weights", + "shape": [ + 784, + 128 + ] + }, + "hidden2/biases": { + "filename": "hidden2_biases", + "shape": [ + 32 + ] + }, + "hidden2/weights": { + "filename": "hidden2_weights", + "shape": [ + 128, + 32 + ] + }, + "softmax_linear/biases": { + "filename": "softmax_linear_biases", + "shape": [ + 10 + ] + }, + "softmax_linear/weights": { + "filename": "softmax_linear_weights", + "shape": [ + 32, + 10 + ] + } +} diff --git a/demos/mnist/mnist.md b/demos/mnist/mnist.md new file mode 100644 index 0000000000..02d9b71724 --- /dev/null +++ b/demos/mnist/mnist.md @@ -0,0 +1,125 @@ +--- +layout: page +order: 4 +--- + +# Port TensorFlow models +This tutorial demonstrates training and porting a TensorFlow model to **deeplearn.js**. +The code and all the necessary resources used in this tutorial are stored in +`demos/mnist`. + +We will use a fully connected neural network that predicts hand-written digits +from the MNIST dataset. The code is forked from the official +[TensorFlow MNIST tutorial](https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/examples/tutorials/mnist/fully_connected_feed.py). + +> NOTE: We will refer to the base directory of the **deeplearn.js** repo as `$BASE`. + +First, we clone the **deeplearn.js** repository and make sure we have TensorFlow +installed. We cd into `$BASE` and train the model by running: + +```bash +python demos/mnist/fully_connected_feed.py +``` + +The training should take ~1 minute and will store a model checkpoint in +`/tmp/tensorflow/mnist/tensorflow/mnist/logs/fully_connected_feed/`. + +Next, we need to port the weights from the TensorFlow checkpoint to **deeplearn.js**. +We provide a script that does this. We run it from the `$BASE` directory: + +```bash +python scripts/dump_checkpoint_vars.py --output_dir=demos/mnist/ --checkpoint_file=/tmp/tensorflow/mnist/logs/fully_connected_feed/model.ckpt-1999 +``` + +The script will save a set of files (one file per variable, and a +`manifest.json`) in the `demos/mnist` directory. The `manifest.json` is a simple +dictionary that maps variable names to files and their shapes: + +```json +{ + ..., + "hidden1/weights": { + "filename": "hidden1_weights", + "shape": [784, 128] + }, + ... +} +``` + +One last thing before we start coding - we need to run a static HTTP server from +the `$BASE` directory. + +```bash +./node_modules/.bin/http-server +>> Starting up http-server, serving ./ +>> Available on: +>> http://127.0.0.1:8080 +>> Hit CTRL-C to stop the server +``` + +Make sure you can access `manifest.json` via HTTP by visiting +`http://localhost:8080/demos/mnist/manifest.json` in the browser. + +We are ready to write some **deeplearn.js** code! + +> NOTE: If you choose to write in TypeScript, +make sure you compile the code to JavaScript and serve it via the static HTTP +server. + + +To read the weights, we need to create a `CheckpointLoader` and point it to the +manifest file. We then call `loader.getAllVariables()` which returns a +dictionary that maps variable names to `NDArray`s. At that point, we are ready +to write our model. Here is a snippet demonstrating the use of +`CheckpointLoader`: + +```ts +import {CheckpointLoader, Graph} from 'deeplearnjs'; +// manifest.json is in the same dir as index.html. +const reader = new CheckpointReader('.'); +reader.getAllVariables().then(vars => { + // Write your model here. + const g = new Graph(); + const input = g.placeholder('input', [784]); + const hidden1W = g.constant(vars['hidden1/weights']); + const hidden1B = g.constant(vars['hidden1/biases']); + const hidden1 = g.relu(g.add(g.matmul(input, hidden1W), hidden1B)); + ... + ... +}); +``` + +For details regarding the full model code see `demos/mnist/mnist.ts`. The demo +provides the exact implementation of the MNIST model using 3 different API: + +- `buildModelGraphAPI()` uses the Graph API which mimics the TensorFlow API, +providing a lazy execution with feeds and fetches. Users do not need to worry +about GPU-related memory leaks, other than their input data. +- `buildModelLayerAPI()` uses the Graph API in conjuction with `Graph.layers`, +which mimics the Keras layers API. +- `buildModelMathAPI()` uses the Math API. This is the lowest level API in +**deeplearn.js** giving the most control to the user. Math commands execute immediately, +like numpy. Math commands are wrapped in math.scope() so that NDArrays created +by intermediate math commands are automatically cleaned up. + +To run the mnist demo, we provide a `watch-demo` script that watches and +recompiles the typescript code when it changes. In addition, the script runs a +simple HTTP server on 8080 that serves the static html/js files. Before you run +`watch-demo`, make sure you kill the HTTP server we started earlier in the +tutorial in order to free up the 8080 port. Then run `watch-demo` from `$BASE` +pointed to the entry-point of the web app demo, `demos/mnist/mnist.ts`: + +```bash +/scripts/watch-demo demos/mnist/mnist.ts +>> Starting up http-server, serving ./ +>> Available on: +>> http://127.0.0.1:8080 +>> http://192.168.1.5:8080 +>> Hit CTRL-C to stop the server +>> 1410084 bytes written to demos/mnist/bundle.js (0.91 seconds) at 5:17:45 PM +``` + +Visit `http://localhost:8080/demos/mnist/` and you should see a simple page +showing test accuracy of ~90% measured using a test set of 50 mnist images +stored in `demos/mnist/sample_data.json`. Feel free to play with the demo +(e.g. make it more interactive) and send us a pull request! diff --git a/demos/mnist/mnist.ts b/demos/mnist/mnist.ts new file mode 100644 index 0000000000..d8cb7dde92 --- /dev/null +++ b/demos/mnist/mnist.ts @@ -0,0 +1,139 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Array1D, Array2D, CheckpointLoader, Graph, NDArray, NDArrayInitializer, NDArrayMath, NDArrayMathGPU, Scalar, Session, Tensor} from '../deeplearnjs'; + +// manifest.json lives in the same directory as the mnist demo. +const reader = new CheckpointLoader('.'); +reader.getAllVariables().then(vars => { + // Get sample data. + const xhr = new XMLHttpRequest(); + xhr.open('GET', 'sample_data.json'); + xhr.onload = () => { + const data = JSON.parse(xhr.responseText) as SampleData; + const math = new NDArrayMathGPU(); + const evalMethod = buildModelMathAPI(math, data, vars); + const [input, probs] = buildModelLayersAPI(data, vars); + const [input2, probs2] = buildModelGraphAPI(data, vars); + const sess = new Session(input.node.graph, math); + + math.scope(() => { + let numCorrect = 0; + for (let i = 0; i < data.images.length; i++) { + const inputData = Array1D.new(data.images[i]); + const probsVal = sess.eval(probs, [{tensor: input, data: inputData}]); + const probsVal2 = + sess.eval(probs2, [{tensor: input2, data: inputData}]); + const probsVal3 = evalMethod(inputData); + if (data.labels[i] === probsVal.get()) { + numCorrect++; + } + } + const accuracy = numCorrect * 100 / data.images.length; + document.getElementById('accuracy')!.innerHTML = accuracy + '%'; + }); + }; + xhr.onerror = (err) => console.error(err); + xhr.send(); +}); + +interface SampleData { + images: number[][]; + labels: number[]; +} + +/** + * Builds a 3-layer fully connected MNIST model using the Math API. This is the + * lowest level user-facing API in Learn.js giving the most control to the user. + * Math commands execute immediately, like numpy. Math commands are wrapped in + * math.scope() so that NDArrays created by intermediate math commands are + * automatically cleaned up. + */ +function buildModelMathAPI( + math: NDArrayMath, data: SampleData, + vars: {[varName: string]: NDArray}): (x: Array1D) => Scalar { + const hidden1W = vars['hidden1/weights'] as Array2D; + const hidden1B = vars['hidden1/biases'] as Array1D; + const hidden2W = vars['hidden2/weights'] as Array2D; + const hidden2B = vars['hidden2/biases'] as Array1D; + const softmaxW = vars['softmax_linear/weights'] as Array2D; + const softmaxB = vars['softmax_linear/biases'] as Array1D; + + return (x: Array1D): Scalar => { + return math.scope(() => { + const hidden1 = + math.relu(math.add(math.vectorTimesMatrix(x, hidden1W), hidden1B)); + const hidden2 = math.relu( + math.add(math.vectorTimesMatrix(hidden1, hidden2W), hidden2B)); + const logits = + math.add(math.vectorTimesMatrix(hidden2, softmaxW), softmaxB); + return math.argMax(logits); + }); + }; +} + +/** + * Builds a 3-layers fully connected MNIST model using the Graph API. This API + * mimics the TensorFlow API, providing a lazy execution with feeds and fetches. + * Users do not need to worry about GPU-related memory leaks, other than their + * input data. + */ +function buildModelGraphAPI( + data: SampleData, vars: {[varName: string]: NDArray}): Tensor[] { + const g = new Graph(); + // TODO: Support batching. + const input = g.placeholder('input', [784]); + const hidden1W = g.constant(vars['hidden1/weights']); + const hidden1B = g.constant(vars['hidden1/biases']); + const hidden1 = g.relu(g.add(g.matmul(input, hidden1W), hidden1B)); + + const hidden2W = g.constant(vars['hidden2/weights']); + const hidden2B = g.constant(vars['hidden2/biases']); + const hidden2 = g.relu(g.add(g.matmul(hidden1, hidden2W), hidden2B)); + + const softmaxW = g.constant(vars['softmax_linear/weights']); + const softmaxB = g.constant(vars['softmax_linear/biases']); + const logits = g.add(g.matmul(hidden2, softmaxW), softmaxB); + return [input, g.argmax(logits)]; +} + +/** + * Builds a 3-layers fully connected MNIST model using the Graph API in + * conjuction with `Graph.layers`, which mimics the Keras layers API. + */ +function buildModelLayersAPI( + data: SampleData, vars: {[varName: string]: NDArray}): Tensor[] { + const g = new Graph(); + // TODO: Support batching. + const input = g.placeholder('input', [784]); + const hidden1W = vars['hidden1/weights']; + const hidden1B = vars['hidden1/biases']; + const hidden1 = g.layers.dense( + 'hidden1', input, hidden1W.shape[1], (x) => g.relu(x), true, + new NDArrayInitializer(hidden1W), new NDArrayInitializer(hidden1B)); + + const hidden2W = vars['hidden2/weights']; + const hidden2B = vars['hidden2/biases']; + const hidden2 = g.layers.dense( + 'hidden2', hidden1, hidden2W.shape[1], (x) => g.relu(x), true, + new NDArrayInitializer(hidden2W), new NDArrayInitializer(hidden2B)); + + const softmaxW = vars['softmax_linear/weights']; + const softmaxB = vars['softmax_linear/biases']; + const logits = g.layers.dense( + 'softmax', hidden2, softmaxW.shape[1], null, true, + new NDArrayInitializer(softmaxW), new NDArrayInitializer(softmaxB)); + return [input, g.argmax(logits)]; +} diff --git a/demos/mnist/sample_data.json b/demos/mnist/sample_data.json new file mode 100644 index 0000000000..af250795b1 --- /dev/null +++ b/demos/mnist/sample_data.json @@ -0,0 +1 @@ +{"images": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.412, 0.533, 0.702, 0.792, 0.706, 0.447, 0.071, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.188, 0.953, 0.996, 0.996, 0.996, 0.996, 0.855, 0.996, 0.863, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.302, 0.941, 0.898, 0.384, 0.353, 0.42, 0.455, 0.145, 0.816, 0.992, 0.647, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.706, 0.996, 0.847, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.639, 0.996, 0.761, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.91, 0.996, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.949, 0.996, 0.588, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.996, 0.247, 0.0, 0.0, 0.0, 0.031, 0.329, 0.929, 0.996, 0.518, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.396, 0.996, 0.984, 0.42, 0.0, 0.031, 0.675, 0.996, 0.996, 0.525, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.302, 0.941, 0.984, 0.765, 0.776, 0.996, 0.851, 0.282, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.286, 0.996, 0.996, 1.0, 0.882, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.996, 0.996, 0.996, 0.969, 0.31, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.133, 0.965, 0.976, 0.451, 0.965, 0.996, 0.878, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.812, 0.996, 0.592, 0.0, 0.369, 0.953, 0.996, 0.671, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69, 0.996, 0.627, 0.016, 0.0, 0.0, 0.631, 0.996, 0.996, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.42, 1.0, 0.675, 0.016, 0.0, 0.0, 0.0, 0.529, 0.996, 0.996, 0.255, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.157, 0.98, 0.996, 0.298, 0.0, 0.0, 0.0, 0.0, 0.443, 0.996, 0.996, 0.325, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.996, 0.882, 0.055, 0.0, 0.0, 0.0, 0.0, 0.529, 0.996, 0.976, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.761, 0.996, 0.827, 0.0, 0.0, 0.0, 0.0, 0.133, 0.863, 0.996, 0.506, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.996, 0.957, 0.337, 0.004, 0.0, 0.02, 0.706, 0.996, 0.749, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.094, 0.835, 0.996, 0.996, 0.671, 0.522, 0.831, 0.996, 0.694, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.325, 0.651, 0.804, 0.91, 0.91, 0.463, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.369, 0.0, 0.184, 0.588, 0.992, 0.992, 1.0, 0.827, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.812, 0.969, 0.525, 0.914, 0.988, 0.988, 0.988, 0.992, 0.988, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.831, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.992, 0.988, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.565, 0.992, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.992, 0.988, 0.604, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.71, 0.988, 0.992, 0.988, 0.988, 0.988, 0.992, 0.62, 0.561, 0.561, 0.992, 0.988, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.788, 0.988, 0.992, 0.988, 0.988, 0.502, 0.137, 0.02, 0.0, 0.0, 0.827, 0.988, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.71, 0.988, 0.988, 0.992, 0.945, 0.439, 0.122, 0.0, 0.0, 0.0, 0.0, 0.506, 0.988, 0.439, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.988, 0.988, 0.988, 0.992, 0.38, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.004, 0.671, 0.992, 0.992, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.247, 1.0, 0.992, 0.282, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.608, 0.988, 0.988, 0.702, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.929, 0.992, 0.906, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.145, 0.988, 0.988, 0.988, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.988, 0.992, 0.62, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.988, 0.988, 0.824, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.867, 0.988, 0.749, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.831, 0.992, 0.808, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.914, 0.992, 0.992, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.451, 0.988, 0.988, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.608, 0.914, 0.988, 0.988, 0.988, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.851, 0.988, 0.988, 0.867, 0.224, 0.145, 0.145, 0.145, 0.306, 0.749, 0.988, 0.992, 0.988, 0.965, 0.518, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.851, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.992, 0.988, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.855, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.992, 0.992, 1.0, 0.624, 0.161, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.686, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.824, 0.467, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.706, 0.988, 0.988, 0.988, 0.988, 0.988, 0.992, 0.945, 0.843, 0.361, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.424, 0.988, 0.988, 0.988, 0.988, 0.749, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.851, 0.996, 0.902, 0.071, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.494, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.557, 0.992, 0.996, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.992, 0.996, 0.941, 0.149, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.992, 0.996, 0.992, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.992, 0.996, 0.992, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.992, 0.996, 0.992, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.847, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.851, 0.996, 1.0, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.847, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.98, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.992, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.992, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.992, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.992, 0.992, 0.996, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.141, 0.937, 0.992, 0.761, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.486, 0.988, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.424, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.149, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.91, 0.988, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.188, 1.0, 0.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.996, 0.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.588, 0.996, 0.471, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.871, 0.996, 0.114, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.329, 0.996, 0.847, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.573, 0.996, 0.38, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.906, 0.988, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.235, 0.996, 0.663, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.624, 0.996, 0.325, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.945, 0.804, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.267, 0.996, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.004, 0.835, 0.929, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.996, 0.51, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.408, 0.949, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.608, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.404, 0.996, 0.871, 0.996, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.875, 0.976, 0.914, 0.78, 0.976, 0.847, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.349, 0.933, 0.918, 0.541, 0.09, 0.0, 0.922, 0.847, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.212, 0.757, 0.973, 0.843, 0.157, 0.0, 0.0, 0.251, 0.976, 0.541, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.486, 0.933, 0.992, 0.329, 0.0, 0.0, 0.0, 0.0, 0.894, 0.992, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.945, 0.973, 0.329, 0.0, 0.0, 0.0, 0.024, 0.667, 1.0, 0.451, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.545, 0.988, 0.882, 0.341, 0.0, 0.0, 0.0, 0.031, 0.651, 0.992, 0.753, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.996, 0.831, 0.247, 0.0, 0.0, 0.0, 0.11, 0.824, 0.992, 0.992, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.235, 0.933, 0.847, 0.118, 0.0, 0.0, 0.0, 0.0, 0.647, 0.992, 0.992, 0.992, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.855, 0.863, 0.102, 0.0, 0.0, 0.027, 0.357, 0.584, 0.973, 0.992, 0.992, 0.992, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.996, 0.412, 0.0, 0.165, 0.467, 0.792, 0.996, 0.714, 0.357, 0.451, 0.996, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.573, 0.992, 0.941, 0.925, 0.961, 0.992, 0.898, 0.522, 0.027, 0.0, 0.22, 0.992, 0.635, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.773, 0.847, 0.902, 0.773, 0.318, 0.082, 0.0, 0.0, 0.0, 0.675, 0.98, 0.294, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.169, 0.961, 0.922, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.341, 0.992, 0.663, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.969, 0.902, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.992, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.18, 0.945, 0.808, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.969, 0.871, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.51, 0.71, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.416, 1.0, 0.882, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.612, 0.992, 0.992, 0.992, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.82, 0.992, 0.992, 0.992, 0.776, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.176, 0.8, 0.996, 0.992, 0.992, 0.776, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.094, 0.925, 0.992, 0.996, 0.992, 0.776, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.384, 0.992, 0.992, 0.984, 0.663, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.812, 0.992, 0.992, 0.949, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.741, 0.992, 0.992, 0.812, 0.133, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.467, 0.976, 0.992, 0.992, 0.518, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.871, 0.992, 0.992, 0.788, 0.043, 0.0, 0.0, 0.0, 0.322, 0.478, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.729, 0.996, 0.996, 0.98, 0.09, 0.047, 0.525, 0.796, 0.996, 1.0, 0.996, 0.996, 0.839, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.992, 0.992, 0.961, 0.412, 0.525, 0.976, 0.996, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.714, 0.992, 0.992, 0.867, 0.941, 0.992, 0.992, 0.996, 0.98, 0.851, 0.878, 0.992, 0.992, 0.784, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.235, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.902, 0.784, 0.298, 0.027, 0.569, 0.992, 0.992, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.616, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.631, 0.118, 0.024, 0.573, 0.992, 0.992, 0.451, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.616, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.875, 0.78, 0.992, 0.992, 0.788, 0.169, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.616, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.929, 0.188, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.616, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.757, 0.498, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.976, 0.518, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.365, 0.518, 0.882, 0.992, 0.992, 0.788, 0.518, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.208, 0.561, 0.561, 0.996, 0.992, 0.992, 0.992, 0.898, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.357, 0.835, 0.988, 0.988, 0.988, 0.992, 0.808, 0.831, 0.988, 0.988, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.353, 0.953, 0.988, 0.922, 0.6, 0.416, 0.173, 0.051, 0.067, 0.957, 0.988, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.741, 0.753, 0.267, 0.035, 0.0, 0.0, 0.0, 0.0, 0.035, 0.953, 0.988, 0.231, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.486, 0.988, 0.847, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.953, 0.988, 0.435, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.482, 0.988, 0.929, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.396, 0.988, 0.988, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.694, 0.988, 0.871, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.992, 0.988, 0.416, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.694, 1.0, 0.733, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.42, 0.988, 0.961, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.145, 0.945, 0.988, 0.506, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.004, 0.62, 0.988, 0.988, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.329, 0.988, 0.988, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.478, 0.988, 0.988, 0.129, 0.0, 0.0, 0.0, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.682, 0.988, 0.502, 0.012, 0.027, 0.349, 0.835, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.906, 0.988, 0.573, 0.176, 0.824, 0.945, 0.463, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.584, 0.988, 0.988, 0.988, 0.992, 0.596, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.82, 0.988, 0.8, 0.318, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.753, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.867, 0.882, 0.196, 0.333, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.451, 0.992, 0.957, 0.988, 0.557, 0.051, 0.122, 0.671, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69, 0.992, 0.988, 0.988, 0.988, 0.851, 0.855, 0.988, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.137, 0.827, 0.992, 0.816, 0.988, 0.988, 0.988, 0.992, 0.988, 0.839, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.992, 0.992, 0.929, 0.388, 0.914, 0.992, 0.992, 0.996, 0.992, 0.925, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.988, 0.988, 0.992, 0.659, 0.737, 0.988, 0.988, 0.992, 0.988, 0.988, 0.722, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.988, 0.988, 0.992, 0.686, 0.918, 0.969, 0.561, 0.925, 0.988, 0.988, 0.988, 0.749, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.769, 0.988, 0.992, 0.988, 0.867, 0.384, 0.0, 0.055, 0.471, 0.867, 0.988, 0.988, 0.518, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.494, 0.988, 0.992, 0.714, 0.082, 0.0, 0.0, 0.0, 0.0, 0.118, 0.576, 0.922, 0.969, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.349, 0.176, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 1.0, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.329, 0.745, 0.314, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.106, 0.992, 0.906, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.988, 0.62, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.961, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.557, 0.988, 0.937, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.992, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.988, 0.992, 0.937, 0.62, 0.137, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.992, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 0.996, 0.992, 0.992, 0.992, 0.82, 0.58, 0.216, 0.188, 0.58, 0.925, 0.969, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.992, 0.988, 0.988, 0.988, 0.988, 0.992, 0.906, 0.969, 0.988, 0.851, 0.655, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.565, 0.969, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.957, 0.827, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.388, 0.969, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.988, 0.725, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.165, 0.682, 0.992, 0.988, 0.988, 0.886, 0.231, 0.071, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.278, 0.608, 0.992, 0.996, 0.996, 0.996, 0.89, 0.396, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.443, 0.878, 0.992, 0.992, 0.973, 0.953, 0.71, 0.588, 0.992, 0.894, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.682, 0.992, 0.992, 0.792, 0.349, 0.145, 0.0, 0.0, 0.059, 0.992, 0.894, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.871, 0.396, 0.063, 0.0, 0.0, 0.0, 0.0, 0.4, 0.992, 0.894, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.718, 0.992, 0.741, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.459, 0.98, 0.992, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.541, 0.992, 0.973, 0.553, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.773, 0.984, 0.957, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.671, 0.992, 0.992, 0.847, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.463, 0.914, 0.992, 0.992, 0.992, 0.718, 0.145, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.192, 0.286, 0.761, 0.992, 0.863, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.478, 0.969, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.878, 0.945, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.89, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.878, 0.992, 0.471, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.761, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.89, 0.992, 0.275, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.992, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.655, 0.992, 0.894, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.255, 0.953, 0.984, 0.635, 0.086, 0.0, 0.0, 0.059, 0.231, 0.71, 0.992, 0.996, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.396, 0.992, 0.992, 0.855, 0.741, 0.741, 0.847, 0.992, 0.992, 0.992, 0.643, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.282, 0.878, 0.992, 0.996, 0.992, 0.992, 0.992, 0.984, 0.557, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.58, 0.902, 0.992, 0.831, 0.51, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.302, 0.992, 0.992, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.373, 0.925, 0.988, 0.988, 0.729, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.788, 0.988, 0.988, 0.988, 0.761, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.773, 0.992, 0.988, 0.957, 0.518, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.42, 0.988, 0.992, 0.918, 0.31, 0.141, 0.294, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.941, 0.988, 0.988, 0.945, 0.322, 0.114, 0.835, 0.988, 0.698, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.769, 0.988, 0.988, 0.988, 0.275, 0.0, 0.643, 0.988, 0.988, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.106, 0.769, 0.988, 0.988, 0.988, 0.78, 0.0, 0.0, 0.643, 0.988, 0.988, 0.839, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.827, 0.988, 0.988, 0.988, 0.851, 0.094, 0.0, 0.0, 0.643, 0.988, 0.988, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.988, 0.988, 0.988, 0.988, 0.812, 0.0, 0.0, 0.0, 0.643, 0.988, 0.988, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.945, 0.992, 0.992, 0.992, 0.992, 0.992, 1.0, 0.855, 0.788, 0.992, 0.992, 0.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.584, 0.753, 0.973, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.988, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.329, 0.451, 0.929, 0.933, 0.929, 0.965, 0.988, 0.988, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.988, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.988, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.988, 0.29, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.831, 0.137, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.796, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.988, 0.761, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.678, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.4, 0.161, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.282, 0.596, 0.996, 0.992, 0.082, 0.0, 0.918, 0.992, 0.918, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.718, 0.992, 0.988, 0.992, 0.988, 0.4, 0.0, 0.118, 0.831, 0.992, 0.514, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.518, 0.996, 0.992, 0.996, 0.835, 0.918, 0.992, 0.482, 0.0, 0.0, 0.161, 1.0, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.914, 0.988, 0.992, 0.988, 0.675, 0.039, 0.439, 0.831, 0.0, 0.0, 0.0, 0.0, 0.835, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.839, 0.992, 0.996, 0.914, 0.718, 0.078, 0.0, 0.0, 0.243, 0.239, 0.0, 0.0, 0.0, 0.0, 0.996, 0.675, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.796, 0.992, 0.988, 0.357, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.675, 0.831, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.518, 0.992, 0.957, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.557, 0.992, 0.988, 0.635, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.992, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.914, 0.996, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.914, 0.996, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.988, 0.835, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.835, 0.988, 0.753, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.992, 0.639, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.282, 0.757, 0.996, 0.914, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.914, 0.988, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.796, 0.992, 0.988, 0.753, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.282, 0.596, 0.996, 0.992, 0.957, 0.635, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.675, 0.988, 0.875, 0.478, 0.082, 0.078, 0.4, 0.557, 0.796, 0.796, 0.992, 0.988, 0.992, 0.831, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.875, 1.0, 0.992, 1.0, 0.992, 0.996, 0.992, 0.996, 0.992, 0.957, 0.635, 0.161, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.439, 0.753, 0.992, 0.988, 0.914, 0.592, 0.592, 0.275, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.357, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.337, 0.745, 0.992, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.337, 0.992, 0.992, 0.992, 0.796, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.929, 0.992, 0.992, 0.992, 0.714, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.357, 0.992, 0.992, 0.992, 0.757, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.898, 0.992, 0.992, 0.965, 0.412, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.992, 0.863, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.882, 0.224, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.196, 0.259, 0.259, 0.259, 0.675, 0.259, 0.259, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.992, 0.573, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.894, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.573, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.529, 0.965, 0.976, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.976, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.573, 0.0, 0.0, 0.0, 0.02, 0.2, 0.871, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.973, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.737, 0.086, 0.0, 0.0, 0.545, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.992, 0.992, 0.71, 0.051, 0.0, 0.545, 0.992, 0.992, 0.961, 0.459, 0.255, 0.255, 0.298, 0.871, 0.992, 0.992, 0.992, 0.714, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.357, 0.992, 0.992, 0.992, 0.729, 0.455, 0.749, 0.992, 0.992, 0.671, 0.0, 0.0, 0.337, 0.694, 0.992, 0.992, 0.89, 0.541, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.761, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.929, 0.804, 0.804, 0.945, 0.992, 0.992, 0.992, 0.247, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.376, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.906, 0.478, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.125, 0.651, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.824, 0.765, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.416, 0.902, 0.992, 0.992, 0.992, 0.992, 0.973, 0.416, 0.106, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.416, 0.286, 0.306, 0.059, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.102, 0.275, 0.667, 0.996, 0.996, 0.996, 0.796, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.643, 0.937, 0.996, 0.992, 0.992, 0.992, 0.996, 0.992, 0.918, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.518, 0.992, 0.992, 0.996, 0.91, 0.902, 0.902, 0.773, 0.984, 0.992, 0.925, 0.169, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.133, 0.667, 0.992, 0.992, 0.992, 0.404, 0.024, 0.0, 0.0, 0.0, 0.506, 0.992, 0.992, 0.624, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.592, 1.0, 0.847, 0.525, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.816, 0.996, 0.498, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.871, 0.847, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.992, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.443, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.992, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.992, 0.231, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.38, 0.996, 0.663, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.922, 0.973, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.733, 0.992, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.592, 0.996, 0.78, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.969, 0.996, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.569, 0.992, 0.608, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.992, 0.992, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.643, 0.969, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.984, 0.902, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.992, 0.933, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.925, 0.212, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.255, 0.973, 1.0, 0.675, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.231, 0.569, 0.929, 0.988, 0.988, 0.992, 0.988, 0.643, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.212, 0.616, 0.882, 0.988, 0.988, 0.988, 0.988, 0.988, 0.992, 0.988, 0.933, 0.204, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.494, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.812, 0.988, 0.988, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.569, 0.282, 0.051, 0.988, 0.988, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.988, 0.988, 0.988, 0.988, 0.757, 0.298, 0.024, 0.0, 0.051, 0.988, 0.988, 0.467, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.608, 0.988, 0.988, 0.925, 0.655, 0.298, 0.035, 0.0, 0.0, 0.0, 0.435, 0.988, 0.988, 0.416, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.365, 0.141, 0.114, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.702, 0.988, 0.988, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.992, 0.988, 0.925, 0.188, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.992, 0.988, 0.847, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.227, 0.745, 1.0, 0.992, 0.827, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.988, 0.992, 0.98, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.91, 0.988, 0.992, 0.906, 0.145, 0.145, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.157, 0.722, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.639, 0.192, 0.192, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.239, 0.376, 0.937, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.882, 0.506, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.11, 0.8, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.525, 0.988, 0.988, 0.988, 0.988, 0.988, 0.988, 0.831, 0.188, 0.188, 0.188, 0.188, 0.188, 0.322, 0.655, 0.655, 0.957, 0.514, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.212, 0.988, 0.988, 0.988, 0.988, 0.988, 0.824, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.145, 0.988, 0.988, 0.988, 0.988, 0.62, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.675, 0.988, 0.851, 0.361, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.094, 0.094, 0.094, 0.094, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.471, 0.729, 0.988, 0.988, 0.992, 0.988, 0.769, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.643, 0.722, 0.992, 0.988, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.176, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.722, 0.988, 0.988, 0.992, 0.988, 0.988, 0.69, 0.627, 0.827, 0.988, 0.988, 0.176, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.722, 0.988, 0.988, 0.537, 0.251, 0.086, 0.016, 0.0, 0.541, 0.988, 0.988, 0.176, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.29, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.584, 0.992, 0.992, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.992, 0.988, 0.816, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.902, 0.992, 0.941, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.133, 0.933, 0.992, 0.808, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.855, 0.988, 0.992, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.541, 0.992, 0.992, 0.659, 0.0, 0.0, 0.0, 0.0, 0.012, 0.051, 0.094, 0.094, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.988, 0.988, 0.906, 0.165, 0.0, 0.0, 0.0, 0.0, 0.667, 0.827, 0.988, 0.988, 0.827, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.722, 0.725, 0.894, 0.988, 0.988, 0.212, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.988, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.957, 0.988, 0.992, 0.988, 0.988, 0.69, 0.016, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.988, 0.988, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.533, 0.965, 0.988, 0.988, 0.992, 0.988, 0.988, 0.957, 0.494, 0.455, 0.455, 0.78, 0.902, 0.98, 0.992, 0.988, 0.988, 0.824, 0.333, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.341, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.992, 0.992, 0.992, 0.988, 0.902, 0.451, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.831, 0.988, 0.988, 0.988, 0.906, 0.812, 0.925, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.839, 0.6, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.988, 0.89, 0.188, 0.0, 0.173, 0.392, 0.718, 0.718, 0.478, 0.271, 0.271, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.627, 0.502, 0.114, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.58, 0.69, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 1.0, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.996, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.8, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.839, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.996, 0.553, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.89, 0.553, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.718, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.463, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.741, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.839, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.769, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.345, 0.835, 0.071, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.604, 0.992, 0.753, 0.184, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.988, 0.988, 0.992, 0.427, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.918, 0.988, 0.992, 0.427, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.38, 0.988, 0.992, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.576, 0.992, 1.0, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.82, 0.988, 0.918, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.988, 0.988, 0.733, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.988, 0.988, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.992, 0.992, 0.745, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.988, 0.988, 0.255, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.729, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.447, 0.996, 0.969, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.761, 0.992, 0.769, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.333, 0.988, 0.992, 0.427, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.333, 0.988, 0.945, 0.184, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.384, 0.992, 0.639, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.773, 0.988, 0.737, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.176, 0.941, 0.988, 0.882, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.6, 0.988, 0.882, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 0.902, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.129, 0.945, 0.996, 0.286, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.557, 0.996, 0.98, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.996, 0.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.988, 0.996, 0.831, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.796, 0.996, 1.0, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.89, 0.996, 0.69, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.612, 0.996, 0.996, 0.549, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.937, 0.996, 0.753, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.996, 0.996, 0.427, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.965, 0.996, 0.996, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.996, 0.996, 0.384, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.914, 0.996, 0.925, 0.133, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.996, 0.988, 0.204, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.8, 0.996, 0.851, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.996, 0.976, 0.357, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.694, 0.996, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.694, 0.996, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.694, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.525, 0.694, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.49, 0.49, 0.49, 0.631, 0.996, 0.996, 0.996, 1.0, 0.996, 0.996, 0.945, 0.451, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.929, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.424, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.949, 0.992, 0.992, 0.992, 0.992, 0.882, 0.557, 0.557, 0.557, 0.851, 0.992, 0.992, 0.992, 0.431, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.267, 0.941, 0.643, 0.584, 0.176, 0.055, 0.0, 0.0, 0.0, 0.051, 0.663, 0.992, 0.992, 0.431, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.725, 0.957, 0.992, 0.922, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.361, 0.361, 0.361, 0.725, 0.89, 0.992, 0.992, 0.992, 0.471, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.129, 0.518, 0.843, 0.89, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.776, 0.345, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.388, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.549, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.647, 0.949, 0.906, 0.937, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.973, 0.471, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.192, 0.157, 0.18, 0.227, 0.227, 0.592, 0.737, 0.91, 0.992, 0.992, 0.976, 0.467, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.169, 0.608, 0.992, 0.992, 0.98, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.718, 0.992, 0.992, 0.824, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.18, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.259, 0.992, 0.992, 0.969, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.988, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.929, 0.992, 0.973, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.38, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.129, 0.639, 0.992, 0.91, 0.173, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.682, 0.992, 0.827, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.482, 0.992, 0.992, 0.988, 0.412, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.451, 0.992, 0.992, 0.827, 0.588, 0.141, 0.078, 0.078, 0.078, 0.078, 0.078, 0.078, 0.435, 0.651, 0.992, 0.992, 0.988, 0.604, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.945, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.988, 0.604, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.212, 0.459, 0.929, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.988, 0.969, 0.604, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.435, 0.482, 0.482, 0.639, 0.729, 0.992, 0.608, 0.482, 0.482, 0.408, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.682, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.18, 0.494, 0.306, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.224, 0.71, 0.996, 0.733, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.639, 0.996, 0.769, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.247, 0.361, 0.769, 0.996, 0.996, 0.996, 0.733, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.506, 0.996, 0.957, 0.478, 0.227, 0.4, 0.604, 0.604, 0.914, 0.992, 0.996, 0.988, 0.773, 0.89, 0.996, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.604, 0.984, 0.996, 0.996, 0.996, 0.996, 0.98, 0.902, 0.902, 0.553, 0.224, 0.0, 0.529, 0.945, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.282, 0.282, 0.282, 0.282, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.659, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.906, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.129, 0.949, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.408, 0.996, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.996, 0.58, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.498, 0.996, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.541, 0.996, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.847, 0.859, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.42, 0.996, 0.675, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.42, 0.996, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 0.996, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.133, 0.953, 0.776, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.996, 0.714, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 0.996, 0.714, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.173, 0.996, 0.569, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.173, 0.392, 0.604, 0.737, 0.878, 0.592, 0.082, 0.0, 0.0, 0.086, 0.29, 0.392, 0.604, 0.737, 0.878, 0.89, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.863, 0.996, 0.851, 0.612, 0.584, 0.584, 0.878, 0.773, 0.541, 0.729, 0.957, 0.898, 0.62, 0.584, 0.365, 0.235, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.702, 0.733, 0.114, 0.012, 0.0, 0.282, 0.714, 0.976, 0.808, 0.62, 0.369, 0.122, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.498, 0.953, 0.388, 0.467, 0.714, 0.957, 0.612, 0.192, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.843, 1.0, 0.996, 0.416, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.776, 0.831, 0.396, 0.89, 0.655, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.588, 0.902, 0.145, 0.0, 0.2, 0.949, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.894, 0.506, 0.0, 0.0, 0.0, 0.651, 0.827, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.894, 0.835, 0.525, 0.584, 0.675, 0.965, 0.392, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.588, 0.875, 0.851, 0.631, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.184, 0.192, 0.588, 0.588, 0.588, 0.757, 0.808, 0.996, 1.0, 0.89, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.369, 0.573, 0.773, 0.788, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.314, 0.992, 0.992, 0.992, 0.992, 0.902, 0.851, 0.851, 0.851, 0.851, 0.71, 0.827, 0.557, 0.882, 0.992, 0.545, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.667, 0.294, 0.263, 0.263, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.992, 0.545, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.992, 0.545, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.773, 0.992, 0.431, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.894, 0.992, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.922, 0.992, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.992, 0.761, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.886, 0.992, 0.325, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.992, 0.961, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.992, 0.678, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.937, 0.992, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.38, 0.992, 0.957, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.753, 0.957, 0.314, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.349, 0.969, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.992, 0.596, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.976, 0.98, 0.114, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.388, 0.992, 0.647, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.922, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.314, 0.812, 1.0, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.992, 0.992, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.243, 0.839, 0.992, 0.992, 0.992, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.553, 0.992, 0.992, 0.992, 0.992, 0.992, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.882, 0.992, 0.992, 0.992, 0.867, 0.58, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.765, 0.992, 0.992, 0.992, 0.788, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.314, 0.992, 0.992, 0.992, 0.835, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.992, 0.204, 0.0, 0.302, 0.416, 0.416, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.639, 0.157, 0.722, 0.918, 0.992, 0.992, 0.918, 0.722, 0.102, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.51, 0.792, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.655, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.51, 0.792, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.62, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.643, 0.627, 0.831, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.988, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.992, 0.204, 0.173, 0.831, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.541, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.929, 0.992, 0.992, 0.992, 0.914, 0.608, 0.278, 0.235, 0.098, 0.098, 0.624, 0.992, 0.992, 0.992, 0.541, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.569, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.827, 0.514, 0.376, 0.584, 0.992, 0.992, 0.992, 0.776, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.545, 0.973, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.945, 0.922, 0.992, 0.992, 0.992, 0.992, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.235, 0.784, 0.949, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.647, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.259, 0.612, 0.612, 0.863, 0.729, 0.992, 0.992, 0.992, 0.937, 0.337, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.204, 0.094, 0.71, 0.373, 0.306, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.443, 0.757, 0.996, 0.992, 0.996, 0.992, 0.996, 0.675, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.718, 0.992, 0.988, 0.992, 0.988, 0.992, 0.988, 0.992, 0.988, 0.953, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.796, 1.0, 0.914, 0.718, 0.4, 0.796, 0.796, 0.918, 0.992, 0.996, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.318, 0.592, 0.196, 0.0, 0.0, 0.0, 0.161, 0.757, 0.988, 0.992, 0.435, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.835, 0.996, 0.992, 0.796, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.835, 0.988, 0.992, 0.988, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.992, 0.996, 0.992, 0.996, 0.592, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.831, 0.992, 0.988, 0.992, 0.91, 0.875, 0.478, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.875, 0.996, 0.992, 0.996, 0.992, 0.996, 0.278, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.196, 0.514, 0.835, 0.988, 0.992, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.635, 0.996, 0.992, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.796, 0.992, 0.988, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.992, 0.996, 0.357, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.557, 0.992, 0.988, 0.914, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.839, 0.992, 0.996, 0.835, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.639, 0.953, 0.992, 0.988, 0.675, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.678, 0.678, 0.992, 1.0, 0.992, 0.878, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.988, 0.992, 0.988, 0.992, 0.671, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.992, 0.957, 0.796, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.753, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.204, 0.027, 0.365, 0.514, 0.514, 0.514, 0.753, 0.514, 0.922, 0.824, 0.514, 0.314, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.82, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.984, 0.949, 0.949, 0.973, 0.992, 0.996, 0.58, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.733, 0.937, 0.949, 0.827, 0.439, 0.439, 0.439, 0.31, 0.0, 0.0, 0.216, 0.604, 0.996, 0.749, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.996, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.286, 0.953, 0.996, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.878, 0.996, 0.937, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.055, 0.694, 0.996, 0.996, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.455, 0.996, 0.996, 0.922, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.404, 0.996, 0.996, 0.918, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.941, 0.996, 0.996, 0.51, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.188, 0.941, 0.996, 0.996, 0.369, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.318, 0.835, 0.996, 0.996, 0.604, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.22, 0.8, 0.996, 0.996, 0.604, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.227, 0.792, 0.996, 0.996, 0.604, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.31, 0.949, 0.996, 0.996, 0.608, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.537, 0.957, 0.996, 0.996, 0.612, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.467, 0.961, 0.996, 0.996, 0.616, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.722, 0.996, 0.996, 0.996, 0.706, 0.176, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.996, 0.996, 0.941, 0.224, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.537, 1.0, 0.949, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.329, 1.0, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.102, 0.984, 0.992, 0.957, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.188, 0.882, 0.812, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.953, 0.992, 0.965, 0.247, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.859, 0.98, 0.235, 0.016, 0.0, 0.0, 0.0, 0.0, 0.067, 0.749, 0.992, 0.992, 0.733, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.671, 0.992, 0.992, 0.439, 0.0, 0.0, 0.0, 0.0, 0.0, 0.353, 0.992, 0.992, 0.965, 0.525, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.62, 0.992, 0.992, 0.875, 0.376, 0.0, 0.0, 0.0, 0.0, 0.0, 0.404, 0.992, 0.992, 0.729, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.439, 0.992, 0.992, 0.992, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.667, 0.992, 0.992, 0.576, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.882, 0.992, 0.992, 0.808, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.569, 0.937, 0.992, 0.91, 0.165, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.984, 0.992, 0.992, 0.992, 0.824, 0.373, 0.204, 0.0, 0.0, 0.204, 0.776, 0.992, 0.957, 0.376, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.957, 0.992, 0.992, 0.992, 0.992, 0.992, 0.929, 0.769, 0.388, 0.788, 0.992, 0.992, 0.929, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.467, 0.761, 0.851, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.486, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.11, 0.278, 0.71, 0.788, 0.847, 0.992, 0.992, 0.992, 0.992, 0.882, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.573, 0.992, 0.992, 0.992, 0.745, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.686, 0.992, 0.992, 0.875, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.702, 0.992, 0.992, 0.486, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.792, 0.992, 0.973, 0.275, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.137, 0.878, 0.992, 0.894, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.282, 0.992, 0.992, 0.8, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.988, 0.992, 0.882, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.882, 0.875, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.2, 0.518, 0.596, 0.596, 0.757, 0.914, 0.996, 0.675, 0.757, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4, 0.718, 0.796, 0.875, 0.992, 0.988, 0.992, 0.988, 0.992, 0.988, 0.992, 0.988, 0.992, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.992, 0.996, 0.992, 0.996, 0.992, 0.996, 0.992, 0.996, 0.914, 0.796, 0.796, 0.918, 0.992, 1.0, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.592, 0.592, 0.592, 0.592, 0.357, 0.196, 0.196, 0.118, 0.0, 0.0, 0.439, 0.988, 0.992, 0.435, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.839, 0.992, 1.0, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.992, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.996, 0.992, 0.718, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.718, 0.992, 0.988, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.992, 0.996, 0.992, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.988, 0.992, 0.831, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.839, 0.992, 0.996, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4, 0.992, 0.988, 0.914, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.835, 0.996, 0.992, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.596, 0.988, 0.992, 0.831, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.996, 0.992, 0.957, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.875, 0.992, 0.988, 0.635, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.992, 0.996, 0.753, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.992, 0.988, 0.914, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.718, 0.996, 0.992, 0.322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.078, 0.992, 0.671, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.929, 0.369, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.992, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.925, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.314, 0.992, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.545, 0.996, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.871, 0.992, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.902, 0.992, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.902, 0.992, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.906, 0.969, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.953, 0.702, 0.0, 0.0, 0.0, 0.0, 0.067, 0.545, 0.541, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.306, 0.992, 0.537, 0.0, 0.0, 0.02, 0.553, 0.878, 0.969, 0.984, 0.941, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.992, 0.537, 0.0, 0.0, 0.576, 0.992, 0.859, 0.329, 0.639, 0.992, 0.404, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.635, 0.996, 0.541, 0.0, 0.6, 0.996, 0.608, 0.098, 0.0, 0.545, 0.996, 0.631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.992, 0.537, 0.525, 0.945, 0.545, 0.016, 0.0, 0.0, 0.741, 0.992, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.992, 0.537, 0.722, 0.69, 0.0, 0.0, 0.0, 0.224, 0.976, 0.918, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.992, 0.573, 0.82, 0.365, 0.0, 0.0, 0.0, 0.667, 0.992, 0.635, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.973, 0.937, 0.725, 0.365, 0.0, 0.027, 0.408, 1.0, 0.878, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.902, 0.984, 0.412, 0.035, 0.169, 0.651, 0.992, 0.945, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.976, 0.992, 0.816, 0.976, 0.992, 0.957, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.992, 0.996, 0.659, 0.467, 0.173, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.898, 0.392, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.765, 0.996, 0.988, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.2, 0.847, 0.996, 0.89, 0.275, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.804, 0.996, 0.996, 0.267, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.196, 0.969, 0.996, 0.667, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.553, 0.906, 0.988, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.486, 0.996, 0.992, 0.333, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.067, 0.431, 0.996, 0.996, 0.533, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.635, 0.973, 0.996, 0.549, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.435, 0.988, 0.996, 0.686, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.996, 0.871, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.773, 0.996, 0.878, 0.149, 0.0, 0.0, 0.0, 0.0, 0.286, 0.424, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.157, 0.639, 0.996, 0.894, 0.141, 0.0, 0.0, 0.0, 0.145, 0.604, 0.988, 0.996, 0.953, 0.145, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.624, 0.996, 0.996, 0.373, 0.0, 0.0, 0.0, 0.271, 0.906, 0.996, 0.996, 0.996, 0.996, 0.541, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.996, 0.894, 0.255, 0.004, 0.0, 0.0, 0.216, 0.992, 0.996, 0.996, 0.996, 0.996, 0.996, 0.204, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.302, 0.957, 0.949, 0.271, 0.0, 0.0, 0.0, 0.298, 0.894, 0.996, 0.996, 0.996, 0.996, 0.878, 0.431, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.553, 0.976, 0.643, 0.0, 0.0, 0.0, 0.231, 0.89, 0.996, 0.996, 0.996, 0.949, 0.286, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.698, 0.922, 0.369, 0.416, 0.467, 0.463, 0.839, 0.996, 0.996, 0.953, 0.894, 0.49, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.792, 0.996, 0.996, 0.996, 0.996, 0.961, 0.827, 0.365, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.494, 0.996, 0.863, 0.647, 0.647, 0.114, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.306, 0.765, 0.878, 1.0, 0.984, 0.447, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.502, 0.988, 0.988, 0.769, 0.502, 0.518, 0.922, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.29, 0.753, 0.996, 0.925, 0.318, 0.0, 0.0, 0.0, 0.282, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.486, 0.988, 0.976, 0.502, 0.035, 0.0, 0.0, 0.0, 0.0, 0.173, 0.545, 0.137, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.592, 0.996, 0.894, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.494, 0.996, 0.596, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.165, 0.953, 0.878, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.416, 0.996, 0.996, 0.596, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.588, 0.996, 0.655, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.18, 0.898, 0.996, 0.996, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.996, 0.655, 0.0, 0.0, 0.0, 0.0, 0.0, 0.31, 0.894, 0.965, 0.996, 0.984, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.267, 0.945, 0.894, 0.412, 0.0, 0.0, 0.0, 0.286, 0.898, 0.753, 0.478, 0.996, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.941, 0.984, 0.769, 0.565, 0.565, 0.914, 0.867, 0.224, 0.627, 0.996, 0.773, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.78, 0.937, 0.937, 0.937, 0.753, 0.063, 0.075, 0.878, 0.996, 0.447, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.286, 0.996, 0.91, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.545, 0.996, 0.71, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.847, 0.996, 0.553, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.996, 0.984, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.996, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.804, 0.996, 0.431, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.996, 0.996, 0.204, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.996, 0.996, 0.537, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.753, 0.996, 0.439, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.165, 0.388, 0.992, 0.486, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.569, 0.808, 0.984, 0.984, 0.984, 0.871, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.145, 0.992, 0.984, 0.984, 0.984, 0.984, 0.992, 0.42, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.667, 0.984, 0.992, 0.984, 0.984, 0.984, 0.984, 0.992, 0.663, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.984, 0.984, 0.992, 0.984, 0.984, 0.984, 0.984, 0.992, 0.984, 0.643, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.286, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.137, 0.0, 0.714, 1.0, 0.992, 0.992, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69, 0.984, 0.984, 0.984, 0.984, 0.992, 0.659, 0.059, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.651, 0.992, 0.984, 0.984, 0.984, 0.882, 0.643, 0.059, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.984, 0.992, 0.984, 0.984, 0.8, 0.161, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.612, 0.827, 0.984, 0.992, 0.984, 0.882, 0.161, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.165, 0.894, 0.992, 0.992, 1.0, 0.992, 0.643, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.714, 1.0, 0.992, 0.992, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.565, 0.984, 0.984, 0.984, 0.992, 0.659, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.565, 0.984, 0.984, 0.984, 0.745, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.565, 0.984, 0.984, 0.984, 0.706, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.565, 0.984, 0.984, 0.984, 0.706, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.412, 0.953, 0.992, 0.992, 1.0, 0.584, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.714, 1.0, 0.992, 0.992, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.663, 0.984, 0.992, 0.984, 0.925, 0.565, 0.565, 0.412, 0.0, 0.0, 0.0, 0.706, 0.992, 0.984, 0.984, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.843, 0.992, 0.984, 0.984, 0.984, 0.984, 0.953, 0.851, 0.851, 0.851, 0.945, 0.992, 0.984, 0.965, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.686, 0.984, 0.984, 0.984, 0.984, 0.992, 0.984, 0.984, 0.984, 0.984, 0.992, 0.984, 0.537, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.082, 0.278, 0.38, 0.984, 0.984, 0.992, 0.984, 0.984, 0.984, 0.478, 0.282, 0.278, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.502, 1.0, 1.0, 0.502, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 1.0, 0.749, 1.0, 1.0, 0.502, 0.502, 0.502, 0.502, 0.502, 0.749, 1.0, 1.0, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.749, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.749, 1.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 0.749, 0.251, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.749, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.502, 0.502, 1.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.141, 0.945, 0.212, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 0.992, 0.212, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.298, 0.192, 0.0, 0.0, 0.0, 0.0, 0.0, 0.196, 0.957, 0.969, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.788, 0.992, 0.788, 0.027, 0.0, 0.0, 0.0, 0.0, 0.792, 0.992, 0.824, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.635, 0.992, 0.992, 0.357, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.996, 0.894, 0.0, 0.0, 0.0, 0.024, 0.667, 1.0, 0.753, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.404, 0.98, 0.992, 0.435, 0.0, 0.0, 0.0, 0.149, 0.992, 0.996, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.906, 0.992, 0.573, 0.043, 0.0, 0.0, 0.125, 0.835, 0.992, 0.698, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.329, 0.996, 0.992, 0.702, 0.047, 0.0, 0.047, 0.353, 0.886, 0.992, 0.965, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.18, 0.757, 0.992, 0.996, 0.459, 0.043, 0.31, 0.71, 0.894, 0.992, 0.992, 0.965, 0.282, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.141, 0.945, 0.996, 0.996, 1.0, 0.996, 0.996, 0.996, 0.996, 1.0, 0.996, 0.996, 0.694, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.298, 0.961, 0.992, 0.992, 0.996, 0.992, 0.882, 0.753, 0.424, 0.792, 0.992, 0.91, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.294, 0.498, 0.271, 0.141, 0.071, 0.0, 0.055, 0.945, 0.961, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.525, 0.996, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.992, 0.894, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.043, 0.843, 0.996, 0.259, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.992, 0.584, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.922, 0.906, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.569, 0.992, 0.416, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.569, 0.941, 0.137, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.561, 0.996, 0.996, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.235, 0.569, 0.353, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.098, 0.624, 0.992, 0.992, 0.475, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.204, 0.961, 0.992, 0.851, 0.125, 0.0, 0.0, 0.0, 0.0, 0.027, 0.573, 0.992, 0.992, 0.992, 0.933, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.804, 0.992, 0.992, 0.851, 0.031, 0.094, 0.02, 0.192, 0.569, 0.992, 0.992, 0.992, 0.922, 0.231, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.714, 0.992, 0.992, 0.992, 0.725, 0.851, 0.737, 0.992, 0.992, 0.992, 0.992, 0.914, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.49, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.925, 0.345, 0.157, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.914, 0.992, 0.992, 0.992, 0.992, 0.996, 0.961, 0.659, 0.345, 0.145, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.094, 0.851, 0.992, 0.847, 0.412, 0.141, 0.141, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.992, 0.992, 0.776, 0.631, 0.675, 0.455, 0.859, 0.431, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.467, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.984, 0.949, 0.224, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.459, 0.988, 0.996, 0.996, 0.976, 0.949, 0.957, 0.949, 0.949, 0.961, 0.996, 0.996, 0.455, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.463, 0.898, 0.49, 0.243, 0.0, 0.0, 0.0, 0.0, 0.082, 0.788, 0.992, 0.992, 0.184, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.847, 0.992, 0.851, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.129, 0.529, 0.918, 0.992, 0.737, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.376, 0.714, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.745, 0.992, 0.992, 0.992, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.886, 0.992, 0.961, 0.443, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.333, 0.792, 0.992, 0.992, 0.992, 0.376, 0.145, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.227, 0.992, 0.992, 0.992, 0.859, 0.808, 0.647, 0.337, 0.192, 0.322, 0.459, 0.992, 0.992, 0.992, 0.992, 0.737, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.184, 0.851, 0.992, 0.992, 0.992, 0.992, 0.992, 0.933, 0.988, 0.996, 0.992, 0.992, 0.992, 0.608, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.494, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.616, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.388, 0.882, 0.992, 0.992, 0.992, 0.992, 0.749, 0.475, 0.043, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.494, 1.0, 0.894, 0.553, 0.553, 0.553, 0.553, 0.553, 0.357, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.894, 0.988, 0.988, 0.988, 0.992, 0.988, 0.988, 0.988, 0.737, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.22, 0.561, 0.463, 0.22, 0.22, 0.561, 0.804, 0.969, 0.259, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.992, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.447, 0.886, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.447, 0.996, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.906, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.737, 0.918, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.992, 0.745, 0.075, 0.0, 0.0, 0.0, 0.263, 0.965, 0.882, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.796, 0.988, 0.757, 0.247, 0.0, 0.0, 0.725, 0.988, 0.49, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.541, 0.922, 0.992, 0.753, 0.259, 0.8, 0.992, 0.396, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.11, 0.522, 0.992, 0.988, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.306, 0.988, 0.988, 0.988, 0.725, 0.333, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.988, 0.988, 0.988, 0.992, 0.988, 0.757, 0.247, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.992, 0.992, 0.306, 0.494, 0.906, 0.992, 0.796, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.988, 0.988, 0.11, 0.0, 0.075, 0.843, 0.988, 0.443, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.988, 0.988, 0.11, 0.0, 0.0, 0.576, 0.988, 0.639, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.988, 0.988, 0.11, 0.0, 0.0, 0.773, 0.988, 0.247, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.114, 0.992, 0.992, 0.157, 0.0, 0.176, 0.898, 0.745, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.769, 0.988, 0.647, 0.275, 0.882, 0.988, 0.451, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.247, 0.918, 0.988, 0.992, 0.988, 0.58, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.941, 0.992, 0.694, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.165, 0.184, 0.361, 0.839, 1.0, 0.765, 0.898, 0.376, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.773, 0.969, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.969, 0.373, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.529, 0.957, 0.976, 0.992, 0.992, 0.992, 0.933, 0.851, 0.851, 0.851, 0.961, 0.992, 0.812, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.992, 0.992, 0.992, 0.706, 0.404, 0.149, 0.0, 0.0, 0.0, 0.753, 0.992, 0.686, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.992, 0.859, 0.369, 0.012, 0.0, 0.0, 0.0, 0.0, 0.02, 0.776, 0.941, 0.455, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.169, 0.306, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.729, 0.992, 0.467, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.627, 0.992, 0.973, 0.106, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.282, 0.863, 0.992, 0.992, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.773, 0.992, 0.992, 0.341, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.498, 0.992, 0.953, 0.631, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.345, 0.784, 0.992, 0.631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.686, 0.992, 0.992, 0.741, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.58, 0.992, 0.992, 0.992, 0.89, 0.753, 0.753, 0.753, 0.753, 0.537, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.345, 0.847, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.953, 0.702, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.624, 0.984, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.918, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.918, 0.992, 0.992, 0.992, 0.992, 0.992, 0.922, 0.643, 0.333, 0.098, 0.098, 0.098, 0.408, 0.506, 0.824, 0.992, 0.992, 0.933, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.992, 0.992, 0.906, 0.686, 0.325, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.102, 0.38, 0.992, 0.992, 0.698, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.98, 0.671, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.357, 0.988, 0.992, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.369, 0.231, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.855, 0.992, 0.584, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.855, 0.992, 0.424, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.992, 0.918, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.439, 0.91, 0.992, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.796, 0.996, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.796, 0.992, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.796, 0.996, 0.196, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.953, 0.914, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.992, 0.878, 0.078, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.518, 0.988, 0.796, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.596, 0.992, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.757, 0.988, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.557, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.518, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.992, 0.514, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.992, 1.0, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.835, 0.988, 0.992, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.361, 0.992, 1.0, 0.592, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.831, 0.992, 0.275, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.502, 1.0, 1.0, 1.0, 0.749, 0.502, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.502, 0.251, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 0.502, 0.251, 0.749, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 0.749, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 1.0, 1.0, 1.0, 0.502, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.749, 1.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.502, 0.749, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.251, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 1.0, 1.0, 0.749, 0.502, 0.0, 0.0, 0.0, 0.0, 0.251, 0.749, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.749, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.251, 0.502, 1.0, 0.749, 0.749, 0.502, 0.502, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.667, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.827, 0.859, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.996, 0.965, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.996, 0.835, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.024, 0.996, 0.835, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.439, 0.996, 0.639, 0.0, 0.0, 0.0, 0.0, 0.024, 0.424, 0.149, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.957, 0.996, 0.361, 0.0, 0.0, 0.0, 0.0, 0.059, 0.996, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.467, 0.996, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.773, 0.996, 0.604, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.957, 0.976, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.98, 0.996, 0.812, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.675, 0.996, 0.537, 0.0, 0.0, 0.0, 0.0, 0.0, 0.314, 0.992, 1.0, 0.486, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.871, 0.675, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.447, 0.996, 0.996, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.671, 0.996, 0.929, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.447, 0.996, 0.996, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.745, 0.996, 0.996, 0.996, 0.91, 0.831, 0.447, 0.584, 0.91, 0.949, 0.996, 0.996, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.145, 0.647, 0.953, 0.953, 0.996, 0.996, 0.996, 0.996, 0.98, 0.953, 0.996, 0.996, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.42, 0.42, 0.42, 0.42, 0.263, 0.0, 0.98, 1.0, 0.404, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.675, 0.996, 0.812, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.996, 0.859, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.996, 0.937, 0.188, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.996, 0.812, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.298, 0.725, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.424, 0.906, 1.0, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.067, 0.725, 0.98, 0.992, 0.996, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.827, 0.992, 0.992, 0.992, 0.569, 0.165, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.937, 0.992, 0.992, 0.788, 0.161, 0.031, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.765, 0.996, 0.992, 0.604, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.624, 0.996, 0.945, 0.557, 0.0, 0.0, 0.09, 0.471, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.439, 0.98, 0.961, 0.557, 0.0, 0.027, 0.494, 0.898, 0.859, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.686, 0.992, 0.494, 0.075, 0.486, 0.78, 0.992, 0.753, 0.118, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.969, 0.847, 0.078, 0.643, 0.992, 0.992, 0.847, 0.106, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.875, 0.957, 0.851, 0.996, 0.992, 0.725, 0.133, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.69, 0.996, 0.996, 1.0, 0.741, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.945, 0.992, 0.992, 0.8, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.886, 0.992, 0.992, 0.992, 0.835, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.788, 0.992, 0.992, 0.702, 0.898, 0.996, 0.42, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.529, 0.996, 0.992, 0.604, 0.02, 0.235, 0.996, 0.914, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.996, 1.0, 0.592, 0.0, 0.0, 0.09, 1.0, 0.918, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.886, 0.082, 0.0, 0.102, 0.663, 0.996, 0.737, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.835, 0.31, 0.573, 0.851, 0.992, 0.922, 0.224, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.478, 0.992, 0.996, 0.992, 0.992, 0.992, 0.725, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.522, 0.996, 0.667, 0.608, 0.255, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.624, 0.847, 0.612, 0.439, 0.231, 0.106, 0.09, 0.322, 0.58, 0.071, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239, 0.973, 0.992, 0.996, 0.992, 0.992, 0.91, 0.898, 0.996, 0.992, 0.306, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.922, 0.682, 0.78, 0.871, 0.753, 0.686, 0.506, 0.094, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.216, 0.992, 0.765, 0.0, 0.024, 0.047, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.992, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.996, 0.769, 0.11, 0.231, 0.439, 0.467, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.533, 0.992, 0.871, 0.851, 0.992, 0.992, 0.992, 0.961, 0.514, 0.196, 0.008, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.992, 0.996, 0.992, 0.894, 0.624, 0.451, 0.757, 0.992, 0.992, 0.459, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.918, 0.992, 0.996, 0.506, 0.051, 0.0, 0.0, 0.02, 0.282, 0.945, 0.98, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.624, 0.992, 0.376, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.961, 0.902, 0.149, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.596, 0.996, 0.675, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.886, 0.996, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.118, 0.647, 0.996, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.529, 0.996, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.906, 0.8, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.678, 0.996, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.529, 1.0, 0.886, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.529, 1.0, 0.682, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.357, 0.996, 0.922, 0.459, 0.035, 0.0, 0.0, 0.0, 0.0, 0.102, 0.812, 0.996, 0.357, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.608, 0.992, 0.992, 0.827, 0.396, 0.0, 0.0, 0.098, 0.675, 0.992, 0.835, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.282, 0.886, 0.992, 0.992, 0.922, 0.918, 0.941, 0.992, 0.929, 0.208, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.58, 0.902, 0.996, 0.992, 0.992, 0.784, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.282, 0.961, 0.996, 0.996, 0.996, 0.565, 0.565, 0.733, 0.961, 0.471, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.333, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.992, 0.89, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.992, 0.992, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.98, 0.992, 0.992, 0.675, 0.153, 0.039, 0.043, 0.345, 0.867, 0.992, 0.992, 0.773, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.992, 0.992, 0.992, 0.388, 0.0, 0.0, 0.0, 0.0, 0.137, 0.345, 0.345, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.522, 0.992, 0.992, 0.992, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.502, 0.992, 0.992, 0.992, 0.867, 0.478, 0.478, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.949, 0.608, 0.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.894, 0.486, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.012, 0.502, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.945, 0.153, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.435, 0.435, 0.173, 0.0, 0.192, 0.494, 0.957, 0.996, 0.996, 0.902, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.51, 0.949, 0.992, 0.992, 0.757, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.467, 0.992, 0.992, 0.992, 0.447, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.537, 0.992, 0.992, 0.933, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.965, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.804, 0.992, 0.992, 0.949, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.992, 0.761, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.463, 0.984, 0.992, 0.992, 0.949, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.376, 0.992, 0.992, 0.929, 0.702, 0.216, 0.047, 0.047, 0.294, 0.592, 0.98, 0.992, 0.992, 0.992, 0.914, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.596, 0.933, 0.992, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.992, 0.992, 0.992, 0.706, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.169, 0.835, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.51, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.102, 0.337, 0.486, 0.451, 0.937, 0.996, 0.992, 0.824, 0.145, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.075, 0.133, 0.133, 0.51, 0.51, 0.435, 0.565, 0.565, 0.847, 0.996, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.267, 0.8, 0.863, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.992, 0.992, 0.949, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.961, 0.604, 0.737, 0.424, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.992, 0.941, 0.773, 0.475, 0.855, 0.475, 0.306, 0.039, 0.0, 0.016, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.584, 0.137, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.071, 0.922, 0.871, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.11, 0.992, 0.647, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.596, 0.992, 0.518, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.824, 0.992, 0.992, 0.757, 0.737, 0.42, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.522, 0.992, 0.992, 0.992, 0.992, 0.992, 0.694, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.745, 0.953, 0.867, 0.867, 0.965, 0.996, 0.945, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.067, 0.196, 0.0, 0.0, 0.357, 0.847, 1.0, 0.914, 0.122, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.075, 0.827, 0.992, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.992, 0.976, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.227, 0.992, 0.737, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.231, 0.227, 0.0, 0.0, 0.0, 0.153, 0.906, 0.992, 0.455, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.29, 0.965, 0.647, 0.0, 0.008, 0.369, 0.89, 0.996, 0.965, 0.165, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.259, 0.992, 0.706, 0.573, 0.675, 0.992, 0.992, 0.976, 0.251, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.89, 0.992, 0.992, 0.992, 0.992, 0.863, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.463, 0.804, 0.992, 0.918, 0.431, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.49, 0.816, 0.996, 0.996, 0.973, 0.478, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.122, 0.463, 0.463, 0.82, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.733, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.494, 0.957, 0.992, 0.992, 0.992, 0.702, 0.557, 0.557, 0.592, 0.992, 0.992, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 0.98, 0.992, 0.992, 0.992, 0.827, 0.027, 0.0, 0.0, 0.047, 0.816, 0.992, 0.69, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.325, 0.894, 0.976, 0.992, 0.992, 0.992, 0.608, 0.082, 0.0, 0.0, 0.0, 0.071, 0.949, 0.992, 0.529, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.325, 0.851, 0.992, 0.992, 0.992, 0.992, 0.604, 0.035, 0.0, 0.0, 0.0, 0.0, 0.078, 0.992, 0.992, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.333, 0.929, 0.992, 0.992, 0.992, 0.992, 0.906, 0.435, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.773, 0.992, 0.745, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.439, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.318, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.345, 0.992, 0.992, 0.482, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.141, 0.78, 0.882, 0.992, 0.992, 0.992, 0.992, 0.992, 0.361, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.49, 0.992, 0.827, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.616, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.361, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.212, 0.902, 0.98, 0.341, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.886, 0.863, 0.992, 0.992, 0.992, 0.624, 0.047, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.612, 0.992, 0.969, 0.235, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.278, 0.149, 0.804, 0.992, 0.992, 0.627, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.533, 0.886, 0.976, 0.486, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.322, 0.98, 0.992, 0.831, 0.102, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.569, 0.992, 0.992, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.537, 0.992, 0.992, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.306, 0.992, 0.992, 0.898, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.906, 0.992, 0.992, 0.165, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.569, 0.992, 0.992, 0.925, 0.184, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.255, 0.788, 0.992, 0.78, 0.243, 0.0, 0.0, 0.086, 0.251, 0.612, 0.702, 0.992, 0.973, 0.545, 0.38, 0.145, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.973, 0.992, 0.992, 0.431, 0.078, 0.51, 0.922, 0.992, 0.992, 0.992, 0.871, 0.333, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.71, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.992, 0.953, 0.824, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.949, 0.98, 0.992, 0.992, 0.992, 0.992, 0.973, 0.557, 0.106, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.212, 0.482, 0.482, 0.482, 0.482, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.71, 0.329, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.651, 0.996, 0.694, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.686, 0.996, 0.996, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.204, 0.965, 0.953, 0.322, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.58, 0.89, 0.996, 0.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.761, 0.996, 0.996, 0.541, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.549, 0.984, 0.996, 0.482, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.427, 0.992, 0.996, 0.996, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.576, 0.996, 0.996, 0.714, 0.027, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.169, 0.961, 0.996, 0.859, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.608, 0.996, 0.996, 0.424, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.792, 0.996, 0.996, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.792, 0.996, 0.996, 0.239, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.275, 0.275, 0.275, 0.275, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.792, 0.996, 0.996, 0.286, 0.063, 0.063, 0.435, 0.38, 0.671, 0.698, 0.996, 0.996, 0.996, 0.996, 0.569, 0.063, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.98, 0.263, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.141, 0.894, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.925, 0.753, 0.875, 0.996, 0.996, 0.996, 0.996, 0.996, 0.675, 0.043, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.576, 0.996, 0.996, 0.996, 0.996, 0.996, 1.0, 0.89, 0.639, 0.816, 0.996, 0.996, 0.996, 0.996, 0.996, 0.706, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.576, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.969, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.459, 0.784, 0.816, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.984, 0.784, 0.267, 0.173, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.063, 0.392, 0.965, 0.996, 1.0, 0.996, 0.941, 0.392, 0.392, 0.369, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.565, 0.992, 0.506, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.196, 0.918, 0.988, 0.776, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.522, 0.973, 0.988, 0.776, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.533, 0.973, 0.988, 0.988, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.475, 0.988, 0.988, 0.988, 0.988, 0.259, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.859, 0.988, 0.988, 0.922, 0.365, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.992, 0.988, 0.988, 0.675, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.816, 0.992, 0.988, 0.988, 0.675, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.008, 0.596, 0.988, 0.992, 0.988, 0.988, 0.373, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.384, 0.988, 0.988, 0.992, 0.988, 0.792, 0.075, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.631, 0.992, 0.992, 1.0, 0.792, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.153, 0.969, 0.988, 0.988, 0.792, 0.055, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.627, 0.988, 0.988, 0.988, 0.522, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.345, 0.941, 0.988, 0.988, 0.82, 0.082, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.459, 0.988, 0.988, 0.973, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.196, 0.922, 0.988, 0.988, 0.933, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.651, 0.988, 0.988, 0.988, 0.576, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.318, 0.988, 0.988, 0.988, 0.588, 0.153, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.298, 0.949, 0.988, 0.988, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.467, 0.741, 0.173, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.114, 0.016, 0.408, 0.898, 0.992, 0.506, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.333, 0.988, 0.702, 0.988, 0.988, 0.988, 0.992, 0.478, 0.051, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.482, 0.988, 0.992, 0.988, 0.58, 0.22, 0.992, 0.988, 0.561, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.965, 0.988, 0.992, 0.4, 0.024, 0.0, 0.6, 0.988, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.667, 0.992, 0.992, 1.0, 0.329, 0.0, 0.0, 0.051, 0.812, 0.992, 0.357, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.988, 0.988, 0.969, 0.255, 0.0, 0.0, 0.0, 0.663, 0.988, 0.843, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.988, 0.988, 0.392, 0.0, 0.0, 0.0, 0.0, 0.322, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.102, 0.953, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.161, 0.992, 0.992, 0.992, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.992, 0.992, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.553, 0.988, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.898, 0.988, 0.878, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.988, 0.988, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.996, 0.992, 0.659, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.471, 0.992, 0.745, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.365, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.663, 0.988, 0.255, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.039, 0.773, 0.988, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.992, 0.988, 0.612, 0.0, 0.0, 0.0, 0.0, 0.0, 0.702, 0.988, 0.694, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.702, 0.992, 0.698, 0.063, 0.0, 0.075, 0.259, 0.749, 0.996, 0.82, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.086, 0.918, 0.988, 0.843, 0.663, 0.882, 0.988, 0.988, 0.82, 0.11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.514, 0.988, 0.988, 0.992, 0.988, 0.988, 0.839, 0.098, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.027, 0.255, 0.941, 0.992, 0.694, 0.404, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.318, 1.0, 0.302, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.976, 0.992, 0.616, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.976, 0.992, 0.871, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.286, 0.988, 0.992, 0.737, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.388, 0.992, 0.992, 0.467, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.671, 0.992, 0.992, 0.467, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.098, 0.886, 0.992, 0.992, 0.275, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.208, 0.992, 0.992, 0.992, 0.059, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.545, 0.992, 0.992, 0.525, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.016, 0.847, 0.992, 0.992, 0.243, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.349, 0.992, 0.992, 0.929, 0.149, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.431, 0.992, 0.992, 0.765, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.737, 0.992, 0.992, 0.282, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.184, 0.918, 0.992, 0.992, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.992, 0.875, 0.012, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.031, 0.82, 0.992, 0.992, 0.408, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.173, 0.992, 0.992, 0.855, 0.067, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.651, 0.992, 0.992, 0.773, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.875, 0.992, 0.992, 0.384, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.353, 0.855, 0.984, 0.227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.816, 0.992, 0.498, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.051, 0.843, 0.988, 0.902, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.988, 0.988, 0.78, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.988, 0.988, 0.824, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.988, 0.988, 0.902, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.275, 0.992, 0.992, 0.906, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.224, 0.957, 0.988, 0.902, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.988, 0.902, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.988, 0.902, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.988, 0.945, 0.165, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.816, 0.992, 1.0, 0.361, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.988, 0.992, 0.361, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.812, 0.988, 0.992, 0.686, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.443, 0.988, 0.992, 0.808, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.988, 0.992, 0.89, 0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.992, 1.0, 0.992, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.988, 0.992, 0.988, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.988, 0.992, 0.988, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.365, 0.988, 0.992, 0.988, 0.271, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.035, 0.702, 0.992, 0.616, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.58, 0.996, 0.996, 0.333, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.953, 0.992, 0.992, 0.514, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.953, 0.992, 0.992, 0.349, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.376, 0.992, 0.992, 0.659, 0.004, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.392, 0.992, 0.992, 0.549, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.729, 0.992, 0.992, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.125, 0.906, 0.992, 0.776, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.043, 0.035, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.992, 0.992, 0.345, 0.0, 0.0, 0.0, 0.263, 0.608, 0.608, 0.608, 0.776, 0.98, 0.91, 0.459, 0.016, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.992, 0.992, 0.345, 0.0, 0.149, 0.38, 0.929, 0.992, 0.996, 0.992, 0.992, 0.992, 0.992, 0.992, 0.216, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.992, 0.992, 0.345, 0.494, 0.929, 0.992, 0.992, 0.992, 0.996, 0.992, 0.992, 0.992, 0.992, 0.992, 0.894, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.941, 0.816, 0.435, 0.435, 0.435, 0.745, 0.996, 0.996, 0.824, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.922, 0.992, 0.992, 0.992, 0.992, 0.949, 0.733, 0.227, 0.0, 0.0, 0.0, 0.0, 0.067, 0.729, 0.992, 0.631, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.263, 0.992, 0.992, 0.992, 0.992, 0.957, 0.141, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.349, 0.992, 0.647, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.149, 0.922, 0.992, 0.843, 0.325, 0.039, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.592, 0.992, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.09, 0.514, 0.984, 0.627, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.22, 0.902, 0.992, 0.388, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.953, 0.949, 0.506, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.373, 0.922, 0.992, 0.62, 0.086, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.745, 0.992, 0.965, 0.498, 0.047, 0.047, 0.047, 0.047, 0.349, 0.835, 0.992, 0.992, 0.98, 0.306, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.243, 0.859, 0.992, 0.992, 0.992, 0.992, 0.992, 1.0, 0.992, 0.992, 0.992, 0.878, 0.345, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.059, 0.702, 0.992, 0.992, 0.992, 0.992, 0.996, 0.992, 0.569, 0.447, 0.09, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.047, 0.522, 0.561, 0.69, 0.992, 0.561, 0.447, 0.024, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], "labels": [8, 0, 1, 1, 9, 6, 2, 5, 3, 4, 0, 6, 7, 2, 2, 1, 1, 1, 3, 7, 8, 7, 6, 3, 7, 4, 7, 6, 6, 9, 0, 0, 4, 5, 8, 2, 1, 5, 4, 8, 5, 5, 5, 0, 6, 1, 0, 1, 1, 6]} \ No newline at end of file diff --git a/demos/mnist/softmax_linear_biases b/demos/mnist/softmax_linear_biases new file mode 100644 index 0000000000..eb8fee70f0 --- /dev/null +++ b/demos/mnist/softmax_linear_biases @@ -0,0 +1 @@ +$ý[ >x­®¼’nú½Šr½îù>ÈÃ< >à ¾ñ½ \ No newline at end of file diff --git a/demos/mnist/softmax_linear_weights b/demos/mnist/softmax_linear_weights new file mode 100644 index 0000000000000000000000000000000000000000..3d875142f2ab8e7d89fb507d05d5372fda76fbb9 GIT binary patch literal 1280 zcmWN<3oz7q8~|_>V$>BPDWp<*5D~Ti?{~}HMn-zv7w#_V-=ZlG{pDy7P!p4AOW-$yM&=^GQ8$6=C6Z3a>eZPc*b>Oe>emG`L2UVk0Qb zQgDr*W0}eJH+0jABBSHR+g9W z2)@`HgFVaC**uLSba^<2JH+mSYNwEGwR3~Hyb(6?@B}$F9VXKh3!afrK3MoGU|d)Z zj|RusKx8lt+elC=D+BZ;M^SR11bL9pO6(1!s`YxZo61pCw6=kPB#rRFMYyVEM~R*- z^k$+nrsk^WLX%e$2aA002C@}NIytaPTqueF%+#GfvepT+M>szH78O2)7InO;@@ zuXQ=)yenN_L6k{-lM*A1!`9mlI*@hn(ViL-)$4b)#_|RHDL$UyCtEpxSHmh z%wnXE8g9{xgt5YRkl}8J4iUdVkbw(a2<&4=1C=P>n?>2yUukn(83bP7@n@EmL+sU6 z*yhpzi8VRWr&=p1_`W7Re>cX~XZ}S^)2F%NxL$Cry}^b8)}epQ3%0l11a6D7Ij_ET zy#Dta@Lf?F1SUjaoa8dfE7!<^cCM%8mSH3e_{2_RC*m%t4}ChKhi1=oIIBr5deE_v z$LSctcX0?v%%n9~A^JdngaGuKDc)tS_mT+9YKV_+gB3NBfRD7{O7p;6k{ z9bJe!%M2O6M2}npZ0WSJm`N>7Q2fyX<`q4}$;Z~1$j@NN}qSvY@g~!h0A-UG@UPNbu2}_VLP1MpU<87H!ilGZ@SCWltw#*^aI_=18XuF@)_7>|QI$$HT&ewz0IS$-8Yyc)<;7I!4NAe!@u{#v z&LbPQ0Pyt`(EbE7l0;UK)o3UMPZhxrw`J^EgbLruQ;BOG40xemv~h)5Kh~EHLe~Np zT07J&eUWR5;(`cisNf*`nyz57AB#b3Jq+IFH>pY($%UkbF;V0a+%+PB%u9i6s#d_{ z#+AhDS&A}D!drP^=r?;5bo0`&;B7vIk4@5>U~Mk7;{i2#b#Nam`)T6kHMI1wCEu4e zkTmxosYgbz_^4cz@6=;0hJ{!cunKFdoWUvHka(u{*ff)jrgQq)?!+~8Wmt#6JRD6H qpJ!<`StvTR9h##x+1Yf)!d$jMe1I2tbzf&gxig&RJrTMkq|<*WNPwjP literal 0 HcmV?d00001 diff --git a/demos/model-builder/bundle.js b/demos/model-builder/bundle.js new file mode 100644 index 0000000000..845f76f5e8 --- /dev/null +++ b/demos/model-builder/bundle.js @@ -0,0 +1,9461 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + var lastLayer = this.hiddenLayers[this.hiddenLayers.length - 1]; + valid = valid && + learnjs_1.util.arraysEqual(this.labelShape, lastLayer.getOutputShape()); + } + this.isValid = valid && (this.hiddenLayers.length > 0); + }; + ModelBuilder.prototype.layerParamChanged = function () { + var lastOutputShape = this.inputShape; + for (var i = 0; i < this.hiddenLayers.length; i++) { + lastOutputShape = this.hiddenLayers[i].setInputShape(lastOutputShape); + } + this.validateModel(); + if (this.isValid) { + this.createModel(); + this.startInference(); + } + }; + ModelBuilder.prototype.downloadModel = function () { + var modelJson = this.getModelAsJson(); + var blob = new Blob([modelJson], { type: 'text/json' }); + var textFile = window.URL.createObjectURL(blob); + var a = document.createElement('a'); + document.body.appendChild(a); + a.style.display = 'none'; + a.href = textFile; + a.download = this.selectedDatasetName + '_model'; + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(textFile); + }; + ModelBuilder.prototype.uploadModel = function () { + this.querySelector('#model-file').click(); + }; + ModelBuilder.prototype.setupUploadModelButton = function () { + var _this = this; + var fileInput = this.querySelector('#model-file'); + fileInput.addEventListener('change', function (event) { + var file = fileInput.files[0]; + fileInput.value = ''; + var fileReader = new FileReader(); + fileReader.onload = function (evt) { + _this.removeAllLayers(); + var modelJson = fileReader.result; + _this.loadModelFromJson(modelJson); + }; + fileReader.readAsText(file); + }); + }; + ModelBuilder.prototype.getModelAsJson = function () { + var layerBuilders = []; + for (var i = 0; i < this.hiddenLayers.length; i++) { + layerBuilders.push(this.hiddenLayers[i].layerBuilder); + } + return JSON.stringify(layerBuilders); + }; + ModelBuilder.prototype.loadModelFromJson = function (modelJson) { + var lastOutputShape = this.inputShape; + var layerBuilders = JSON.parse(modelJson); + for (var i = 0; i < layerBuilders.length; i++) { + var modelLayer = this.addLayer(); + modelLayer.loadParamsFromLayerBuilder(lastOutputShape, layerBuilders[i]); + lastOutputShape = this.hiddenLayers[i].setInputShape(lastOutputShape); + } + this.validateModel(); + }; + ModelBuilder.prototype.uploadWeights = function () { + this.querySelector('#weights-file').click(); + }; + ModelBuilder.prototype.setupUploadWeightsButton = function () { + var _this = this; + var fileInput = this.querySelector('#weights-file'); + fileInput.addEventListener('change', function (event) { + var file = fileInput.files[0]; + fileInput.value = ''; + var fileReader = new FileReader(); + fileReader.onload = function (evt) { + var weightsJson = fileReader.result; + _this.loadWeightsFromJson(weightsJson); + _this.createModel(); + _this.startInference(); + }; + fileReader.readAsText(file); + }); + }; + ModelBuilder.prototype.loadWeightsFromJson = function (weightsJson) { + this.loadedWeights = JSON.parse(weightsJson); + }; + return ModelBuilder; +}(exports.ModelBuilderPolymer)); +exports.ModelBuilder = ModelBuilder; +document.registerElement(ModelBuilder.prototype.is, ModelBuilder); + +},{"../demo-footer":1,"../demo-header":2,"../learnjs":3,"../ndarray-image-visualizer":9,"../ndarray-logits-visualizer":10,"../polymer-spec":11,"../xhr-dataset":12,"./model-layer":6,"./model_builder_util":7,"./tensorflow":8}],6:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var polymer_spec_1 = require("../polymer-spec"); +var layer_builder = require("./layer_builder"); +var model_builder_util = require("./model_builder_util"); +exports.ModelLayerPolymer = polymer_spec_1.PolymerElement({ + is: 'model-layer', + properties: { + layerName: String, + inputShapeDisplay: String, + outputShapeDisplay: String, + isStatic: { type: Boolean, value: false }, + layerNames: Array, + selectedLayerName: String, + hasError: { type: Boolean, value: false }, + errorMessages: Array, + } +}); +var ModelLayer = (function (_super) { + __extends(ModelLayer, _super); + function ModelLayer() { + return _super !== null && _super.apply(this, arguments) || this; + } + ModelLayer.prototype.initialize = function (modelBuilder, inputShape) { + var _this = this; + this.modelBuilder = modelBuilder; + this.paramContainer = + this.querySelector('.param-container'); + this.layerNames = [ + 'Fully connected', 'ReLU', 'Convolution', 'Max pool', 'Reshape', 'Flatten' + ]; + this.inputShape = inputShape; + this.buildParamsUI('Fully connected', this.inputShape); + this.querySelector('.dropdown-content').addEventListener('iron-activate', function (event) { + _this.buildParamsUI(event.detail.selected, _this.inputShape); + }); + this.querySelector('#remove-layer').addEventListener('click', function (event) { + modelBuilder.removeLayer(_this); + }); + }; + ModelLayer.prototype.setInputShape = function (shape) { + this.inputShape = shape; + this.inputShapeDisplay = + model_builder_util.getDisplayShape(this.inputShape); + var errors = []; + var validationErrors = this.layerBuilder.validate(this.inputShape); + if (validationErrors != null) { + for (var i = 0; i < validationErrors.length; i++) { + errors.push('Error: ' + validationErrors[i]); + } + } + try { + this.outputShape = this.layerBuilder.getOutputShape(this.inputShape); + } + catch (e) { + errors.push(e); + } + this.outputShapeDisplay = + model_builder_util.getDisplayShape(this.outputShape); + if (errors.length > 0) { + this.hasError = true; + this.errorMessages = errors; + } + else { + this.hasError = false; + this.errorMessages = []; + } + return this.outputShape; + }; + ModelLayer.prototype.isValid = function () { + return !this.hasError; + }; + ModelLayer.prototype.getOutputShape = function () { + return this.outputShape; + }; + ModelLayer.prototype.addLayer = function (g, network, index, weights) { + return this.layerBuilder.addLayer(g, network, this.inputShape, index, weights); + }; + ModelLayer.prototype.buildParamsUI = function (layerName, inputShape, layerBuilderJson) { + this.selectedLayerName = layerName; + this.layerBuilder = + layer_builder.getLayerBuilder(layerName, layerBuilderJson); + this.paramContainer.innerHTML = ''; + var layerParams = this.layerBuilder.getLayerParams(); + for (var i = 0; i < layerParams.length; i++) { + var initialValue = layerBuilderJson != null ? + layerParams[i].getValue() : + layerParams[i].initialValue(inputShape); + this.addParamField(layerParams[i].label, initialValue, layerParams[i].setValue, layerParams[i].type, layerParams[i].min, layerParams[i].max); + } + this.modelBuilder.layerParamChanged(); + }; + ModelLayer.prototype.loadParamsFromLayerBuilder = function (inputShape, layerBuilderJson) { + this.buildParamsUI(layerBuilderJson.layerName, inputShape, layerBuilderJson); + }; + ModelLayer.prototype.addParamField = function (label, initialValue, setValue, type, min, max) { + var _this = this; + var input = document.createElement('paper-input'); + input.setAttribute('always-float-label', 'true'); + input.setAttribute('label', label); + input.setAttribute('value', '' + initialValue); + input.setAttribute('type', type); + if (type === 'number') { + input.setAttribute('min', '' + min); + input.setAttribute('max', '' + max); + } + input.className = 'param-input'; + this.paramContainer.appendChild(input); + input.addEventListener('input', function (event) { + if (type === 'number') { + setValue(event.target.valueAsNumber); + } + else { + setValue(event.target.value); + } + _this.modelBuilder.layerParamChanged(); + }); + setValue(initialValue); + }; + return ModelLayer; +}(exports.ModelLayerPolymer)); +exports.ModelLayer = ModelLayer; +document.registerElement(ModelLayer.prototype.is, ModelLayer); + +},{"../polymer-spec":11,"./layer_builder":4,"./model_builder_util":7}],7:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getDisplayShape(shape) { + return '[' + shape + ']'; +} +exports.getDisplayShape = getDisplayShape; + +},{}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Normalization; +(function (Normalization) { + Normalization[Normalization["NORMALIZATION_NEGATIVE_ONE_TO_ONE"] = 0] = "NORMALIZATION_NEGATIVE_ONE_TO_ONE"; + Normalization[Normalization["NORMALIZATION_ZERO_TO_ONE"] = 1] = "NORMALIZATION_ZERO_TO_ONE"; + Normalization[Normalization["NORMALIZATION_NONE"] = 2] = "NORMALIZATION_NONE"; +})(Normalization = exports.Normalization || (exports.Normalization = {})); +function generatePython(datasetName, normalizationStrategy, inputShape, modelLayers) { + var loadData = generateLoadData(datasetName, normalizationStrategy); + var buildModel = generateBuildModel(inputShape, modelLayers); + var captureWeights = generateCaptureWeights(modelLayers); + return [loadData, buildModel, captureWeights].join('\n\n'); +} +exports.generatePython = generatePython; +function generateLoadData(datasetName, normalizationStrategy) { + var loadFunction; + switch (datasetName) { + case 'CIFAR 10': { + loadFunction = 'cifar10'; + break; + } + case 'MNIST': { + loadFunction = 'mnist'; + break; + } + default: { + throw new Error('datasetName must be \'CIFAR 10\' or \'MNIST\''); + } + } + var normString; + switch (normalizationStrategy) { + case Normalization.NORMALIZATION_NEGATIVE_ONE_TO_ONE: { + normString = 'NORMALIZATION_NEGATIVE_ONE_TO_ONE'; + break; + } + case Normalization.NORMALIZATION_ZERO_TO_ONE: { + normString = 'NORMALIZATION_ZERO_TO_ONE'; + break; + } + case Normalization.NORMALIZATION_NONE: { + normString = 'NORMALIZATION_NONE'; + break; + } + default: { + throw new Error('invalid normalizationStrategy value'); + } + } + return "def load_data():\n return learnjs_colab.load_" + loadFunction + "(learnjs_colab." + normString + ")\n"; +} +function generateBuildModelLayer(layerIndex, inputShape, layer) { + var src = ''; + var W = 'W_' + layerIndex; + var b = 'b_' + layerIndex; + var outputShape = layer.getOutputShape(inputShape); + switch (layer.layerName) { + case 'Fully connected': { + var shape = [inputShape[0], outputShape].join(', '); + src = " " + W + " = tf.Variable(tf.truncated_normal([" + shape + "],\n stddev = 1.0 / math.sqrt(" + outputShape[0] + ")))\n " + b + " = tf.Variable(tf.truncated_normal([" + outputShape[0] + "], stddev = 0.1))\n layers.append({ 'x': layers[-1]['y'],\n 'W': " + W + ",\n 'b': " + b + ",\n 'y': tf.add(tf.matmul(layers[-1]['y'], " + W + "), " + b + ") })"; + break; + } + case 'ReLU': { + src = " layers.append({ 'x': layers[-1]['y'],\n 'y': tf.nn.relu(layers[-1]['y']) })"; + break; + } + case 'Convolution': { + var conv = layer; + var f = conv.fieldSize; + var d1 = inputShape[inputShape.length - 1]; + var d2 = outputShape[outputShape.length - 1]; + var wShape = '[' + f + ', ' + f + ', ' + d1 + ', ' + d2 + ']'; + var stride = '[1, ' + conv.stride + ', ' + conv.stride + ', 1]'; + src = " " + W + " = tf.Variable(tf.truncated_normal(" + wShape + ", stddev = 0.1))\n " + b + " = tf.Variable(tf.truncated_normal([" + d2 + "], stddev = 0.1))\n layers.append({ 'x': layers[-1]['y'],\n 'W': " + W + ",\n 'b': " + b + ",\n 'y': tf.add(tf.nn.conv2d(layers[-1]['y'],\n " + W + ",\n strides = " + stride + ",\n padding = 'SAME'), " + b + ") })"; + break; + } + case 'Max pool': { + var mp = layer; + var field = '[1, ' + mp.fieldSize + ', ' + mp.fieldSize + ', 1]'; + var stride = '[1, ' + mp.stride + ', ' + mp.stride + ', 1]'; + src = " layers.append({ 'x': layers[-1]['y'],\n 'y': tf.nn.max_pool(layers[-1]['y'],\n " + field + ",\n " + stride + ",\n padding = 'SAME') })"; + break; + } + case 'Reshape': { + break; + } + case 'Flatten': { + src = " layers.append({ 'x': layers[-1]['y'],\n 'y': tf.reshape(layers[-1]['y'], [-1, " + outputShape[0] + "]) })"; + break; + } + default: { + throw new Error('unknown layer type \'' + layer.layerName + '\''); + } + } + return src; +} +function generateBuildModel(inputShape, modelLayers) { + var inputShapeStr = inputShape.join(', '); + var sources = []; + sources.push("def build_model():\n layers = []\n\n layers.append({ 'y': tf.placeholder(tf.float32, [None, " + inputShapeStr + "]),\n 'y_label': tf.placeholder(tf.float32, [None, 10]) })"); + for (var i = 0; i < modelLayers.length; ++i) { + sources.push(generateBuildModelLayer(i + 1, inputShape, modelLayers[i])); + inputShape = modelLayers[i].getOutputShape(inputShape); + } + sources.push(' return layers\n'); + return sources.join('\n\n'); +} +function generateCaptureWeights(modelLayers) { + var sources = []; + sources.push("def capture_weights():\n weights = []"); + for (var i = 0; i < modelLayers.length; ++i) { + var layer = modelLayers[i]; + var index = i + 1; + var src = ''; + var W = '\'W\': model[' + index + '][\'W\']'; + var b = '\'b\': model[' + index + '][\'b\']'; + switch (layer.layerName) { + case 'Fully connected': { + src = " weights.append({ " + W + ".eval().flatten().tolist(),\n " + b + ".eval().flatten().tolist() })"; + break; + } + case 'Convolution': { + src = " weights.append({ " + W + ".eval().transpose().flatten().tolist(),\n " + b + ".eval().flatten().tolist() })"; + break; + } + default: { + src = ' weights.append({})'; + } + } + src += ' # ' + layer.layerName; + sources.push(src); + } + sources.push(' return weights'); + return sources.join('\n'); +} + +},{}],9:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var polymer_spec_1 = require("./polymer-spec"); +exports.NDArrayImageVisualizerPolymer = polymer_spec_1.PolymerElement({ is: 'ndarray-image-visualizer', properties: {} }); +var NDArrayImageVisualizer = (function (_super) { + __extends(NDArrayImageVisualizer, _super); + function NDArrayImageVisualizer() { + return _super !== null && _super.apply(this, arguments) || this; + } + NDArrayImageVisualizer.prototype.ready = function () { + this.canvas = this.querySelector('#canvas'); + this.canvas.width = 0; + this.canvas.height = 0; + this.canvasContext = + this.canvas.getContext('2d'); + this.canvas.style.display = 'none'; + }; + NDArrayImageVisualizer.prototype.setShape = function (shape) { + this.canvas.width = shape[1]; + this.canvas.height = shape[0]; + }; + NDArrayImageVisualizer.prototype.setSize = function (width, height) { + this.canvas.style.width = width + 'px'; + this.canvas.style.height = height + 'px'; + }; + NDArrayImageVisualizer.prototype.saveImageDataFromNDArray = function (ndarray) { + this.imageData = this.canvasContext.createImageData(this.canvas.width, this.canvas.height); + if (ndarray.shape[2] === 1) { + this.drawGrayscaleImageData(ndarray); + } + else if (ndarray.shape[2] === 3) { + this.drawRGBImageData(ndarray); + } + }; + NDArrayImageVisualizer.prototype.drawRGBImageData = function (ndarray) { + var pixelOffset = 0; + for (var i = 0; i < ndarray.shape[0]; i++) { + for (var j = 0; j < ndarray.shape[1]; j++) { + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 1); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 2); + this.imageData.data[pixelOffset++] = 255; + } + } + }; + NDArrayImageVisualizer.prototype.drawGrayscaleImageData = function (ndarray) { + var pixelOffset = 0; + for (var i = 0; i < ndarray.shape[0]; i++) { + for (var j = 0; j < ndarray.shape[1]; j++) { + var value = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = 255; + } + } + }; + NDArrayImageVisualizer.prototype.draw = function () { + this.canvas.style.display = ''; + this.canvasContext.putImageData(this.imageData, 0, 0); + }; + return NDArrayImageVisualizer; +}(exports.NDArrayImageVisualizerPolymer)); +exports.NDArrayImageVisualizer = NDArrayImageVisualizer; +document.registerElement(NDArrayImageVisualizer.prototype.is, NDArrayImageVisualizer); + +},{"./polymer-spec":11}],10:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var math_cpu_1 = require("../src/math/math_cpu"); +var polymer_spec_1 = require("./polymer-spec"); +var TOP_K = 3; +exports.NDArrayLogitsVisualizerPolymer = polymer_spec_1.PolymerElement({ is: 'ndarray-logits-visualizer', properties: {} }); +var NDArrayLogitsVisualizer = (function (_super) { + __extends(NDArrayLogitsVisualizer, _super); + function NDArrayLogitsVisualizer() { + return _super !== null && _super.apply(this, arguments) || this; + } + NDArrayLogitsVisualizer.prototype.initialize = function (width, height) { + this.width = width; + this.logitLabelElements = []; + this.logitVizElements = []; + var container = this.querySelector('.logits-container'); + container.style.height = height + 'px'; + for (var i = 0; i < TOP_K; i++) { + var logitContainer = document.createElement('div'); + logitContainer.style.height = height / (TOP_K + 1) + 'px'; + logitContainer.style.margin = + height / ((2 * TOP_K) * (TOP_K + 1)) + 'px 0'; + logitContainer.className = + 'single-logit-container ndarray-logits-visualizer'; + var logitLabelElement = document.createElement('div'); + logitLabelElement.className = 'logit-label ndarray-logits-visualizer'; + this.logitLabelElements.push(logitLabelElement); + var logitVizOuterElement = document.createElement('div'); + logitVizOuterElement.className = + 'logit-viz-outer ndarray-logits-visualizer'; + var logitVisInnerElement = document.createElement('div'); + logitVisInnerElement.className = + 'logit-viz-inner ndarray-logits-visualizer'; + logitVisInnerElement.innerHTML = ' '; + logitVizOuterElement.appendChild(logitVisInnerElement); + this.logitVizElements.push(logitVisInnerElement); + logitContainer.appendChild(logitLabelElement); + logitContainer.appendChild(logitVizOuterElement); + container.appendChild(logitContainer); + } + }; + NDArrayLogitsVisualizer.prototype.drawLogits = function (predictedLogits, labelLogits, labelClassNames) { + var mathCpu = new math_cpu_1.NDArrayMathCPU(); + var labelClass = mathCpu.argMax(labelLogits).get(); + var topk = mathCpu.topK(predictedLogits, TOP_K); + var topkIndices = topk.indices.getValues(); + var topkValues = topk.values.getValues(); + for (var i = 0; i < topkIndices.length; i++) { + var index = topkIndices[i]; + this.logitLabelElements[i].innerText = + labelClassNames ? labelClassNames[index] : index + ''; + this.logitLabelElements[i].style.width = + labelClassNames != null ? '100px' : '20px'; + this.logitVizElements[i].style.backgroundColor = index === labelClass ? + 'rgba(120, 185, 50, .84)' : + 'rgba(220, 10, 10, 0.84)'; + this.logitVizElements[i].style.width = + Math.floor(100 * topkValues[i]) + '%'; + this.logitVizElements[i].innerText = + (100 * topkValues[i]).toFixed(1) + "%"; + } + }; + return NDArrayLogitsVisualizer; +}(exports.NDArrayLogitsVisualizerPolymer)); +exports.NDArrayLogitsVisualizer = NDArrayLogitsVisualizer; +document.registerElement(NDArrayLogitsVisualizer.prototype.is, NDArrayLogitsVisualizer); + +},{"../src/math/math_cpu":28,"./polymer-spec":11}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function PolymerElement(spec) { + return Polymer.Class(spec); +} +exports.PolymerElement = PolymerElement; + +},{}],12:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var dataset_1 = require("../src/dataset"); +var ndarray_1 = require("../src/math/ndarray"); +var util = require("../src/util"); +var PARSING_IMAGE_CANVAS_HEIGHT_PX = 1000; +function getXhrDatasetConfig(jsonConfigPath) { + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', jsonConfigPath); + xhr.onload = function () { + resolve(JSON.parse(xhr.responseText)); + }; + xhr.onerror = function (error) { + reject(error); + }; + xhr.send(); + }); +} +exports.getXhrDatasetConfig = getXhrDatasetConfig; +var XhrDataset = (function (_super) { + __extends(XhrDataset, _super); + function XhrDataset(xhrDatasetConfig) { + var _this = _super.call(this, xhrDatasetConfig.data.map(function (x) { return x.shape; })) || this; + _this.xhrDatasetConfig = xhrDatasetConfig; + return _this; + } + XhrDataset.prototype.getNDArray = function (info) { + var dataPromise = info.dataType === 'png' ? + parseTypedArrayFromPng(info, info.shape) : + parseTypedArrayFromBinary(info); + return dataPromise.then(function (data) { + var inputSize = util.sizeFromShape(info.shape); + var ndarrays = []; + for (var i = 0; i < data.length / inputSize; i++) { + var values = data.subarray(i * inputSize, (i + 1) * inputSize); + var ndarray = ndarray_1.NDArray.make(info.shape, { values: new Float32Array(values) }); + ndarrays.push(ndarray); + } + return ndarrays; + }); + }; + XhrDataset.prototype.fetchData = function () { + var _this = this; + return new Promise(function (resolve, reject) { + var promises = _this.xhrDatasetConfig.data.map(function (x) { return _this.getNDArray(x); }); + Promise.all(promises).then(function (data) { + _this.dataset = data; + resolve(); + }); + }); + }; + return XhrDataset; +}(dataset_1.InMemoryDataset)); +exports.XhrDataset = XhrDataset; +function parseTypedArrayFromBinary(info) { + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', info.path); + xhr.responseType = 'arraybuffer'; + xhr.onload = function (event) { + var data = (info.dataType === 'float32') ? + new Float32Array(xhr.response) : + new Uint8Array(xhr.response); + resolve(data); + }; + xhr.onerror = function (err) { return reject(err); }; + xhr.send(); + }); +} +function parseGrayscaleImageData(data, result, resultOffset) { + var idx = resultOffset; + for (var i = 0; i < data.length; i += 4) { + result[idx++] = data[i]; + } +} +function parseRGBImageData(data, result, resultOffset) { + var idx = resultOffset; + for (var i = 0; i < data.length; i += 4) { + result[idx] = data[i]; + result[idx + 1] = data[i + 1]; + result[idx + 2] = data[i + 2]; + idx += 3; + } +} +function parseImage(img, shape) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + var N = img.height; + var inputSize = util.sizeFromShape(shape); + var result = new Uint8Array(N * inputSize); + if (img.width !== shape[0] * shape[1]) { + throw new Error("Image width (" + img.width + ") must be multiple of " + + ("rows*columns (" + shape[0] + "*" + shape[1] + ") of the ndarray")); + } + canvas.width = img.width; + canvas.height = PARSING_IMAGE_CANVAS_HEIGHT_PX; + var sx = 0; + var sWidth = canvas.width; + var sHeight = canvas.height; + var dx = 0; + var dy = 0; + var dWidth = sWidth; + var dHeight = sHeight; + var depth = shape[2]; + var offset = 0; + var numPasses = Math.ceil(N / canvas.height); + for (var pass = 0; pass < numPasses; ++pass) { + var sy = pass * canvas.height; + if ((pass === numPasses - 1) && (N % canvas.height > 0)) { + canvas.height = N % canvas.height; + sHeight = canvas.height; + dHeight = sHeight; + } + ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); + var data = ctx.getImageData(0, 0, canvas.width, canvas.height).data; + (depth === 1) ? parseGrayscaleImageData(data, result, offset) : + parseRGBImageData(data, result, offset); + offset += canvas.height * inputSize; + } + return result; +} +function parseTypedArrayFromPng(info, shape) { + return new Promise(function (resolve, reject) { + var img = new Image(); + img.setAttribute('crossOrigin', ''); + img.onload = function () { + var result = parseImage(img, shape); + img.src = ''; + img = null; + resolve(result); + }; + img.src = info.path; + }); +} + +},{"../src/dataset":14,"../src/math/ndarray":30,"../src/util":94}],13:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var MANIFEST_FILE = 'manifest.json'; +var CheckpointLoader = (function () { + function CheckpointLoader(urlPath) { + this.urlPath = urlPath; + if (this.urlPath.charAt(this.urlPath.length - 1) !== '/') { + this.urlPath += '/'; + } + } + CheckpointLoader.prototype.loadManifest = function () { + var _this = this; + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', _this.urlPath + MANIFEST_FILE); + xhr.onload = function () { + _this.checkpointManifest = JSON.parse(xhr.responseText); + resolve(); + }; + xhr.onerror = function (error) { + throw new Error(MANIFEST_FILE + " not found at " + _this.urlPath + ". " + error); + }; + xhr.send(); + }); + }; + CheckpointLoader.prototype.getCheckpointManifest = function () { + var _this = this; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + resolve(_this.checkpointManifest); + }); + }); + } + return new Promise(function (resolve, reject) { + resolve(_this.checkpointManifest); + }); + }; + CheckpointLoader.prototype.getAllVariables = function () { + var _this = this; + if (this.variables != null) { + return new Promise(function (resolve, reject) { + resolve(_this.variables); + }); + } + return new Promise(function (resolve, reject) { + _this.getCheckpointManifest().then(function (checkpointDefinition) { + var variableNames = Object.keys(_this.checkpointManifest); + var variablePromises = []; + for (var i = 0; i < variableNames.length; i++) { + variablePromises.push(_this.getVariable(variableNames[i])); + } + Promise.all(variablePromises).then(function (variables) { + _this.variables = {}; + for (var i = 0; i < variables.length; i++) { + _this.variables[variableNames[i]] = variables[i]; + } + resolve(_this.variables); + }); + }); + }); + }; + CheckpointLoader.prototype.getVariable = function (varName) { + var _this = this; + if (!(varName in this.checkpointManifest)) { + throw new Error('Cannot load non-existant variable ' + varName); + } + var variableRequestPromiseMethod = function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + var fname = _this.checkpointManifest[varName].filename; + xhr.open('GET', _this.urlPath + fname); + xhr.onload = function () { + var values = new Float32Array(xhr.response); + var ndarray = ndarray_1.NDArray.make(_this.checkpointManifest[varName].shape, { values: values }); + resolve(ndarray); + }; + xhr.onerror = function (error) { + throw new Error('Could not fetch variable ' + varName + ': ' + error); + }; + xhr.send(); + }; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + new Promise(variableRequestPromiseMethod).then(resolve); + }); + }); + } + return new Promise(variableRequestPromiseMethod); + }; + return CheckpointLoader; +}()); +exports.CheckpointLoader = CheckpointLoader; + +},{"./math/ndarray":30}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var STATS_SAMPLE_PERCENTAGE = 0.1; +var InMemoryDataset = (function () { + function InMemoryDataset(dataShapes) { + this.dataShapes = dataShapes; + this.normalizationInfo = {}; + } + InMemoryDataset.prototype.getDataShape = function (dataIndex) { + return this.dataShapes[dataIndex]; + }; + InMemoryDataset.prototype.getData = function () { + return this.dataset; + }; + InMemoryDataset.prototype.getStats = function () { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + return this.dataset.map(function (d) { return _this.getStatsForData(d); }); + }; + InMemoryDataset.prototype.getStatsForData = function (data) { + var inputMin = Number.POSITIVE_INFINITY; + var inputMax = Number.NEGATIVE_INFINITY; + var exampleIndices = data.map(function (example, i) { return i; }); + util.shuffle(exampleIndices); + exampleIndices = + exampleIndices.slice(exampleIndices.length * STATS_SAMPLE_PERCENTAGE); + for (var i = 0; i < exampleIndices.length; i++) { + var inputValues = data[exampleIndices[i]].getValues(); + for (var j = 0; j < inputValues.length; j++) { + inputMin = Math.min(inputMin, inputValues[j]); + inputMax = Math.max(inputMax, inputValues[j]); + } + } + return { + inputMin: inputMin, + inputMax: inputMax, + exampleCount: data.length, + shape: data[0].shape, + }; + }; + InMemoryDataset.prototype.normalizeExamplesToRange = function (examples, curLowerBounds, curUpperBounds, newLowerBounds, newUpperBounds) { + var curBoundsIsPerDimension = (curUpperBounds instanceof Float32Array && + curLowerBounds instanceof Float32Array); + var newBoundsIsPerDimension = (newLowerBounds instanceof Float32Array && + newUpperBounds instanceof Float32Array); + var inputSize = util.sizeFromShape(examples[0].shape); + var newExamples = []; + examples.forEach(function (example) { + var inputValues = example.getValues(); + var normalizedValues = new Float32Array(inputSize); + for (var j = 0; j < inputSize; j++) { + var curLowerBound = curBoundsIsPerDimension ? + curLowerBounds[j] : + curLowerBounds; + var curUpperBound = curBoundsIsPerDimension ? + curUpperBounds[j] : + curUpperBounds; + var curRange = curUpperBound - curLowerBound; + var newLowerBound = newBoundsIsPerDimension ? + newLowerBounds[j] : + newLowerBounds; + var newUpperBound = newBoundsIsPerDimension ? + newUpperBounds[j] : + newUpperBounds; + var newRange = newUpperBound - newLowerBound; + if (curRange === 0) { + normalizedValues[j] = newLowerBound; + } + else { + normalizedValues[j] = newLowerBound + + newRange * (inputValues[j] - curLowerBound) / curRange; + } + } + newExamples.push(ndarray_1.NDArray.make(example.shape, { values: normalizedValues })); + }); + return newExamples; + }; + InMemoryDataset.prototype.computeBounds = function (dataIndex) { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + var size = util.sizeFromShape(this.dataset[dataIndex][0].shape); + this.normalizationInfo[dataIndex] = { + isNormalized: false, + minValues: new Float32Array(size), + maxValues: new Float32Array(size) + }; + for (var i = 0; i < size; i++) { + this.normalizationInfo[dataIndex].minValues[i] = Number.POSITIVE_INFINITY; + this.normalizationInfo[dataIndex].maxValues[i] = Number.NEGATIVE_INFINITY; + } + this.dataset[dataIndex].forEach(function (example) { + var inputValues = example.getValues(); + for (var k = 0; k < size; k++) { + _this.normalizationInfo[dataIndex].minValues[k] = Math.min(_this.normalizationInfo[dataIndex].minValues[k], inputValues[k]); + _this.normalizationInfo[dataIndex].maxValues[k] = Math.max(_this.normalizationInfo[dataIndex].maxValues[k], inputValues[k]); + } + }); + }; + InMemoryDataset.prototype.normalizeWithinBounds = function (dataIndex, lowerBound, upperBound) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + if (dataIndex >= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":30,"./util":94}],15:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":16,"./math/concat3d_util":23,"./math/conv_util":24,"./math/ndarray":30,"./util":94}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":20}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":30,"./session":90}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":15,"./priority_queue":89}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":13,"./dataset":14,"./graph":15,"./graph_runner":17,"./initializers":20,"./input_provider":21,"./math/conv_util":24,"./math/math":27,"./math/math_cpu":28,"./math/math_gpu":29,"./math/ndarray":30,"./math/webgl/gpgpu_context":43,"./math/webgl/gpgpu_util":44,"./math/webgl/render_ndarray_gpu_util":56,"./math/webgl/webgl_util":66,"./optimizer":88,"./session":90,"./sgd_optimizer":92,"./util":94}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":30}],21:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":30,"./util":94}],22:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":30}],23:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":94}],24:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":94}],25:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":30}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":94,"./concat3d_util":23,"./copy2d_util":25,"./ndarray":30}],28:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":24,"../util":94,"./concat3d_util":23,"./copy2d_util":25,"./math":27,"./ndarray":30}],29:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":94,"./concat3d_util":23,"./conv_util":24,"./math":27,"./ndarray":30,"./webgl/addscaledmat_gpu":31,"./webgl/addsubmuldiv_gpu":32,"./webgl/argmaxequals_gpu":33,"./webgl/argminmax_gpu":34,"./webgl/avg_pool_gpu":35,"./webgl/batchnorm_gpu":36,"./webgl/concat3d_gpu":38,"./webgl/conv_backprop_gpu":39,"./webgl/conv_gpu":40,"./webgl/copy_gpu":41,"./webgl/exp_gpu":42,"./webgl/gpgpu_context":43,"./webgl/gpgpu_util":44,"./webgl/log_gpu":45,"./webgl/logsumexp_gpu":46,"./webgl/max_pool_backprop_gpu":47,"./webgl/max_pool_gpu":48,"./webgl/min_pool_gpu":49,"./webgl/minmax_gpu":50,"./webgl/mulmat_gpu":51,"./webgl/neg_gpu":52,"./webgl/pool_gpu":53,"./webgl/reducesum_gpu":54,"./webgl/relu_gpu":55,"./webgl/reshape_gpu":57,"./webgl/resize_bilinear_gpu":58,"./webgl/shader_compiler":59,"./webgl/sigmoid_gpu":60,"./webgl/step_gpu":61,"./webgl/texture_manager":63,"./webgl/trig_gpu":64,"./webgl/webgl_util":66}],30:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":94,"./webgl/webgl_util":66}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":43}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":27,"./binaryop_gpu":37}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":34}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":66}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":53}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":43}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":24}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":24,"./conv_gpu":40}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":24}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":65}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":44,"./tex_util":62,"./webgl_util":66}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":62,"./webgl_util":66}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":65}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":43}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":24}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":53}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":53}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":66}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":27,"./shader_compiler":59}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":65}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":24,"./webgl_util":66}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":43}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":65}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":66}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":94}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":24}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":94}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":65}],61:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":65}],62:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],63:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],64:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":65}],65:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":43}],66:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":94}],67:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":15,"./graph_util":18,"./ops/add":68,"./ops/argmax":69,"./ops/argmaxequals":70,"./ops/concat3d":71,"./ops/convolution":72,"./ops/divide":73,"./ops/element_wise_activation":74,"./ops/element_wise_cost":75,"./ops/exp":76,"./ops/linear_combination":77,"./ops/log":78,"./ops/matmul":79,"./ops/max_pool":80,"./ops/multiply":81,"./ops/reduce_sum":83,"./ops/reshape":84,"./ops/softmax":85,"./ops/split":86,"./ops/subtract":87}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":18,"../math/ndarray":30,"../util":94,"./op":82}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":82}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":82}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":23,"./op":82}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":24,"../util":94,"./op":82}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":18,"../util":94,"./op":82}],74:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":22,"./op":82}],75:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":18,"../math/cost_functions":26,"../math/ndarray":30,"../util":94,"./op":82}],76:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":18,"./op":82}],77:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":18,"./op":82}],78:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":18,"./op":82}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":18,"../math/math":27,"./op":82}],80:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":24,"../util":94,"./op":82}],81:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":18,"../util":94,"./op":82}],82:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],83:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":18,"../math/ndarray":30,"../util":94,"./op":82}],84:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":94,"./op":82}],85:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":15,"../math/ndarray":30,"../util":94,"./op":82}],86:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":18,"../util":94,"./op":82}],87:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":18,"../math/ndarray":30,"../util":94,"./op":82}],88:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],89:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],90:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":30,"./operation_emitter":67,"./session_util":91,"./tensor_array_map":93,"./util":94}],91:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":15,"./graph_util":18,"./math/ndarray":30,"./util":94}],92:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":30,"./optimizer":88,"./session_util":91,"./tensor_array_map":93}],93:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],94:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[5]) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJkZW1vcy9kZW1vLWZvb3Rlci50cyIsImRlbW9zL2RlbW8taGVhZGVyLnRzIiwiZGVtb3MvbGVhcm5qcy50cyIsImRlbW9zL21vZGVsLWJ1aWxkZXIvbGF5ZXJfYnVpbGRlci50cyIsImRlbW9zL21vZGVsLWJ1aWxkZXIvbW9kZWwtYnVpbGRlci50cyIsImRlbW9zL21vZGVsLWJ1aWxkZXIvbW9kZWwtbGF5ZXIudHMiLCJkZW1vcy9tb2RlbC1idWlsZGVyL21vZGVsX2J1aWxkZXJfdXRpbC50cyIsImRlbW9zL21vZGVsLWJ1aWxkZXIvdGVuc29yZmxvdy50cyIsImRlbW9zL25kYXJyYXktaW1hZ2UtdmlzdWFsaXplci50cyIsImRlbW9zL25kYXJyYXktbG9naXRzLXZpc3VhbGl6ZXIudHMiLCJkZW1vcy9wb2x5bWVyLXNwZWMudHMiLCJkZW1vcy94aHItZGF0YXNldC50cyIsInNyYy9jaGVja3BvaW50X2xvYWRlci50cyIsInNyYy9kYXRhc2V0LnRzIiwic3JjL2dyYXBoLnRzIiwic3JjL2dyYXBoX2xheWVycy50cyIsInNyYy9ncmFwaF9ydW5uZXIudHMiLCJzcmMvZ3JhcGhfdXRpbC50cyIsInNyYy9pbmRleC50cyIsInNyYy9pbml0aWFsaXplcnMudHMiLCJzcmMvaW5wdXRfcHJvdmlkZXIudHMiLCJzcmMvbWF0aC9hY3RpdmF0aW9uX2Z1bmN0aW9ucy50cyIsInNyYy9tYXRoL2NvbmNhdDNkX3V0aWwudHMiLCJzcmMvbWF0aC9jb252X3V0aWwudHMiLCJzcmMvbWF0aC9jb3B5MmRfdXRpbC50cyIsInNyYy9tYXRoL2Nvc3RfZnVuY3Rpb25zLnRzIiwic3JjL21hdGgvbWF0aC50cyIsInNyYy9tYXRoL21hdGhfY3B1LnRzIiwic3JjL21hdGgvbWF0aF9ncHUudHMiLCJzcmMvbWF0aC9uZGFycmF5LnRzIiwic3JjL21hdGgvd2ViZ2wvYWRkc2NhbGVkbWF0X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9hcmdtYXhlcXVhbHNfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYXJnbWlubWF4X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2F2Z19wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2JhdGNobm9ybV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9iaW5hcnlvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb25jYXQzZF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb252X2JhY2twcm9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2NvbnZfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvY29weV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9leHBfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvZ3BncHVfY29udGV4dC50cyIsInNyYy9tYXRoL3dlYmdsL2dwZ3B1X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC9sb2dfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbG9nc3VtZXhwX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21pbl9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21pbm1heF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9tdWxtYXRfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbmVnX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVkdWNlc3VtX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3JlbHVfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC9yZXNoYXBlX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3Jlc2l6ZV9iaWxpbmVhcl9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9zaGFkZXJfY29tcGlsZXIudHMiLCJzcmMvbWF0aC93ZWJnbC9zaWdtb2lkX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3N0ZXBfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvdGV4X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC90ZXh0dXJlX21hbmFnZXIudHMiLCJzcmMvbWF0aC93ZWJnbC90cmlnX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3VuYXJ5b3BfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvd2ViZ2xfdXRpbC50cyIsInNyYy9vcGVyYXRpb25fZW1pdHRlci50cyIsInNyYy9vcHMvYWRkLnRzIiwic3JjL29wcy9hcmdtYXgudHMiLCJzcmMvb3BzL2FyZ21heGVxdWFscy50cyIsInNyYy9vcHMvY29uY2F0M2QudHMiLCJzcmMvb3BzL2NvbnZvbHV0aW9uLnRzIiwic3JjL29wcy9kaXZpZGUudHMiLCJzcmMvb3BzL2VsZW1lbnRfd2lzZV9hY3RpdmF0aW9uLnRzIiwic3JjL29wcy9lbGVtZW50X3dpc2VfY29zdC50cyIsInNyYy9vcHMvZXhwLnRzIiwic3JjL29wcy9saW5lYXJfY29tYmluYXRpb24udHMiLCJzcmMvb3BzL2xvZy50cyIsInNyYy9vcHMvbWF0bXVsLnRzIiwic3JjL29wcy9tYXhfcG9vbC50cyIsInNyYy9vcHMvbXVsdGlwbHkudHMiLCJzcmMvb3BzL29wLnRzIiwic3JjL29wcy9yZWR1Y2Vfc3VtLnRzIiwic3JjL29wcy9yZXNoYXBlLnRzIiwic3JjL29wcy9zb2Z0bWF4LnRzIiwic3JjL29wcy9zcGxpdC50cyIsInNyYy9vcHMvc3VidHJhY3QudHMiLCJzcmMvb3B0aW1pemVyLnRzIiwic3JjL3ByaW9yaXR5X3F1ZXVlLnRzIiwic3JjL3Nlc3Npb24udHMiLCJzcmMvc2Vzc2lvbl91dGlsLnRzIiwic3JjL3NnZF9vcHRpbWl6ZXIudHMiLCJzcmMvdGVuc29yX2FycmF5X21hcC50cyIsInNyYy91dGlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FD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LElBQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDN0IsSUFBTSxRQUFRLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM3QixJQUFNLFFBQVEsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVCLElBQU0sUUFBUSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFNUIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztZQUNsQyxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFDOUQsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEdBQUcsT0FBTyxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDO1lBRXJFLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7Z0JBQ2xDLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEdBQUcsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDOUQsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEdBQUcsT0FBTyxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDO2dCQUVyRSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFVBQVUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO29CQUN2QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFdBQVcsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUV4QyxJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7d0JBQ2hCLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7NEJBQ3RDLElBQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLE9BQU8sQ0FBQzs0QkFDdEMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztnQ0FDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxNQUFNLEdBQUcsT0FBTyxDQUFDO2dDQUN0QyxPQUFPLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQzs0QkFDcEQsQ0FBQzt3QkFDSCxDQUFDO3dCQUNELEVBQUUsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO29CQUNsQyxDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsc0NBQWEsR0FBYixVQUFjLEVBQVc7UUFDdkIsSUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNoQyxJQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVCLElBQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUIsSUFBTSxNQUFNLEdBQUcsSUFBSSxZQUFZLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDN0MsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxXQUFXLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztZQUN4QyxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUM7WUFDWixHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO2dCQUNqQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO29CQUNqQyxHQUFHLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixDQUFDO1lBQ0gsQ0FBQztZQUNELE1BQU0sQ0FBQyxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUM7UUFDbkIsQ0FBQztRQUNELE1BQU0sQ0FBQyxpQkFBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBRVMsMENBQWlCLEdBQTNCLFVBQStDLENBQUksRUFBRSxNQUFnQjtRQUNuRSxJQUFNLFFBQVEsR0FBYSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0MsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDekMsUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkMsQ0FBQztRQUNELElBQU0sWUFBWSxHQUFHLElBQUksWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5QyxJQUFNLE1BQU0sR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDN0IsSUFBTSxNQUFNLEdBQUcsaUJBQU8sQ0FBQyxJQUFJLENBQUksUUFBUSxFQUFFLEVBQUMsTUFBTSxFQUFFLFlBQVksRUFBQyxDQUFDLENBQUM7UUFDakUsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDaEMsSUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUc1QixJQUFNLE1BQU0sR0FBYSxJQUFJLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDL0MsR0FBRyxDQUFDLENBQUMsSUFBSSxHQUFDLEdBQUcsQ0FBQyxFQUFFLEdBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLEdBQUMsRUFBRSxFQUFFLENBQUM7Z0JBQ3ZDLE1BQU0sQ0FBQyxHQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUMsQ0FBQyxDQUFDLENBQUM7WUFDN0IsQ0FBQztZQUVELElBQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0MsWUFBWSxDQUFDLFFBQVEsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNyQyxDQUFDO1FBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sNkJBQUksR0FBWixVQUNJLENBQVUsRUFBRSxLQUFhLEVBQUUsTUFBYyxFQUFFLEdBQVcsRUFDdEQsUUFBMkI7UUFDdkIsSUFBQSxZQUErQixFQUE5QixhQUFLLEVBQUUsYUFBSyxFQUFFLGFBQUssQ0FBWTtRQUN0QyxJQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsb0JBQW9CLENBQzlDLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUN0RCxJQUFNLENBQUMsR0FBRyxpQkFBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNyQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQy9CLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO2dCQUN2QyxJQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQztnQkFDbkMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ3BDLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssR0FBRyxRQUFRLENBQUMsQ0FBQztnQkFDaEQsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7b0JBQ3ZDLElBQU0sUUFBUSxHQUFHLEVBQUUsR0FBRyxNQUFNLEdBQUcsR0FBRyxDQUFDO29CQUNuQyxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztvQkFDcEMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsS0FBSyxHQUFHLFFBQVEsQ0FBQyxDQUFDO29CQUdoRCxJQUFJLFdBQVcsR0FDWCxDQUFDLFFBQVEsS0FBSyxLQUFLLEdBQUcsTUFBTSxDQUFDLGlCQUFpQjt3QkFDeEIsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7b0JBQ3BELElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztvQkFFakIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzt3QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFFBQVEsQ0FBQzt3QkFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzs0QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFFBQVEsQ0FBQzs0QkFDekIsSUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDOzRCQUMvQixFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dDQUNqQixXQUFXLEdBQUcsR0FBRyxDQUFDO2dDQUNsQixRQUFRLEdBQUcsR0FBRyxDQUFDO2dDQUNmLEtBQUssQ0FBQzs0QkFDUixDQUFDOzRCQUNELEVBQUUsQ0FBQyxDQUFDLENBQUMsUUFBUSxLQUFLLEtBQUssSUFBSSxLQUFLLEdBQUcsV0FBVyxDQUFDO2dDQUMzQyxDQUFDLFFBQVEsS0FBSyxLQUFLLElBQUksS0FBSyxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQ0FDaEQsV0FBVyxHQUFHLEtBQUssQ0FBQzs0QkFDdEIsQ0FBQzs0QkFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0NBQzlCLFFBQVEsSUFBSSxLQUFLLEdBQUcsQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLENBQUM7NEJBQ3RDLENBQUM7d0JBQ0gsQ0FBQzt3QkFDRCxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDOzRCQUN2QixLQUFLLENBQUM7d0JBQ1IsQ0FBQztvQkFDSCxDQUFDO29CQUNELENBQUMsQ0FBQyxHQUFHLENBQUMsUUFBUSxLQUFLLEtBQUssR0FBRyxRQUFRLEdBQUcsV0FBVyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hFLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRVMsd0NBQWUsR0FBekIsVUFDSSxDQUFVLEVBQUUsS0FBYSxFQUFFLE1BQWMsRUFBRSxHQUFXO1FBQ3hELE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQseUNBQWdCLEdBQWhCLFVBQWlCLENBQVUsRUFBRSxLQUFhLEVBQUUsTUFBYyxFQUFFLEdBQVc7UUFDL0QsSUFBQSxZQUErQixFQUE5QixhQUFLLEVBQUUsYUFBSyxFQUFFLGFBQUssQ0FBWTtRQUN0QyxJQUFNLFdBQVcsR0FDYixTQUFTLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUN2RSxJQUFNLFlBQVksR0FBRyxpQkFBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNoRCxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQy9CLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7Z0JBQzNDLElBQU0sUUFBUSxHQUFHLEVBQUUsR0FBRyxNQUFNLEdBQUcsR0FBRyxDQUFDO2dCQUNuQyxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDcEMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsS0FBSyxHQUFHLFFBQVEsQ0FBQyxDQUFDO2dCQUNoRCxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO29CQUMzQyxJQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQztvQkFDbkMsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7b0JBQ3BDLElBQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssR0FBRyxRQUFRLENBQUMsQ0FBQztvQkFDaEQsSUFBSSxRQUFRLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDO29CQUN4QyxJQUFJLFdBQVcsR0FBRyxDQUFDLENBQUMsQ0FBQztvQkFDckIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzt3QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFFBQVEsQ0FBQzt3QkFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzs0QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFFBQVEsQ0FBQzs0QkFDekIsSUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDOzRCQUMvQixFQUFFLENBQUMsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztnQ0FDckIsUUFBUSxHQUFHLEtBQUssQ0FBQztnQ0FDakIsV0FBVyxHQUFHLEVBQUUsR0FBRyxLQUFLLEdBQUcsRUFBRSxDQUFDOzRCQUNoQyxDQUFDO3dCQUNILENBQUM7b0JBQ0gsQ0FBQztvQkFDRCxZQUFZLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUMzQyxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFDRCxNQUFNLENBQUMsWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFUyxnREFBdUIsR0FBakMsVUFDSSxFQUFXLEVBQUUsQ0FBVSxFQUFFLEtBQWEsRUFBRSxVQUFrQixFQUMxRCxPQUFlO1FBQ2pCLElBQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUMxRSxJQUFNLEdBQUcsR0FBRyxLQUFLLEdBQUcsQ0FBQyxHQUFHLE9BQU8sQ0FBQztRQUMxQixJQUFBLGFBQWtDLEVBQWpDLGNBQU0sRUFBRSxjQUFNLEVBQUUsYUFBSyxDQUFhO1FBR3pDLElBQU0sYUFBYSxHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxHQUFHLFVBQVUsR0FBRyxDQUFDLENBQUM7UUFDcEQsSUFBTSxhQUFhLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEdBQUcsVUFBVSxHQUFHLENBQUMsQ0FBQztRQUVwRCxJQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsb0JBQW9CLENBQzlDLENBQUMsYUFBYSxFQUFFLGFBQWEsRUFBRSxLQUFLLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNqRSxJQUFNLEVBQUUsR0FBRyxpQkFBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUV0QyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQy9CLEdBQUcsQ0FBQyxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDO2dCQUMzQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQztvQkFFM0MsSUFBTSxTQUFTLEdBQUcsR0FBRyxHQUFHLEdBQUcsQ0FBQztvQkFDNUIsSUFBTSxTQUFTLEdBQUcsR0FBRyxHQUFHLEdBQUcsQ0FBQztvQkFDNUIsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO29CQUNoQixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUNsQyxJQUFNLEdBQUcsR0FBRyxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUMsR0FBRyxVQUFVLENBQUM7d0JBQzFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLElBQUksR0FBRyxJQUFJLE1BQU0sSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUM7NEJBQ3hELFFBQVEsQ0FBQzt3QkFDWCxDQUFDO3dCQUNELEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7NEJBQ2xDLElBQU0sR0FBRyxHQUFHLENBQUMsU0FBUyxHQUFHLEVBQUUsQ0FBQyxHQUFHLFVBQVUsQ0FBQzs0QkFDMUMsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsSUFBSSxHQUFHLElBQUksTUFBTSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFD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Q3JELEVBQUUsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNuQixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ25CLENBQUM7QUFQRCxrREFPQztBQUVELCtCQUFzQyxDQUFTO0lBQzdDLElBQU0sZUFBZSxHQUFHLElBQUksV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDM0IsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ3pCLE1BQU0sQ0FBQyxlQUFlLENBQUM7QUFDekIsQ0FBQztBQVBELHNEQU9DIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblBvbHltZXIoe2lzOiAnZGVtby1mb290ZXInfSk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5Qb2x5bWVyKHtpczogJ2RlbW8taGVhZGVyJ30pO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyBUaGlzIGZpbGUgaXMganVzdCBhbiBhbGlhcyB0aGF0IHBvaW50cyB0byB0aGUgY3VycmVudCBsZWFybmpzIHZlcnNpb25cbi8vIGF0IHRoaXMgYnJhbmNoLCBzbyBkZW1vcyBjYW4gaW1wb3J0IHRoZSBsaWJyYXJ5IGFzICcuLi9sZWFybmpzJy5cbmV4cG9ydCAqIGZyb20gJy4uL3NyYy9pbmRleCc7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgQXJyYXk0RCwgY29udl91dGlsLCBHcmFwaCwgSW5pdGlhbGl6ZXIsIE5EQXJyYXksIE5EQXJyYXlJbml0aWFsaXplciwgVGVuc29yLCB1dGlsLCBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciwgWmVyb3NJbml0aWFsaXplcn0gZnJvbSAnLi4vbGVhcm5qcyc7XG5cbi8qKlxuICogQ2xhc3NlcyB0aGF0IHNwZWNpZnkgb3BlcmF0aW9uIHBhcmFtZXRlcnMsIGhvdyB0aGV5IGFmZmVjdCBvdXRwdXQgc2hhcGUsXG4gKiBhbmQgbWV0aG9kcyBmb3IgYnVpbGRpbmcgdGhlIG9wZXJhdGlvbnMgdGhlbXNlbHZlcy4gQW55IG5ldyBvcHMgdG8gYmUgYWRkZWRcbiAqIHRvIHRoZSBtb2RlbCBidWlsZGVyIFVJIHNob3VsZCBiZSBhZGRlZCBoZXJlLlxuICovXG5cbmV4cG9ydCB0eXBlIExheWVyTmFtZSA9ICdGdWxseSBjb25uZWN0ZWQnIHwgJ1JlTFUnIHwgJ0NvbnZvbHV0aW9uJyB8XG4gICAgJ01heCBwb29sJyB8ICdSZXNoYXBlJyB8ICdGbGF0dGVuJztcblxuLyoqXG4gKiBDcmVhdGVzIGEgbGF5ZXIgYnVpbGRlciBvYmplY3QuXG4gKlxuICogQHBhcmFtIGxheWVyTmFtZSBUaGUgbmFtZSBvZiB0aGUgbGF5ZXIgdG8gYnVpbGQuXG4gKiBAcGFyYW0gbGF5ZXJCdWlsZGVySnNvbiBBbiBvcHRpb25hbCBMYXllckJ1aWxkZXIgSlNPTiBvYmplY3QuIFRoaXMgZG9lc24ndFxuICogICAgIGhhdmUgdGhlIHByb3RvdHlwZSBtZXRob2RzIG9uIHRoZW0gYXMgaXQgY29tZXMgZnJvbSBzZXJpYWxpemF0aW9uLiBUaGlzXG4gKiAgICAgbWV0aG9kIGNyZWF0ZXMgdGhlIG9iamVjdCB3aXRoIHRoZSBuZWNlc3NhcnkgcHJvdG90eXBlIG1ldGhvZHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRMYXllckJ1aWxkZXIoXG4gICAgbGF5ZXJOYW1lOiBMYXllck5hbWUsIGxheWVyQnVpbGRlckpzb24/OiBMYXllckJ1aWxkZXIpOiBMYXllckJ1aWxkZXIge1xuICBsZXQgbGF5ZXJCdWlsZGVyOiBMYXllckJ1aWxkZXI7XG4gIHN3aXRjaCAobGF5ZXJOYW1lKSB7XG4gICAgY2FzZSAnRnVsbHkgY29ubmVjdGVkJzpcbiAgICAgIGxheWVyQnVpbGRlciA9IG5ldyBGdWxseUNvbm5lY3RlZExheWVyQnVpbGRlcigpO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAnUmVMVSc6XG4gICAgICBsYXllckJ1aWxkZXIgPSBuZXcgUmVMVUxheWVyQnVpbGRlcigpO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSAnQ29udm9sdXRpb24nOlxuICAgICAgbGF5ZXJCdWlsZGVyID0gbmV3IENvbnZvbHV0aW9uMkRMYXllckJ1aWxkZXIoKTtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ01heCBwb29sJzpcbiAgICAgIGxheWVyQnVpbGRlciA9IG5ldyBNYXhQb29sTGF5ZXJCdWlsZGVyKCk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdSZXNoYXBlJzpcbiAgICAgIGxheWVyQnVpbGRlciA9IG5ldyBSZXNoYXBlTGF5ZXJCdWlsZGVyKCk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdGbGF0dGVuJzpcbiAgICAgIGxheWVyQnVpbGRlciA9IG5ldyBGbGF0dGVuTGF5ZXJCdWlsZGVyKCk7XG4gICAgICBicmVhaztcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdMYXllciBidWlsZGVyIGZvciAnICsgbGF5ZXJOYW1lICsgJyBub3QgZm91bmQuJyk7XG4gIH1cblxuICAvLyBGb3IgbGF5ZXIgYnVpbGRlcnMgcGFzc2VkIGFzIHNlcmlhbGl6ZWQgb2JqZWN0cywgd2UgY3JlYXRlIHRoZSBvYmplY3RzIGFuZFxuICAvLyBzZXQgdGhlIGZpZWxkcy5cbiAgaWYgKGxheWVyQnVpbGRlckpzb24gIT0gbnVsbCkge1xuICAgIGZvciAoY29uc3QgcHJvcCBpbiBsYXllckJ1aWxkZXJKc29uKSB7XG4gICAgICBpZiAobGF5ZXJCdWlsZGVySnNvbi5oYXNPd25Qcm9wZXJ0eShwcm9wKSkge1xuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIChsYXllckJ1aWxkZXIgYXMgYW55KVtwcm9wXSA9IChsYXllckJ1aWxkZXJKc29uIGFzIGFueSlbcHJvcF07XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBsYXllckJ1aWxkZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTGF5ZXJQYXJhbSB7XG4gIGxhYmVsOiBzdHJpbmc7XG4gIGluaXRpYWxWYWx1ZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcnxzdHJpbmc7XG4gIHR5cGU6ICdudW1iZXInfCd0ZXh0JztcbiAgbWluPzogbnVtYmVyO1xuICBtYXg/OiBudW1iZXI7XG4gIHNldFZhbHVlKHZhbHVlOiBudW1iZXJ8c3RyaW5nKTogdm9pZDtcbiAgZ2V0VmFsdWUoKTogbnVtYmVyfHN0cmluZztcbn1cblxuZXhwb3J0IHR5cGUgTGF5ZXJXZWlnaHRzRGljdCA9IHtcbiAgW25hbWU6IHN0cmluZ106IG51bWJlcltdXG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIExheWVyQnVpbGRlciB7XG4gIGxheWVyTmFtZTogTGF5ZXJOYW1lO1xuICBnZXRMYXllclBhcmFtcygpOiBMYXllclBhcmFtW107XG4gIGdldE91dHB1dFNoYXBlKGlucHV0U2hhcGU6IG51bWJlcltdKTogbnVtYmVyW107XG4gIGFkZExheWVyKFxuICAgICAgZzogR3JhcGgsIG5ldHdvcms6IFRlbnNvciwgaW5wdXRTaGFwZTogbnVtYmVyW10sIGluZGV4OiBudW1iZXIsXG4gICAgICB3ZWlnaHRzPzogTGF5ZXJXZWlnaHRzRGljdHxudWxsKTogVGVuc29yO1xuICAvLyBSZXR1cm4gbnVsbCBpZiBubyBlcnJvcnMsIG90aGVyd2lzZSByZXR1cm4gYW4gYXJyYXkgb2YgZXJyb3JzLlxuICB2YWxpZGF0ZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IHN0cmluZ1tdfG51bGw7XG59XG5cbmV4cG9ydCBjbGFzcyBGdWxseUNvbm5lY3RlZExheWVyQnVpbGRlciBpbXBsZW1lbnRzIExheWVyQnVpbGRlciB7XG4gIGxheWVyTmFtZTogTGF5ZXJOYW1lID0gJ0Z1bGx5IGNvbm5lY3RlZCc7XG4gIGhpZGRlblVuaXRzOiBudW1iZXI7XG5cbiAgZ2V0TGF5ZXJQYXJhbXMoKTogTGF5ZXJQYXJhbVtdIHtcbiAgICByZXR1cm4gW3tcbiAgICAgIGxhYmVsOiAnSGlkZGVuIHVuaXRzJyxcbiAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiAxMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbWluOiAxLFxuICAgICAgbWF4OiAxMDAwLFxuICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogbnVtYmVyKSA9PiB0aGlzLmhpZGRlblVuaXRzID0gdmFsdWUsXG4gICAgICBnZXRWYWx1ZTogKCkgPT4gdGhpcy5oaWRkZW5Vbml0c1xuICAgIH1dO1xuICB9XG5cbiAgZ2V0T3V0cHV0U2hhcGUoaW5wdXRTaGFwZTogbnVtYmVyW10pOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIFt0aGlzLmhpZGRlblVuaXRzXTtcbiAgfVxuXG4gIGFkZExheWVyKFxuICAgICAgZzogR3JhcGgsIG5ldHdvcms6IFRlbnNvciwgaW5wdXRTaGFwZTogbnVtYmVyW10sIGluZGV4OiBudW1iZXIsXG4gICAgICB3ZWlnaHRzOiBMYXllcldlaWdodHNEaWN0fG51bGwpOiBUZW5zb3Ige1xuICAgIGNvbnN0IGlucHV0U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShpbnB1dFNoYXBlKTtcbiAgICBjb25zdCB3U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbdGhpcy5oaWRkZW5Vbml0cywgaW5wdXRTaXplXTtcblxuICAgIGxldCB3ZWlnaHRzSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyO1xuICAgIGxldCBiaWFzSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyO1xuICAgIGlmICh3ZWlnaHRzICE9IG51bGwpIHtcbiAgICAgIHdlaWdodHNJbml0aWFsaXplciA9XG4gICAgICAgICAgbmV3IE5EQXJyYXlJbml0aWFsaXplcihBcnJheTJELm5ldyh3U2hhcGUsIHdlaWdodHNbJ1cnXSkpO1xuICAgICAgYmlhc0luaXRpYWxpemVyID0gbmV3IE5EQXJyYXlJbml0aWFsaXplcihBcnJheTFELm5ldyh3ZWlnaHRzWydiJ10pKTtcbiAgICB9IGVsc2Uge1xuICAgICAgd2VpZ2h0c0luaXRpYWxpemVyID0gbmV3IFZhcmlhbmNlU2NhbGluZ0luaXRpYWxpemVyKCk7XG4gICAgICBiaWFzSW5pdGlhbGl6ZXIgPSBuZXcgWmVyb3NJbml0aWFsaXplcigpO1xuICAgIH1cblxuICAgIGNvbnN0IHVzZUJpYXMgPSB0cnVlO1xuICAgIHJldHVybiBnLmxheWVycy5kZW5zZShcbiAgICAgICAgJ2ZjMScsIG5ldHdvcmssIHRoaXMuaGlkZGVuVW5pdHMsIG51bGwsIHVzZUJpYXMsIHdlaWdodHNJbml0aWFsaXplcixcbiAgICAgICAgYmlhc0luaXRpYWxpemVyKTtcbiAgfVxuXG4gIHZhbGlkYXRlKGlucHV0U2hhcGU6IG51bWJlcltdKSB7XG4gICAgaWYgKGlucHV0U2hhcGUubGVuZ3RoICE9PSAxKSB7XG4gICAgICByZXR1cm4gWydJbnB1dCBzaGFwZSBtdXN0IGJlIGEgQXJyYXkxRC4nXTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFJlTFVMYXllckJ1aWxkZXIgaW1wbGVtZW50cyBMYXllckJ1aWxkZXIge1xuICBsYXllck5hbWU6IExheWVyTmFtZSA9ICdSZUxVJztcbiAgZ2V0TGF5ZXJQYXJhbXMoKTogTGF5ZXJQYXJhbVtdIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICByZXR1cm4gaW5wdXRTaGFwZTtcbiAgfVxuXG4gIGFkZExheWVyKFxuICAgICAgZzogR3JhcGgsIG5ldHdvcms6IFRlbnNvciwgaW5wdXRTaGFwZTogbnVtYmVyW10sIGluZGV4OiBudW1iZXIsXG4gICAgICB3ZWlnaHRzOiBMYXllcldlaWdodHNEaWN0fG51bGwpOiBUZW5zb3Ige1xuICAgIHJldHVybiBnLnJlbHUobmV0d29yayk7XG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IHN0cmluZ1tdfG51bGwge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDb252b2x1dGlvbjJETGF5ZXJCdWlsZGVyIGltcGxlbWVudHMgTGF5ZXJCdWlsZGVyIHtcbiAgbGF5ZXJOYW1lOiBMYXllck5hbWUgPSAnQ29udm9sdXRpb24nO1xuICBmaWVsZFNpemU6IG51bWJlcjtcbiAgc3RyaWRlOiBudW1iZXI7XG4gIHplcm9QYWQ6IG51bWJlcjtcbiAgb3V0cHV0RGVwdGg6IG51bWJlcjtcblxuICBnZXRMYXllclBhcmFtcygpOiBMYXllclBhcmFtW10ge1xuICAgIHJldHVybiBbXG4gICAgICB7XG4gICAgICAgIGxhYmVsOiAnRmllbGQgc2l6ZScsXG4gICAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiAzLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgbWluOiAxLFxuICAgICAgICBtYXg6IDEwMCxcbiAgICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogbnVtYmVyKSA9PiB0aGlzLmZpZWxkU2l6ZSA9IHZhbHVlLFxuICAgICAgICBnZXRWYWx1ZTogKCkgPT4gdGhpcy5maWVsZFNpemVcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGxhYmVsOiAnU3RyaWRlJyxcbiAgICAgICAgaW5pdGlhbFZhbHVlOiAoaW5wdXRTaGFwZTogbnVtYmVyW10pID0+IDEsXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICBtaW46IDEsXG4gICAgICAgIG1heDogMTAwLFxuICAgICAgICBzZXRWYWx1ZTogKHZhbHVlOiBudW1iZXIpID0+IHRoaXMuc3RyaWRlID0gdmFsdWUsXG4gICAgICAgIGdldFZhbHVlOiAoKSA9PiB0aGlzLnN0cmlkZVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdaZXJvIHBhZCcsXG4gICAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiAwLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEwMCxcbiAgICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogbnVtYmVyKSA9PiB0aGlzLnplcm9QYWQgPSB2YWx1ZSxcbiAgICAgICAgZ2V0VmFsdWU6ICgpID0+IHRoaXMuemVyb1BhZFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdPdXRwdXQgZGVwdGgnLFxuICAgICAgICBpbml0aWFsVmFsdWU6IChpbnB1dFNoYXBlOiBudW1iZXJbXSkgPT5cbiAgICAgICAgICAgIHRoaXMub3V0cHV0RGVwdGggIT0gbnVsbCA/IHRoaXMub3V0cHV0RGVwdGggOiAxLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgbWluOiAxLFxuICAgICAgICBtYXg6IDEwMDAsXG4gICAgICAgIHNldFZhbHVlOiAodmFsdWU6IG51bWJlcikgPT4gdGhpcy5vdXRwdXREZXB0aCA9IHZhbHVlLFxuICAgICAgICBnZXRWYWx1ZTogKCkgPT4gdGhpcy5vdXRwdXREZXB0aFxuICAgICAgfVxuICAgIF07XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICByZXR1cm4gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBpbnB1dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgdGhpcy5maWVsZFNpemUsXG4gICAgICAgIHRoaXMub3V0cHV0RGVwdGgsIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpO1xuICB9XG5cbiAgYWRkTGF5ZXIoXG4gICAgICBnOiBHcmFwaCwgbmV0d29yazogVGVuc29yLCBpbnB1dFNoYXBlOiBudW1iZXJbXSwgaW5kZXg6IG51bWJlcixcbiAgICAgIHdlaWdodHM6IExheWVyV2VpZ2h0c0RpY3R8bnVsbCk6IFRlbnNvciB7XG4gICAgY29uc3QgaW5wdXRTaGFwZTNkID0gaW5wdXRTaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG4gICAgY29uc3Qgd1NoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIFt0aGlzLmZpZWxkU2l6ZSwgdGhpcy5maWVsZFNpemUsIGlucHV0U2hhcGVbMl0sIHRoaXMub3V0cHV0RGVwdGhdO1xuICAgIGxldCB3OiBBcnJheTREO1xuICAgIGxldCBiOiBBcnJheTFEO1xuICAgIGlmICh3ZWlnaHRzICE9IG51bGwpIHtcbiAgICAgIHcgPSBBcnJheTRELm5ldyh3U2hhcGUsIHdlaWdodHNbJ1cnXSk7XG4gICAgICBiID0gQXJyYXkxRC5uZXcod2VpZ2h0c1snYiddKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdyA9IE5EQXJyYXkucmFuZFRydW5jYXRlZE5vcm1hbDxBcnJheTREPih3U2hhcGUsIDAsIDAuMSk7XG4gICAgICBiID0gQXJyYXkxRC56ZXJvcyhbdGhpcy5vdXRwdXREZXB0aF0pO1xuICAgIH1cbiAgICBjb25zdCB3VGVuc29yID0gZy52YXJpYWJsZSgnY29udjJkLScgKyBpbmRleCArICctdycsIHcpO1xuICAgIGNvbnN0IGJUZW5zb3IgPSBnLnZhcmlhYmxlKCdjb252MmQtJyArIGluZGV4ICsgJy1iJywgYik7XG4gICAgcmV0dXJuIGcuY29udjJkKFxuICAgICAgICBuZXR3b3JrLCB3VGVuc29yLCBiVGVuc29yLCB0aGlzLmZpZWxkU2l6ZSwgdGhpcy5vdXRwdXREZXB0aCxcbiAgICAgICAgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCk7XG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dFNoYXBlOiBudW1iZXJbXSkge1xuICAgIGlmIChpbnB1dFNoYXBlLmxlbmd0aCAhPT0gMykge1xuICAgICAgcmV0dXJuIFsnSW5wdXQgc2hhcGUgbXVzdCBiZSBhIEFycmF5M0QuJ107XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBNYXhQb29sTGF5ZXJCdWlsZGVyIGltcGxlbWVudHMgTGF5ZXJCdWlsZGVyIHtcbiAgbGF5ZXJOYW1lOiBMYXllck5hbWUgPSAnTWF4IHBvb2wnO1xuICBmaWVsZFNpemU6IG51bWJlcjtcbiAgc3RyaWRlOiBudW1iZXI7XG4gIHplcm9QYWQ6IG51bWJlcjtcblxuICBnZXRMYXllclBhcmFtcygpOiBMYXllclBhcmFtW10ge1xuICAgIHJldHVybiBbXG4gICAgICB7XG4gICAgICAgIGxhYmVsOiAnRmllbGQgc2l6ZScsXG4gICAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiAzLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgbWluOiAxLFxuICAgICAgICBtYXg6IDEwMCxcbiAgICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogbnVtYmVyKSA9PiB0aGlzLmZpZWxkU2l6ZSA9IHZhbHVlLFxuICAgICAgICBnZXRWYWx1ZTogKCkgPT4gdGhpcy5maWVsZFNpemVcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGxhYmVsOiAnU3RyaWRlJyxcbiAgICAgICAgaW5pdGlhbFZhbHVlOiAoaW5wdXRTaGFwZTogbnVtYmVyW10pID0+IDEsXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICBtaW46IDEsXG4gICAgICAgIG1heDogMTAwLFxuICAgICAgICBzZXRWYWx1ZTogKHZhbHVlOiBudW1iZXIpID0+IHRoaXMuc3RyaWRlID0gdmFsdWUsXG4gICAgICAgIGdldFZhbHVlOiAoKSA9PiB0aGlzLnN0cmlkZVxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdaZXJvIHBhZCcsXG4gICAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiAwLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgbWluOiAwLFxuICAgICAgICBtYXg6IDEwMCxcbiAgICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogbnVtYmVyKSA9PiB0aGlzLnplcm9QYWQgPSB2YWx1ZSxcbiAgICAgICAgZ2V0VmFsdWU6ICgpID0+IHRoaXMuemVyb1BhZFxuICAgICAgfVxuICAgIF07XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICByZXR1cm4gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBpbnB1dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgdGhpcy5maWVsZFNpemUsIGlucHV0U2hhcGVbMl0sXG4gICAgICAgIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpO1xuICB9XG5cbiAgYWRkTGF5ZXIoXG4gICAgICBnOiBHcmFwaCwgbmV0d29yazogVGVuc29yLCBpbnB1dFNoYXBlOiBudW1iZXJbXSwgaW5kZXg6IG51bWJlcixcbiAgICAgIHdlaWdodHM6IExheWVyV2VpZ2h0c0RpY3R8bnVsbCk6IFRlbnNvciB7XG4gICAgcmV0dXJuIGcubWF4UG9vbChuZXR3b3JrLCB0aGlzLmZpZWxkU2l6ZSwgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCk7XG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dFNoYXBlOiBudW1iZXJbXSkge1xuICAgIGlmIChpbnB1dFNoYXBlLmxlbmd0aCAhPT0gMykge1xuICAgICAgcmV0dXJuIFsnSW5wdXQgc2hhcGUgbXVzdCBiZSBhIEFycmF5M0QuJ107XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSZXNoYXBlTGF5ZXJCdWlsZGVyIGltcGxlbWVudHMgTGF5ZXJCdWlsZGVyIHtcbiAgbGF5ZXJOYW1lOiBMYXllck5hbWUgPSAnUmVzaGFwZSc7XG4gIG91dHB1dFNoYXBlOiBudW1iZXJbXTtcbiAgZ2V0TGF5ZXJQYXJhbXMoKSB7XG4gICAgcmV0dXJuIFt7XG4gICAgICBsYWJlbDogJ1NoYXBlIChjb21tYSBzZXBhcmF0ZWQpJyxcbiAgICAgIGluaXRpYWxWYWx1ZTogKGlucHV0U2hhcGU6IG51bWJlcltdKSA9PiBpbnB1dFNoYXBlLmpvaW4oJywgJyksXG4gICAgICB0eXBlOiAndGV4dCcgYXMgJ3RleHQnLFxuICAgICAgc2V0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB0aGlzLm91dHB1dFNoYXBlID1cbiAgICAgICAgICB2YWx1ZS5zcGxpdCgnLCcpLm1hcCgodmFsdWUpID0+ICt2YWx1ZSksXG4gICAgICBnZXRWYWx1ZTogKCkgPT4gdGhpcy5vdXRwdXRTaGFwZS5qb2luKCcsICcpXG4gICAgfV07XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICByZXR1cm4gdGhpcy5vdXRwdXRTaGFwZTtcbiAgfVxuXG4gIGFkZExheWVyKFxuICAgICAgZzogR3JhcGgsIG5ldHdvcms6IFRlbnNvciwgaW5wdXRTaGFwZTogbnVtYmVyW10sIGluZGV4OiBudW1iZXIsXG4gICAgICB3ZWlnaHRzOiBMYXllcldlaWdodHNEaWN0fG51bGwpOiBUZW5zb3Ige1xuICAgIHJldHVybiBnLnJlc2hhcGUobmV0d29yaywgdGhpcy5vdXRwdXRTaGFwZSk7XG4gIH1cblxuICB2YWxpZGF0ZShpbnB1dFNoYXBlOiBudW1iZXJbXSkge1xuICAgIGNvbnN0IGlucHV0U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShpbnB1dFNoYXBlKTtcbiAgICBjb25zdCBvdXRwdXRTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMub3V0cHV0U2hhcGUpO1xuICAgIGlmIChpbnB1dFNpemUgIT09IG91dHB1dFNpemUpIHtcbiAgICAgIHJldHVybiBbXG4gICAgICAgIGBJbnB1dCBzaXplICgke2lucHV0U2l6ZX0pIG11c3QgbWF0Y2ggb3V0cHV0IHNpemUgKCR7b3V0cHV0U2l6ZX0pLmBcbiAgICAgIF07XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBGbGF0dGVuTGF5ZXJCdWlsZGVyIGltcGxlbWVudHMgTGF5ZXJCdWlsZGVyIHtcbiAgbGF5ZXJOYW1lOiBMYXllck5hbWUgPSAnRmxhdHRlbic7XG5cbiAgZ2V0TGF5ZXJQYXJhbXMoKTogTGF5ZXJQYXJhbVtdIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZShpbnB1dFNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICByZXR1cm4gW3V0aWwuc2l6ZUZyb21TaGFwZShpbnB1dFNoYXBlKV07XG4gIH1cblxuICBhZGRMYXllcihcbiAgICAgIGc6IEdyYXBoLCBuZXR3b3JrOiBUZW5zb3IsIGlucHV0U2hhcGU6IG51bWJlcltdLCBpbmRleDogbnVtYmVyLFxuICAgICAgd2VpZ2h0czogTGF5ZXJXZWlnaHRzRGljdHxudWxsKTogVGVuc29yIHtcbiAgICByZXR1cm4gZy5yZXNoYXBlKG5ldHdvcmssIHRoaXMuZ2V0T3V0cHV0U2hhcGUoaW5wdXRTaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoaW5wdXRTaGFwZTogbnVtYmVyW10pOiBzdHJpbmdbXXxudWxsIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tdW51c2VkLXZhcmlhYmxlXG5pbXBvcnQgJy4uL25kYXJyYXktaW1hZ2UtdmlzdWFsaXplcic7XG5pbXBvcnQgJy4uL25kYXJyYXktbG9naXRzLXZpc3VhbGl6ZXInO1xuaW1wb3J0ICcuL21vZGVsLWxheWVyJztcbmltcG9ydCAnLi4vZGVtby1oZWFkZXInO1xuaW1wb3J0ICcuLi9kZW1vLWZvb3Rlcic7XG5cbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkzRCwgRGF0YVN0YXRzLCBGZWVkRW50cnksIEdyYXBoLCBHcmFwaFJ1bm5lciwgR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyLCBJbkNQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIsIEluTWVtb3J5RGF0YXNldCwgTWV0cmljUmVkdWN0aW9uLCBOREFycmF5LCBOREFycmF5TWF0aCwgTkRBcnJheU1hdGhDUFUsIE5EQXJyYXlNYXRoR1BVLCBPcHRpbWl6ZXIsIFNjYWxhciwgU2Vzc2lvbiwgU0dET3B0aW1pemVyLCBUZW5zb3IsIHV0aWx9IGZyb20gJy4uL2xlYXJuanMnO1xuaW1wb3J0IHtOREFycmF5SW1hZ2VWaXN1YWxpemVyfSBmcm9tICcuLi9uZGFycmF5LWltYWdlLXZpc3VhbGl6ZXInO1xuaW1wb3J0IHtOREFycmF5TG9naXRzVmlzdWFsaXplcn0gZnJvbSAnLi4vbmRhcnJheS1sb2dpdHMtdmlzdWFsaXplcic7XG5pbXBvcnQge1BvbHltZXJFbGVtZW50LCBQb2x5bWVySFRNTEVsZW1lbnR9IGZyb20gJy4uL3BvbHltZXItc3BlYyc7XG5pbXBvcnQgKiBhcyB4aHJfZGF0YXNldCBmcm9tICcuLi94aHItZGF0YXNldCc7XG5pbXBvcnQge1hockRhdGFzZXQsIFhockRhdGFzZXRDb25maWd9IGZyb20gJy4uL3hoci1kYXRhc2V0JztcblxuaW1wb3J0IHtMYXllckJ1aWxkZXIsIExheWVyV2VpZ2h0c0RpY3R9IGZyb20gJy4vbGF5ZXJfYnVpbGRlcic7XG5pbXBvcnQge01vZGVsTGF5ZXJ9IGZyb20gJy4vbW9kZWwtbGF5ZXInO1xuaW1wb3J0ICogYXMgbW9kZWxfYnVpbGRlcl91dGlsIGZyb20gJy4vbW9kZWxfYnVpbGRlcl91dGlsJztcbmltcG9ydCB7Tm9ybWFsaXphdGlvbn0gZnJvbSAnLi90ZW5zb3JmbG93JztcblxuY29uc3QgREFUQVNFVFNfQ09ORklHX0pTT04gPSAnbW9kZWwtYnVpbGRlci1kYXRhc2V0cy1jb25maWcuanNvbic7XG5cbi8vIFRPRE8obnN0aG9yYXQpOiBNYWtlIHRoZXNlIHBhcmFtZXRlcnMgaW4gdGhlIFVJLlxuY29uc3QgQkFUQ0hfU0laRSA9IDY0O1xuY29uc3QgTEVBUk5JTkdfUkFURSA9IDAuMTtcbi8qKiBIb3cgb2Z0ZW4gdG8gZXZhbHVhdGUgdGhlIG1vZGVsIGFnYWluc3QgdGVzdCBkYXRhLiAqL1xuY29uc3QgRVZBTF9JTlRFUlZBTF9NUyA9IDE1MDA7XG4vKiogSG93IG9mdGVuIHRvIGNvbXB1dGUgdGhlIGNvc3QuIERvd25sb2FkaW5nIHRoZSBjb3N0IHN0YWxscyB0aGUgR1BVLiAqL1xuY29uc3QgQ09TVF9JTlRFUlZBTF9NUyA9IDUwMDtcbi8qKiBIb3cgbWFueSBpbmZlcmVuY2UgZXhhbXBsZXMgdG8gc2hvdyB3aGVuIGV2YWx1YXRpbmcgYWNjdXJhY3kuICovXG5jb25zdCBJTkZFUkVOQ0VfRVhBTVBMRV9DT1VOVCA9IDE1O1xuY29uc3QgSU5GRVJFTkNFX0lNQUdFX1NJWkVfUFggPSAxMDA7XG4vKipcbiAqIEhvdyBvZnRlbiB0byBzaG93IGluZmVyZW5jZSBleGFtcGxlcy4gVGhpcyBzaG91bGQgYmUgbGVzcyBvZnRlbiB0aGFuXG4gKiBFVkFMX0lOVEVSVkFMX01TIGFzIHdlIG9ubHkgc2hvdyBpbmZlcmVuY2UgZXhhbXBsZXMgZHVyaW5nIGFuIGV2YWwuXG4gKi9cbmNvbnN0IElORkVSRU5DRV9FWEFNUExFX0lOVEVSVkFMX01TID0gMzAwMDtcblxuLy8gU21vb3RoaW5nIGZhY3RvciBmb3IgdGhlIGV4YW1wbGVzL3Mgc3RhbmRhbG9uZSB0ZXh0IHN0YXRpc3RpYy5cbmNvbnN0IEVYQU1QTEVfU0VDX1NUQVRfU01PT1RISU5HX0ZBQ1RPUiA9IC43O1xuXG5jb25zdCBUUkFJTl9URVNUX1JBVElPID0gNSAvIDY7XG5cbmNvbnN0IElNQUdFX0RBVEFfSU5ERVggPSAwO1xuY29uc3QgTEFCRUxfREFUQV9JTkRFWCA9IDE7XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTp2YXJpYWJsZS1uYW1lXG5leHBvcnQgbGV0IE1vZGVsQnVpbGRlclBvbHltZXIgPSBQb2x5bWVyRWxlbWVudCh7XG4gIGlzOiAnbW9kZWwtYnVpbGRlcicsXG4gIHByb3BlcnRpZXM6IHtcbiAgICBpbnB1dFNoYXBlRGlzcGxheTogU3RyaW5nLFxuICAgIGlzVmFsaWQ6IEJvb2xlYW4sXG4gICAgaW5mZXJlbmNlc1BlclNlYzogTnVtYmVyLFxuICAgIGluZmVyZW5jZUR1cmF0aW9uOiBOdW1iZXIsXG4gICAgZXhhbXBsZXNUcmFpbmVkOiBOdW1iZXIsXG4gICAgZXhhbXBsZXNQZXJTZWM6IE51bWJlcixcbiAgICB0b3RhbFRpbWVTZWM6IFN0cmluZyxcbiAgICBhcHBsaWNhdGlvblN0YXRlOiBOdW1iZXIsXG4gICAgbW9kZWxJbml0aWFsaXplZDogQm9vbGVhbixcbiAgICBzaG93VHJhaW5TdGF0czogQm9vbGVhbixcbiAgICBkYXRhc2V0RG93bmxvYWRlZDogQm9vbGVhbixcbiAgICBkYXRhc2V0TmFtZXM6IEFycmF5LFxuICAgIHNlbGVjdGVkRGF0YXNldE5hbWU6IFN0cmluZyxcbiAgICBtb2RlbE5hbWVzOiBBcnJheSxcbiAgICBzZWxlY3RlZE1vZGVsTmFtZTogU3RyaW5nLFxuICAgIHNlbGVjdGVkTm9ybWFsaXphdGlvbk9wdGlvbjpcbiAgICAgICAge3R5cGU6IE51bWJlciwgdmFsdWU6IE5vcm1hbGl6YXRpb24uTk9STUFMSVpBVElPTl9ORUdBVElWRV9PTkVfVE9fT05FfSxcbiAgICAvLyBTdGF0c1xuICAgIHNob3dEYXRhc2V0U3RhdHM6IEJvb2xlYW4sXG4gICAgc3RhdHNJbnB1dE1pbjogTnVtYmVyLFxuICAgIHN0YXRzSW5wdXRNYXg6IE51bWJlcixcbiAgICBzdGF0c0lucHV0U2hhcGVEaXNwbGF5OiBTdHJpbmcsXG4gICAgc3RhdHNMYWJlbFNoYXBlRGlzcGxheTogU3RyaW5nLFxuICAgIHN0YXRzRXhhbXBsZUNvdW50OiBOdW1iZXIsXG4gIH1cbn0pO1xuXG5leHBvcnQgZW51bSBBcHBsaWNhdGlvblN0YXRlIHtcbiAgSURMRSA9IDEsXG4gIFRSQUlOSU5HID0gMlxufVxuXG5leHBvcnQgY2xhc3MgTW9kZWxCdWlsZGVyIGV4dGVuZHMgTW9kZWxCdWlsZGVyUG9seW1lciB7XG4gIC8vIFBvbHltZXIgcHJvcGVydGllcy5cbiAgcHJpdmF0ZSBpc1ZhbGlkOiBib29sZWFuO1xuICBwcml2YXRlIHRvdGFsVGltZVNlYzogc3RyaW5nO1xuICBwcml2YXRlIGFwcGxpY2F0aW9uU3RhdGU6IEFwcGxpY2F0aW9uU3RhdGU7XG4gIHByaXZhdGUgbW9kZWxJbml0aWFsaXplZDogYm9vbGVhbjtcbiAgcHJpdmF0ZSBzaG93VHJhaW5TdGF0czogYm9vbGVhbjtcbiAgcHJpdmF0ZSBzZWxlY3RlZE5vcm1hbGl6YXRpb25PcHRpb246IG51bWJlcjtcblxuICAvLyBEYXRhc2V0cyBhbmQgbW9kZWxzLlxuICBwcml2YXRlIGdyYXBoUnVubmVyOiBHcmFwaFJ1bm5lcjtcbiAgcHJpdmF0ZSBncmFwaDogR3JhcGg7XG4gIHByaXZhdGUgc2Vzc2lvbjogU2Vzc2lvbjtcbiAgcHJpdmF0ZSBvcHRpbWl6ZXI6IE9wdGltaXplcjtcbiAgcHJpdmF0ZSB4VGVuc29yOiBUZW5zb3I7XG4gIHByaXZhdGUgbGFiZWxUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSBjb3N0VGVuc29yOiBUZW5zb3I7XG4gIHByaXZhdGUgYWNjdXJhY3lUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSBwcmVkaWN0aW9uVGVuc29yOiBUZW5zb3I7XG5cbiAgcHJpdmF0ZSBkYXRhc2V0RG93bmxvYWRlZDogYm9vbGVhbjtcbiAgcHJpdmF0ZSBkYXRhc2V0TmFtZXM6IHN0cmluZ1tdO1xuICBwcml2YXRlIHNlbGVjdGVkRGF0YXNldE5hbWU6IHN0cmluZztcbiAgcHJpdmF0ZSBtb2RlbE5hbWVzOiBzdHJpbmdbXTtcbiAgcHJpdmF0ZSBzZWxlY3RlZE1vZGVsTmFtZTogc3RyaW5nO1xuICBwcml2YXRlIGxvYWRlZFdlaWdodHM6IExheWVyV2VpZ2h0c0RpY3RbXXxudWxsO1xuICBwcml2YXRlIGRhdGFTZXRzOiB7W2RhdGFzZXROYW1lOiBzdHJpbmddOiBJbk1lbW9yeURhdGFzZXR9O1xuICBwcml2YXRlIGRhdGFTZXQ6IEluTWVtb3J5RGF0YXNldDtcbiAgcHJpdmF0ZSB4aHJEYXRhc2V0Q29uZmlnczoge1tkYXRhc2V0TmFtZTogc3RyaW5nXTogWGhyRGF0YXNldENvbmZpZ307XG4gIHByaXZhdGUgZGF0YXNldFN0YXRzOiBEYXRhU3RhdHNbXTtcblxuICAvLyBTdGF0cy5cbiAgcHJpdmF0ZSBzaG93RGF0YXNldFN0YXRzOiBib29sZWFuO1xuICBwcml2YXRlIHN0YXRzSW5wdXRSYW5nZTogc3RyaW5nO1xuICBwcml2YXRlIHN0YXRzSW5wdXRTaGFwZURpc3BsYXk6IHN0cmluZztcbiAgcHJpdmF0ZSBzdGF0c0xhYmVsU2hhcGVEaXNwbGF5OiBzdHJpbmc7XG4gIHByaXZhdGUgc3RhdHNFeGFtcGxlQ291bnQ6IG51bWJlcjtcblxuICAvLyBDaGFydHMuXG4gIHByaXZhdGUgY29zdENoYXJ0OiBDaGFydDtcbiAgcHJpdmF0ZSBhY2N1cmFjeUNoYXJ0OiBDaGFydDtcbiAgcHJpdmF0ZSBleGFtcGxlc1BlclNlY0NoYXJ0OiBDaGFydDtcbiAgcHJpdmF0ZSBjb3N0Q2hhcnREYXRhOiBDaGFydFBvaW50W107XG4gIHByaXZhdGUgYWNjdXJhY3lDaGFydERhdGE6IENoYXJ0UG9pbnRbXTtcbiAgcHJpdmF0ZSBleGFtcGxlc1BlclNlY0NoYXJ0RGF0YTogQ2hhcnRQb2ludFtdO1xuXG4gIHByaXZhdGUgdHJhaW5CdXR0b246IEhUTUxCdXR0b25FbGVtZW50O1xuXG4gIC8vIFZpc3VhbGl6ZXJzLlxuICBwcml2YXRlIGlucHV0TkRBcnJheVZpc3VhbGl6ZXJzOiBOREFycmF5SW1hZ2VWaXN1YWxpemVyW107XG4gIHByaXZhdGUgb3V0cHV0TkRBcnJheVZpc3VhbGl6ZXJzOiBOREFycmF5TG9naXRzVmlzdWFsaXplcltdO1xuXG4gIHByaXZhdGUgaW5wdXRTaGFwZTogbnVtYmVyW107XG4gIHByaXZhdGUgbGFiZWxTaGFwZTogbnVtYmVyW107XG4gIHByaXZhdGUgZXhhbXBsZXNQZXJTZWM6IG51bWJlcjtcbiAgcHJpdmF0ZSBleGFtcGxlc1RyYWluZWQ6IG51bWJlcjtcbiAgcHJpdmF0ZSBpbmZlcmVuY2VzUGVyU2VjOiBudW1iZXI7XG4gIHByaXZhdGUgaW5mZXJlbmNlRHVyYXRpb246IG51bWJlcjtcblxuICBwcml2YXRlIGlucHV0TGF5ZXI6IE1vZGVsTGF5ZXI7XG4gIHByaXZhdGUgaGlkZGVuTGF5ZXJzOiBNb2RlbExheWVyW107XG5cbiAgcHJpdmF0ZSBsYXllcnNDb250YWluZXI6IEhUTUxEaXZFbGVtZW50O1xuXG4gIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGg7XG4gIC8vIEtlZXAgb25lIGluc3RhbmNlIG9mIGVhY2ggTkRBcnJheU1hdGggc28gd2UgZG9uJ3QgY3JlYXRlIGEgdXNlci1pbml0aWF0ZWRcbiAgLy8gbnVtYmVyIG9mIE5EQXJyYXlNYXRoR1BVJ3MuXG4gIHByaXZhdGUgbWF0aEdQVTogTkRBcnJheU1hdGhHUFU7XG4gIHByaXZhdGUgbWF0aENQVTogTkRBcnJheU1hdGhDUFU7XG5cbiAgcmVhZHkoKSB7XG4gICAgdGhpcy5tYXRoR1BVID0gbmV3IE5EQXJyYXlNYXRoR1BVKCk7XG4gICAgdGhpcy5tYXRoQ1BVID0gbmV3IE5EQXJyYXlNYXRoQ1BVKCk7XG4gICAgdGhpcy5tYXRoID0gdGhpcy5tYXRoR1BVO1xuXG4gICAgY29uc3QgZXZlbnRPYnNlcnZlcjogR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyID0ge1xuICAgICAgYmF0Y2hlc1RyYWluZWRDYWxsYmFjazogKGJhdGNoZXNUcmFpbmVkOiBudW1iZXIpID0+XG4gICAgICAgICAgdGhpcy5kaXNwbGF5QmF0Y2hlc1RyYWluZWQoYmF0Y2hlc1RyYWluZWQpLFxuICAgICAgYXZnQ29zdENhbGxiYWNrOiAoYXZnQ29zdDogU2NhbGFyKSA9PiB0aGlzLmRpc3BsYXlDb3N0KGF2Z0Nvc3QpLFxuICAgICAgbWV0cmljQ2FsbGJhY2s6IChtZXRyaWM6IFNjYWxhcikgPT4gdGhpcy5kaXNwbGF5QWNjdXJhY3kobWV0cmljKSxcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVzQ2FsbGJhY2s6XG4gICAgICAgICAgKGlucHV0RmVlZHM6IEZlZWRFbnRyeVtdW10sIGluZmVyZW5jZU91dHB1dHM6IE5EQXJyYXlbXSkgPT5cbiAgICAgICAgICAgICAgdGhpcy5kaXNwbGF5SW5mZXJlbmNlRXhhbXBsZXNPdXRwdXQoaW5wdXRGZWVkcywgaW5mZXJlbmNlT3V0cHV0cyksXG4gICAgICBpbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrOiAoZXhhbXBsZXNQZXJTZWM6IG51bWJlcikgPT5cbiAgICAgICAgICB0aGlzLmRpc3BsYXlJbmZlcmVuY2VFeGFtcGxlc1BlclNlYyhleGFtcGxlc1BlclNlYyksXG4gICAgICB0cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2s6IChleGFtcGxlc1BlclNlYzogbnVtYmVyKSA9PlxuICAgICAgICAgIHRoaXMuZGlzcGxheUV4YW1wbGVzUGVyU2VjKGV4YW1wbGVzUGVyU2VjKSxcbiAgICAgIHRvdGFsVGltZUNhbGxiYWNrOiAodG90YWxUaW1lU2VjOiBudW1iZXIpID0+IHRoaXMudG90YWxUaW1lU2VjID1cbiAgICAgICAgICB0b3RhbFRpbWVTZWMudG9GaXhlZCgxKSxcbiAgICB9O1xuICAgIHRoaXMuZ3JhcGhSdW5uZXIgPSBuZXcgR3JhcGhSdW5uZXIodGhpcy5tYXRoLCB0aGlzLnNlc3Npb24sIGV2ZW50T2JzZXJ2ZXIpO1xuICAgIHRoaXMub3B0aW1pemVyID0gbmV3IFNHRE9wdGltaXplcihMRUFSTklOR19SQVRFKTtcblxuICAgIC8vIFNldCB1cCBkYXRhc2V0cy5cbiAgICB0aGlzLnBvcHVsYXRlRGF0YXNldHMoKTtcblxuICAgIHRoaXMucXVlcnlTZWxlY3RvcignI2RhdGFzZXQtZHJvcGRvd24gLmRyb3Bkb3duLWNvbnRlbnQnKSEuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICAnaXJvbi1hY3RpdmF0ZScsIChldmVudDogYW55KSA9PiB7XG4gICAgICAgICAgLy8gVXBkYXRlIHRoZSBkYXRhc2V0LlxuICAgICAgICAgIGNvbnN0IGRhdGFzZXROYW1lID0gZXZlbnQuZGV0YWlsLnNlbGVjdGVkO1xuICAgICAgICAgIHRoaXMudXBkYXRlU2VsZWN0ZWREYXRhc2V0KGRhdGFzZXROYW1lKTtcblxuICAgICAgICAgIC8vIFRPRE8obnN0aG9yYXQpOiBSZW1lbWJlciB0aGUgbGFzdCBtb2RlbCB1c2VkIGZvciBlYWNoIGRhdGFzZXQuXG4gICAgICAgICAgdGhpcy5yZW1vdmVBbGxMYXllcnMoKTtcbiAgICAgICAgfSk7XG4gICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjbW9kZWwtZHJvcGRvd24gLmRyb3Bkb3duLWNvbnRlbnQnKSEuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICAnaXJvbi1hY3RpdmF0ZScsIChldmVudDogYW55KSA9PiB7XG4gICAgICAgICAgLy8gVXBkYXRlIHRoZSBtb2RlbC5cbiAgICAgICAgICBjb25zdCBtb2RlbE5hbWUgPSBldmVudC5kZXRhaWwuc2VsZWN0ZWQ7XG4gICAgICAgICAgdGhpcy51cGRhdGVTZWxlY3RlZE1vZGVsKG1vZGVsTmFtZSk7XG4gICAgICAgIH0pO1xuXG4gICAge1xuICAgICAgY29uc3Qgbm9ybWFsaXphdGlvbkRyb3Bkb3duID1cbiAgICAgICAgICB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNub3JtYWxpemF0aW9uLWRyb3Bkb3duIC5kcm9wZG93bi1jb250ZW50JykhO1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgbm9ybWFsaXphdGlvbkRyb3Bkb3duLmFkZEV2ZW50TGlzdGVuZXIoJ2lyb24tYWN0aXZhdGUnLCAoZXZlbnQ6IGFueSkgPT4ge1xuICAgICAgICBjb25zdCBzZWxlY3RlZE5vcm1hbGl6YXRpb25PcHRpb24gPSBldmVudC5kZXRhaWwuc2VsZWN0ZWQ7XG4gICAgICAgIHRoaXMuYXBwbHlOb3JtYWxpemF0aW9uKHNlbGVjdGVkTm9ybWFsaXphdGlvbk9wdGlvbik7XG4gICAgICAgIHRoaXMuc2V0dXBEYXRhc2V0U3RhdHMoKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMuYXBwbGljYXRpb25TdGF0ZSA9IEFwcGxpY2F0aW9uU3RhdGUuSURMRTtcbiAgICB0aGlzLmxvYWRlZFdlaWdodHMgPSBudWxsO1xuICAgIHRoaXMubW9kZWxJbml0aWFsaXplZCA9IGZhbHNlO1xuICAgIHRoaXMuc2hvd1RyYWluU3RhdHMgPSBmYWxzZTtcbiAgICB0aGlzLnNob3dEYXRhc2V0U3RhdHMgPSBmYWxzZTtcblxuICAgIGNvbnN0IGFkZEJ1dHRvbiA9IHRoaXMucXVlcnlTZWxlY3RvcignI2FkZC1sYXllcicpITtcbiAgICBhZGRCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB0aGlzLmFkZExheWVyKCkpO1xuXG4gICAgY29uc3QgZG93bmxvYWRNb2RlbEJ1dHRvbiA9IHRoaXMucXVlcnlTZWxlY3RvcignI2Rvd25sb2FkLW1vZGVsJykhO1xuICAgIGRvd25sb2FkTW9kZWxCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB0aGlzLmRvd25sb2FkTW9kZWwoKSk7XG4gICAgY29uc3QgdXBsb2FkTW9kZWxCdXR0b24gPSB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyN1cGxvYWQtbW9kZWwnKSE7XG4gICAgdXBsb2FkTW9kZWxCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB0aGlzLnVwbG9hZE1vZGVsKCkpO1xuICAgIHRoaXMuc2V0dXBVcGxvYWRNb2RlbEJ1dHRvbigpO1xuXG4gICAgY29uc3QgdXBsb2FkV2VpZ2h0c0J1dHRvbiA9IHRoaXMucXVlcnlTZWxlY3RvcignI3VwbG9hZC13ZWlnaHRzJykhO1xuICAgIHVwbG9hZFdlaWdodHNCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB0aGlzLnVwbG9hZFdlaWdodHMoKSk7XG4gICAgdGhpcy5zZXR1cFVwbG9hZFdlaWdodHNCdXR0b24oKTtcblxuICAgIGNvbnN0IHN0b3BCdXR0b24gPSB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNzdG9wJykhO1xuICAgIHN0b3BCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgICB0aGlzLmFwcGxpY2F0aW9uU3RhdGUgPSBBcHBsaWNhdGlvblN0YXRlLklETEU7XG4gICAgICB0aGlzLmdyYXBoUnVubmVyLnN0b3BUcmFpbmluZygpO1xuICAgIH0pO1xuXG4gICAgdGhpcy50cmFpbkJ1dHRvbiA9IHRoaXMucXVlcnlTZWxlY3RvcignI3RyYWluJykgYXMgSFRNTEJ1dHRvbkVsZW1lbnQ7XG4gICAgdGhpcy50cmFpbkJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcbiAgICAgIHRoaXMuY3JlYXRlTW9kZWwoKTtcbiAgICAgIHRoaXMuc3RhcnRUcmFpbmluZygpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5xdWVyeVNlbGVjdG9yKFxuICAgICAgICAgICAgJyNlbnZpcm9ubWVudC10b2dnbGUnKSEuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKGV2ZW50KSA9PiB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICB0aGlzLm1hdGggPSAoZXZlbnQudGFyZ2V0IGFzIGFueSkuYWN0aXZlID8gdGhpcy5tYXRoR1BVIDogdGhpcy5tYXRoQ1BVO1xuICAgICAgdGhpcy5ncmFwaFJ1bm5lci5zZXRNYXRoKHRoaXMubWF0aCk7XG4gICAgfSk7XG5cbiAgICB0aGlzLmhpZGRlbkxheWVycyA9IFtdO1xuICAgIHRoaXMuZXhhbXBsZXNQZXJTZWMgPSAwO1xuICAgIHRoaXMuaW5mZXJlbmNlc1BlclNlYyA9IDA7XG4gIH1cblxuICBpc1RyYWluaW5nKGFwcGxpY2F0aW9uU3RhdGU6IEFwcGxpY2F0aW9uU3RhdGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gYXBwbGljYXRpb25TdGF0ZSA9PT0gQXBwbGljYXRpb25TdGF0ZS5UUkFJTklORztcbiAgfVxuXG4gIGlzSWRsZShhcHBsaWNhdGlvblN0YXRlOiBBcHBsaWNhdGlvblN0YXRlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGFwcGxpY2F0aW9uU3RhdGUgPT09IEFwcGxpY2F0aW9uU3RhdGUuSURMRTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0VGVzdERhdGEoKTogTkRBcnJheVtdW10ge1xuICAgIGNvbnN0IGRhdGEgPSB0aGlzLmRhdGFTZXQuZ2V0RGF0YSgpO1xuICAgIGlmIChkYXRhID09IG51bGwpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBjb25zdCBbaW1hZ2VzLCBsYWJlbHNdID0gdGhpcy5kYXRhU2V0LmdldERhdGEoKSBhcyBbTkRBcnJheVtdLCBOREFycmF5W11dO1xuXG4gICAgY29uc3Qgc3RhcnQgPSBNYXRoLmZsb29yKFRSQUlOX1RFU1RfUkFUSU8gKiBpbWFnZXMubGVuZ3RoKTtcblxuICAgIHJldHVybiBbaW1hZ2VzLnNsaWNlKHN0YXJ0KSwgbGFiZWxzLnNsaWNlKHN0YXJ0KV07XG4gIH1cblxuICBwcml2YXRlIGdldFRyYWluaW5nRGF0YSgpOiBOREFycmF5W11bXSB7XG4gICAgY29uc3QgW2ltYWdlcywgbGFiZWxzXSA9IHRoaXMuZGF0YVNldC5nZXREYXRhKCkgYXMgW05EQXJyYXlbXSwgTkRBcnJheVtdXTtcblxuICAgIGNvbnN0IGVuZCA9IE1hdGguZmxvb3IoVFJBSU5fVEVTVF9SQVRJTyAqIGltYWdlcy5sZW5ndGgpO1xuXG4gICAgcmV0dXJuIFtpbWFnZXMuc2xpY2UoMCwgZW5kKSwgbGFiZWxzLnNsaWNlKDAsIGVuZCldO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGFydEluZmVyZW5jZSgpIHtcbiAgICBjb25zdCB0ZXN0RGF0YSA9IHRoaXMuZ2V0VGVzdERhdGEoKTtcbiAgICBpZiAodGVzdERhdGEgPT0gbnVsbCkge1xuICAgICAgLy8gRGF0YXNldCBub3QgcmVhZHkgeWV0LlxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy5pc1ZhbGlkICYmICh0ZXN0RGF0YSAhPSBudWxsKSkge1xuICAgICAgY29uc3QgaW5mZXJlbmNlU2h1ZmZsZWRJbnB1dFByb3ZpZGVyR2VuZXJhdG9yID1cbiAgICAgICAgICBuZXcgSW5DUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyKHRlc3REYXRhKTtcbiAgICAgIGNvbnN0IFtpbmZlcmVuY2VJbnB1dFByb3ZpZGVyLCBpbmZlcmVuY2VMYWJlbFByb3ZpZGVyXSA9XG4gICAgICAgICAgaW5mZXJlbmNlU2h1ZmZsZWRJbnB1dFByb3ZpZGVyR2VuZXJhdG9yLmdldElucHV0UHJvdmlkZXJzKCk7XG5cbiAgICAgIGNvbnN0IGluZmVyZW5jZUZlZWRzID0gW1xuICAgICAgICB7dGVuc29yOiB0aGlzLnhUZW5zb3IsIGRhdGE6IGluZmVyZW5jZUlucHV0UHJvdmlkZXJ9LFxuICAgICAgICB7dGVuc29yOiB0aGlzLmxhYmVsVGVuc29yLCBkYXRhOiBpbmZlcmVuY2VMYWJlbFByb3ZpZGVyfVxuICAgICAgXTtcblxuICAgICAgdGhpcy5ncmFwaFJ1bm5lci5pbmZlcihcbiAgICAgICAgICB0aGlzLnByZWRpY3Rpb25UZW5zb3IsIGluZmVyZW5jZUZlZWRzLCBJTkZFUkVOQ0VfRVhBTVBMRV9JTlRFUlZBTF9NUyxcbiAgICAgICAgICBJTkZFUkVOQ0VfRVhBTVBMRV9DT1VOVCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdGFydFRyYWluaW5nKCkge1xuICAgIGNvbnN0IHRyYWluaW5nRGF0YSA9IHRoaXMuZ2V0VHJhaW5pbmdEYXRhKCk7XG4gICAgY29uc3QgdGVzdERhdGEgPSB0aGlzLmdldFRlc3REYXRhKCk7XG5cbiAgICBpZiAodGhpcy5pc1ZhbGlkICYmICh0cmFpbmluZ0RhdGEgIT0gbnVsbCkgJiYgKHRlc3REYXRhICE9IG51bGwpKSB7XG4gICAgICB0aGlzLnJlY3JlYXRlQ2hhcnRzKCk7XG4gICAgICB0aGlzLmdyYXBoUnVubmVyLnJlc2V0U3RhdGlzdGljcygpO1xuXG4gICAgICBjb25zdCB0cmFpbmluZ1NodWZmbGVkSW5wdXRQcm92aWRlckdlbmVyYXRvciA9XG4gICAgICAgICAgbmV3IEluQ1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlcih0cmFpbmluZ0RhdGEpO1xuICAgICAgY29uc3QgW3RyYWluSW5wdXRQcm92aWRlciwgdHJhaW5MYWJlbFByb3ZpZGVyXSA9XG4gICAgICAgICAgdHJhaW5pbmdTaHVmZmxlZElucHV0UHJvdmlkZXJHZW5lcmF0b3IuZ2V0SW5wdXRQcm92aWRlcnMoKTtcblxuICAgICAgY29uc3QgdHJhaW5GZWVkcyA9IFtcbiAgICAgICAge3RlbnNvcjogdGhpcy54VGVuc29yLCBkYXRhOiB0cmFpbklucHV0UHJvdmlkZXJ9LFxuICAgICAgICB7dGVuc29yOiB0aGlzLmxhYmVsVGVuc29yLCBkYXRhOiB0cmFpbkxhYmVsUHJvdmlkZXJ9XG4gICAgICBdO1xuXG4gICAgICBjb25zdCBhY2N1cmFjeVNodWZmbGVkSW5wdXRQcm92aWRlckdlbmVyYXRvciA9XG4gICAgICAgICAgbmV3IEluQ1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlcih0ZXN0RGF0YSk7XG4gICAgICBjb25zdCBbYWNjdXJhY3lJbnB1dFByb3ZpZGVyLCBhY2N1cmFjeUxhYmVsUHJvdmlkZXJdID1cbiAgICAgICAgICBhY2N1cmFjeVNodWZmbGVkSW5wdXRQcm92aWRlckdlbmVyYXRvci5nZXRJbnB1dFByb3ZpZGVycygpO1xuXG4gICAgICBjb25zdCBhY2N1cmFjeUZlZWRzID0gW1xuICAgICAgICB7dGVuc29yOiB0aGlzLnhUZW5zb3IsIGRhdGE6IGFjY3VyYWN5SW5wdXRQcm92aWRlcn0sXG4gICAgICAgIHt0ZW5zb3I6IHRoaXMubGFiZWxUZW5zb3IsIGRhdGE6IGFjY3VyYWN5TGFiZWxQcm92aWRlcn1cbiAgICAgIF07XG5cbiAgICAgIHRoaXMuZ3JhcGhSdW5uZXIudHJhaW4oXG4gICAgICAgICAgdGhpcy5jb3N0VGVuc29yLCB0cmFpbkZlZWRzLCBCQVRDSF9TSVpFLCB0aGlzLm9wdGltaXplcixcbiAgICAgICAgICB1bmRlZmluZWQgLyoqIG51bUJhdGNoZXMgKi8sIHRoaXMuYWNjdXJhY3lUZW5zb3IsIGFjY3VyYWN5RmVlZHMsXG4gICAgICAgICAgQkFUQ0hfU0laRSwgTWV0cmljUmVkdWN0aW9uLk1FQU4sIEVWQUxfSU5URVJWQUxfTVMsIENPU1RfSU5URVJWQUxfTVMpO1xuXG4gICAgICB0aGlzLnNob3dUcmFpblN0YXRzID0gdHJ1ZTtcbiAgICAgIHRoaXMuYXBwbGljYXRpb25TdGF0ZSA9IEFwcGxpY2F0aW9uU3RhdGUuVFJBSU5JTkc7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVNb2RlbCgpIHtcbiAgICBpZiAodGhpcy5zZXNzaW9uICE9IG51bGwpIHtcbiAgICAgIHRoaXMuc2Vzc2lvbi5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgdGhpcy5tb2RlbEluaXRpYWxpemVkID0gZmFsc2U7XG4gICAgaWYgKHRoaXMuaXNWYWxpZCA9PT0gZmFsc2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmdyYXBoID0gbmV3IEdyYXBoKCk7XG4gICAgY29uc3QgZyA9IHRoaXMuZ3JhcGg7XG4gICAgdGhpcy54VGVuc29yID0gZy5wbGFjZWhvbGRlcignaW5wdXQnLCB0aGlzLmlucHV0U2hhcGUpO1xuICAgIHRoaXMubGFiZWxUZW5zb3IgPSBnLnBsYWNlaG9sZGVyKCdsYWJlbCcsIHRoaXMubGFiZWxTaGFwZSk7XG5cbiAgICBsZXQgbmV0d29yayA9IHRoaXMueFRlbnNvcjtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5oaWRkZW5MYXllcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGxldCB3ZWlnaHRzOiBMYXllcldlaWdodHNEaWN0fG51bGwgPSBudWxsO1xuICAgICAgaWYgKHRoaXMubG9hZGVkV2VpZ2h0cyAhPSBudWxsKSB7XG4gICAgICAgIHdlaWdodHMgPSB0aGlzLmxvYWRlZFdlaWdodHNbaV07XG4gICAgICB9XG4gICAgICBuZXR3b3JrID0gdGhpcy5oaWRkZW5MYXllcnNbaV0uYWRkTGF5ZXIoZywgbmV0d29yaywgaSwgd2VpZ2h0cyk7XG4gICAgfVxuICAgIHRoaXMucHJlZGljdGlvblRlbnNvciA9IG5ldHdvcms7XG4gICAgdGhpcy5jb3N0VGVuc29yID1cbiAgICAgICAgZy5zb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdCh0aGlzLnByZWRpY3Rpb25UZW5zb3IsIHRoaXMubGFiZWxUZW5zb3IpO1xuICAgIHRoaXMuYWNjdXJhY3lUZW5zb3IgPVxuICAgICAgICBnLmFyZ21heEVxdWFscyh0aGlzLnByZWRpY3Rpb25UZW5zb3IsIHRoaXMubGFiZWxUZW5zb3IpO1xuXG4gICAgdGhpcy5sb2FkZWRXZWlnaHRzID0gbnVsbDtcblxuICAgIHRoaXMuc2Vzc2lvbiA9IG5ldyBTZXNzaW9uKGcsIHRoaXMubWF0aCk7XG4gICAgdGhpcy5ncmFwaFJ1bm5lci5zZXRTZXNzaW9uKHRoaXMuc2Vzc2lvbik7XG5cbiAgICB0aGlzLnN0YXJ0SW5mZXJlbmNlKCk7XG5cbiAgICB0aGlzLm1vZGVsSW5pdGlhbGl6ZWQgPSB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSBwb3B1bGF0ZURhdGFzZXRzKCkge1xuICAgIHRoaXMuZGF0YVNldHMgPSB7fTtcbiAgICB4aHJfZGF0YXNldC5nZXRYaHJEYXRhc2V0Q29uZmlnKERBVEFTRVRTX0NPTkZJR19KU09OKVxuICAgICAgICAudGhlbihcbiAgICAgICAgICAgIHhockRhdGFzZXRDb25maWdzID0+IHtcbiAgICAgICAgICAgICAgZm9yIChjb25zdCBkYXRhc2V0TmFtZSBpbiB4aHJEYXRhc2V0Q29uZmlncykge1xuICAgICAgICAgICAgICAgIGlmICh4aHJEYXRhc2V0Q29uZmlncy5oYXNPd25Qcm9wZXJ0eShkYXRhc2V0TmFtZSkpIHtcbiAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YVNldHNbZGF0YXNldE5hbWVdID1cbiAgICAgICAgICAgICAgICAgICAgICBuZXcgWGhyRGF0YXNldCh4aHJEYXRhc2V0Q29uZmlnc1tkYXRhc2V0TmFtZV0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB0aGlzLmRhdGFzZXROYW1lcyA9IE9iamVjdC5rZXlzKHRoaXMuZGF0YVNldHMpO1xuICAgICAgICAgICAgICB0aGlzLnNlbGVjdGVkRGF0YXNldE5hbWUgPSB0aGlzLmRhdGFzZXROYW1lc1swXTtcbiAgICAgICAgICAgICAgdGhpcy54aHJEYXRhc2V0Q29uZmlncyA9IHhockRhdGFzZXRDb25maWdzO1xuICAgICAgICAgICAgICB0aGlzLnVwZGF0ZVNlbGVjdGVkRGF0YXNldCh0aGlzLmRhdGFzZXROYW1lc1swXSk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgZXJyb3IgPT4ge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGFzZXQgY29uZmlnIGNvdWxkIG5vdCBiZSBsb2FkZWQ6ICcgKyBlcnJvcik7XG4gICAgICAgICAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlU2VsZWN0ZWREYXRhc2V0KGRhdGFzZXROYW1lOiBzdHJpbmcpIHtcbiAgICB0aGlzLmdyYXBoUnVubmVyLnN0b3BUcmFpbmluZygpO1xuICAgIHRoaXMuZ3JhcGhSdW5uZXIuc3RvcEluZmVycmluZygpO1xuXG4gICAgaWYgKHRoaXMuZGF0YVNldCAhPSBudWxsKSB7XG4gICAgICB0aGlzLmRhdGFTZXQuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHRoaXMuc2VsZWN0ZWREYXRhc2V0TmFtZSA9IGRhdGFzZXROYW1lO1xuICAgIHRoaXMuZGF0YVNldCA9IHRoaXMuZGF0YVNldHNbZGF0YXNldE5hbWVdO1xuICAgIHRoaXMuZGF0YXNldERvd25sb2FkZWQgPSBmYWxzZTtcbiAgICB0aGlzLnNob3dEYXRhc2V0U3RhdHMgPSBmYWxzZTtcblxuICAgIHRoaXMuZGF0YVNldC5mZXRjaERhdGEoKS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMuZGF0YXNldERvd25sb2FkZWQgPSB0cnVlO1xuICAgICAgdGhpcy5hcHBseU5vcm1hbGl6YXRpb24odGhpcy5zZWxlY3RlZE5vcm1hbGl6YXRpb25PcHRpb24pO1xuICAgICAgdGhpcy5zZXR1cERhdGFzZXRTdGF0cygpO1xuICAgICAgaWYgKHRoaXMuaXNWYWxpZCkge1xuICAgICAgICB0aGlzLmNyZWF0ZU1vZGVsKCk7XG4gICAgICAgIHRoaXMuc3RhcnRJbmZlcmVuY2UoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICAvLyBHZXQgcHJlYnVpbHQgbW9kZWxzLlxuICAgIHRoaXMucG9wdWxhdGVNb2RlbERyb3Bkb3duKCk7XG5cbiAgICB0aGlzLmlucHV0U2hhcGUgPSB0aGlzLmRhdGFTZXQuZ2V0RGF0YVNoYXBlKElNQUdFX0RBVEFfSU5ERVgpO1xuICAgIHRoaXMubGFiZWxTaGFwZSA9IHRoaXMuZGF0YVNldC5nZXREYXRhU2hhcGUoTEFCRUxfREFUQV9JTkRFWCk7XG5cbiAgICB0aGlzLmxheWVyc0NvbnRhaW5lciA9XG4gICAgICAgIHRoaXMucXVlcnlTZWxlY3RvcignI2hpZGRlbi1sYXllcnMnKSBhcyBIVE1MRGl2RWxlbWVudDtcblxuICAgIHRoaXMuaW5wdXRMYXllciA9IHRoaXMucXVlcnlTZWxlY3RvcignI2lucHV0LWxheWVyJykgYXMgTW9kZWxMYXllcjtcbiAgICB0aGlzLmlucHV0TGF5ZXIub3V0cHV0U2hhcGVEaXNwbGF5ID1cbiAgICAgICAgbW9kZWxfYnVpbGRlcl91dGlsLmdldERpc3BsYXlTaGFwZSh0aGlzLmlucHV0U2hhcGUpO1xuXG4gICAgY29uc3QgbGFiZWxTaGFwZURpc3BsYXkgPVxuICAgICAgICBtb2RlbF9idWlsZGVyX3V0aWwuZ2V0RGlzcGxheVNoYXBlKHRoaXMubGFiZWxTaGFwZSk7XG4gICAgY29uc3QgY29zdExheWVyID0gdGhpcy5xdWVyeVNlbGVjdG9yKCcjY29zdC1sYXllcicpIGFzIE1vZGVsTGF5ZXI7XG4gICAgY29zdExheWVyLmlucHV0U2hhcGVEaXNwbGF5ID0gbGFiZWxTaGFwZURpc3BsYXk7XG4gICAgY29zdExheWVyLm91dHB1dFNoYXBlRGlzcGxheSA9IGxhYmVsU2hhcGVEaXNwbGF5O1xuXG4gICAgY29uc3Qgb3V0cHV0TGF5ZXIgPSB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNvdXRwdXQtbGF5ZXInKSBhcyBNb2RlbExheWVyO1xuICAgIG91dHB1dExheWVyLmlucHV0U2hhcGVEaXNwbGF5ID0gbGFiZWxTaGFwZURpc3BsYXk7XG5cbiAgICAvLyBTZXR1cCB0aGUgaW5mZXJlbmNlIGV4YW1wbGUgY29udGFpbmVyLlxuICAgIC8vIFRPRE8obnN0aG9yYXQpOiBHZW5lcmFsaXplIHRoaXMuXG4gICAgY29uc3QgaW5mZXJlbmNlQ29udGFpbmVyID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjaW5mZXJlbmNlLWNvbnRhaW5lcicpIGFzIEhUTUxFbGVtZW50O1xuICAgIGluZmVyZW5jZUNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcbiAgICB0aGlzLmlucHV0TkRBcnJheVZpc3VhbGl6ZXJzID0gW107XG4gICAgdGhpcy5vdXRwdXROREFycmF5VmlzdWFsaXplcnMgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IElORkVSRU5DRV9FWEFNUExFX0NPVU5UOyBpKyspIHtcbiAgICAgIGNvbnN0IGluZmVyZW5jZUV4YW1wbGVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBpbmZlcmVuY2VFeGFtcGxlRWxlbWVudC5jbGFzc05hbWUgPSAnaW5mZXJlbmNlLWV4YW1wbGUnO1xuXG4gICAgICAvLyBTZXQgdXAgdGhlIGlucHV0IHZpc3VhbGl6ZXIuXG4gICAgICBjb25zdCBuZGFycmF5SW1hZ2VWaXN1YWxpemVyID1cbiAgICAgICAgICBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCduZGFycmF5LWltYWdlLXZpc3VhbGl6ZXInKSBhc1xuICAgICAgICAgIE5EQXJyYXlJbWFnZVZpc3VhbGl6ZXI7XG4gICAgICBuZGFycmF5SW1hZ2VWaXN1YWxpemVyLnNldFNoYXBlKHRoaXMuaW5wdXRTaGFwZSk7XG4gICAgICBuZGFycmF5SW1hZ2VWaXN1YWxpemVyLnNldFNpemUoXG4gICAgICAgICAgSU5GRVJFTkNFX0lNQUdFX1NJWkVfUFgsIElORkVSRU5DRV9JTUFHRV9TSVpFX1BYKTtcbiAgICAgIHRoaXMuaW5wdXROREFycmF5VmlzdWFsaXplcnMucHVzaChuZGFycmF5SW1hZ2VWaXN1YWxpemVyKTtcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVFbGVtZW50LmFwcGVuZENoaWxkKG5kYXJyYXlJbWFnZVZpc3VhbGl6ZXIpO1xuXG4gICAgICAvLyBTZXQgdXAgdGhlIG91dHB1dCBuZGFycmF5IHZpc3VhbGl6ZXIuXG4gICAgICBjb25zdCBuZGFycmF5TG9naXRzVmlzdWFsaXplciA9XG4gICAgICAgICAgZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbmRhcnJheS1sb2dpdHMtdmlzdWFsaXplcicpIGFzXG4gICAgICAgICAgTkRBcnJheUxvZ2l0c1Zpc3VhbGl6ZXI7XG4gICAgICBuZGFycmF5TG9naXRzVmlzdWFsaXplci5pbml0aWFsaXplKFxuICAgICAgICAgIElORkVSRU5DRV9JTUFHRV9TSVpFX1BYLCBJTkZFUkVOQ0VfSU1BR0VfU0laRV9QWCk7XG4gICAgICB0aGlzLm91dHB1dE5EQXJyYXlWaXN1YWxpemVycy5wdXNoKG5kYXJyYXlMb2dpdHNWaXN1YWxpemVyKTtcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVFbGVtZW50LmFwcGVuZENoaWxkKG5kYXJyYXlMb2dpdHNWaXN1YWxpemVyKTtcblxuICAgICAgaW5mZXJlbmNlQ29udGFpbmVyLmFwcGVuZENoaWxkKGluZmVyZW5jZUV4YW1wbGVFbGVtZW50KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHBvcHVsYXRlTW9kZWxEcm9wZG93bigpIHtcbiAgICBjb25zdCBtb2RlbE5hbWVzID0gWydDdXN0b20nXTtcblxuICAgIGNvbnN0IG1vZGVsQ29uZmlncyA9XG4gICAgICAgIHRoaXMueGhyRGF0YXNldENvbmZpZ3NbdGhpcy5zZWxlY3RlZERhdGFzZXROYW1lXS5tb2RlbENvbmZpZ3M7XG4gICAgZm9yIChjb25zdCBtb2RlbE5hbWUgaW4gbW9kZWxDb25maWdzKSB7XG4gICAgICBpZiAobW9kZWxDb25maWdzLmhhc093blByb3BlcnR5KG1vZGVsTmFtZSkpIHtcbiAgICAgICAgbW9kZWxOYW1lcy5wdXNoKG1vZGVsTmFtZSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5tb2RlbE5hbWVzID0gbW9kZWxOYW1lcztcbiAgICB0aGlzLnNlbGVjdGVkTW9kZWxOYW1lID0gbW9kZWxOYW1lc1ttb2RlbE5hbWVzLmxlbmd0aCAtIDFdO1xuICAgIHRoaXMudXBkYXRlU2VsZWN0ZWRNb2RlbCh0aGlzLnNlbGVjdGVkTW9kZWxOYW1lKTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlU2VsZWN0ZWRNb2RlbChtb2RlbE5hbWU6IHN0cmluZykge1xuICAgIHRoaXMucmVtb3ZlQWxsTGF5ZXJzKCk7XG4gICAgaWYgKG1vZGVsTmFtZSA9PT0gJ0N1c3RvbScpIHtcbiAgICAgIC8vIFRPRE8obnN0aG9yYXQpOiBSZW1lbWJlciB0aGUgY3VzdG9tIGxheWVycy5cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmxvYWRNb2RlbEZyb21QYXRoKHRoaXMueGhyRGF0YXNldENvbmZpZ3NbdGhpcy5zZWxlY3RlZERhdGFzZXROYW1lXVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5tb2RlbENvbmZpZ3NbbW9kZWxOYW1lXVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5wYXRoKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9hZE1vZGVsRnJvbVBhdGgobW9kZWxQYXRoOiBzdHJpbmcpIHtcbiAgICBjb25zdCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICB4aHIub3BlbignR0VUJywgbW9kZWxQYXRoKTtcblxuICAgIHhoci5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICB0aGlzLmxvYWRNb2RlbEZyb21Kc29uKHhoci5yZXNwb25zZVRleHQpO1xuICAgIH07XG4gICAgeGhyLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnTW9kZWwgY291bGQgbm90IGJlIGZldGNoZWQgZnJvbSAnICsgbW9kZWxQYXRoICsgJzogJyArIGVycm9yKTtcbiAgICB9O1xuICAgIHhoci5zZW5kKCk7XG4gIH1cblxuICBwcml2YXRlIHNldHVwRGF0YXNldFN0YXRzKCkge1xuICAgIHRoaXMuZGF0YXNldFN0YXRzID0gdGhpcy5kYXRhU2V0LmdldFN0YXRzKCk7XG4gICAgdGhpcy5zdGF0c0V4YW1wbGVDb3VudCA9IHRoaXMuZGF0YXNldFN0YXRzW0lNQUdFX0RBVEFfSU5ERVhdLmV4YW1wbGVDb3VudDtcbiAgICB0aGlzLnN0YXRzSW5wdXRSYW5nZSA9ICdbJyArIHRoaXMuZGF0YXNldFN0YXRzW0lNQUdFX0RBVEFfSU5ERVhdLmlucHV0TWluICtcbiAgICAgICAgJywgJyArIHRoaXMuZGF0YXNldFN0YXRzW0lNQUdFX0RBVEFfSU5ERVhdLmlucHV0TWF4ICsgJ10nO1xuICAgIHRoaXMuc3RhdHNJbnB1dFNoYXBlRGlzcGxheSA9IG1vZGVsX2J1aWxkZXJfdXRpbC5nZXREaXNwbGF5U2hhcGUoXG4gICAgICAgIHRoaXMuZGF0YXNldFN0YXRzW0lNQUdFX0RBVEFfSU5ERVhdLnNoYXBlKTtcbiAgICB0aGlzLnN0YXRzTGFiZWxTaGFwZURpc3BsYXkgPSBtb2RlbF9idWlsZGVyX3V0aWwuZ2V0RGlzcGxheVNoYXBlKFxuICAgICAgICB0aGlzLmRhdGFzZXRTdGF0c1tMQUJFTF9EQVRBX0lOREVYXS5zaGFwZSk7XG4gICAgdGhpcy5zaG93RGF0YXNldFN0YXRzID0gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgYXBwbHlOb3JtYWxpemF0aW9uKHNlbGVjdGVkTm9ybWFsaXphdGlvbk9wdGlvbjogbnVtYmVyKSB7XG4gICAgc3dpdGNoIChzZWxlY3RlZE5vcm1hbGl6YXRpb25PcHRpb24pIHtcbiAgICAgIGNhc2UgTm9ybWFsaXphdGlvbi5OT1JNQUxJWkFUSU9OX05FR0FUSVZFX09ORV9UT19PTkU6IHtcbiAgICAgICAgdGhpcy5kYXRhU2V0Lm5vcm1hbGl6ZVdpdGhpbkJvdW5kcyhJTUFHRV9EQVRBX0lOREVYLCAtMSwgMSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgY2FzZSBOb3JtYWxpemF0aW9uLk5PUk1BTElaQVRJT05fWkVST19UT19PTkU6IHtcbiAgICAgICAgdGhpcy5kYXRhU2V0Lm5vcm1hbGl6ZVdpdGhpbkJvdW5kcyhJTUFHRV9EQVRBX0lOREVYLCAwLCAxKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjYXNlIE5vcm1hbGl6YXRpb24uTk9STUFMSVpBVElPTl9OT05FOiB7XG4gICAgICAgIHRoaXMuZGF0YVNldC5yZW1vdmVOb3JtYWxpemF0aW9uKElNQUdFX0RBVEFfSU5ERVgpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGRlZmF1bHQ6IHsgdGhyb3cgbmV3IEVycm9yKCdOb3JtYWxpemF0aW9uIG9wdGlvbiBtdXN0IGJlIDAsIDEsIG9yIDInKTsgfVxuICAgIH1cbiAgICB0aGlzLnNldHVwRGF0YXNldFN0YXRzKCk7XG4gIH1cblxuICBwcml2YXRlIHJlY3JlYXRlQ2hhcnRzKCkge1xuICAgIHRoaXMuY29zdENoYXJ0RGF0YSA9IFtdO1xuICAgIGlmICh0aGlzLmNvc3RDaGFydCAhPSBudWxsKSB7XG4gICAgICB0aGlzLmNvc3RDaGFydC5kZXN0cm95KCk7XG4gICAgfVxuICAgIHRoaXMuY29zdENoYXJ0ID1cbiAgICAgICAgdGhpcy5jcmVhdGVDaGFydCgnY29zdC1jaGFydCcsICdDb3N0JywgdGhpcy5jb3N0Q2hhcnREYXRhLCAwKTtcblxuICAgIGlmICh0aGlzLmFjY3VyYWN5Q2hhcnQgIT0gbnVsbCkge1xuICAgICAgdGhpcy5hY2N1cmFjeUNoYXJ0LmRlc3Ryb3koKTtcbiAgICB9XG4gICAgdGhpcy5hY2N1cmFjeUNoYXJ0RGF0YSA9IFtdO1xuICAgIHRoaXMuYWNjdXJhY3lDaGFydCA9IHRoaXMuY3JlYXRlQ2hhcnQoXG4gICAgICAgICdhY2N1cmFjeS1jaGFydCcsICdBY2N1cmFjeScsIHRoaXMuYWNjdXJhY3lDaGFydERhdGEsIDAsIDEwMCk7XG5cbiAgICBpZiAodGhpcy5leGFtcGxlc1BlclNlY0NoYXJ0ICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZXhhbXBsZXNQZXJTZWNDaGFydC5kZXN0cm95KCk7XG4gICAgfVxuICAgIHRoaXMuZXhhbXBsZXNQZXJTZWNDaGFydERhdGEgPSBbXTtcbiAgICB0aGlzLmV4YW1wbGVzUGVyU2VjQ2hhcnQgPSB0aGlzLmNyZWF0ZUNoYXJ0KFxuICAgICAgICAnZXhhbXBsZXNwZXJzZWMtY2hhcnQnLCAnRXhhbXBsZXMvc2VjJywgdGhpcy5leGFtcGxlc1BlclNlY0NoYXJ0RGF0YSxcbiAgICAgICAgMCk7XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZUNoYXJ0KFxuICAgICAgY2FudmFzSWQ6IHN0cmluZywgbGFiZWw6IHN0cmluZywgZGF0YTogQ2hhcnREYXRhW10sIG1pbj86IG51bWJlcixcbiAgICAgIG1heD86IG51bWJlcik6IENoYXJ0IHtcbiAgICBjb25zdCBjb250ZXh0ID0gKGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGNhbnZhc0lkKSBhcyBIVE1MQ2FudmFzRWxlbWVudClcbiAgICAgICAgICAgICAgICAgICAgICAgIC5nZXRDb250ZXh0KCcyZCcpIGFzIENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRDtcbiAgICByZXR1cm4gbmV3IENoYXJ0KGNvbnRleHQsIHtcbiAgICAgIHR5cGU6ICdsaW5lJyxcbiAgICAgIGRhdGE6IHtcbiAgICAgICAgZGF0YXNldHM6IFt7XG4gICAgICAgICAgZGF0YSxcbiAgICAgICAgICBmaWxsOiBmYWxzZSxcbiAgICAgICAgICBsYWJlbCxcbiAgICAgICAgICBwb2ludFJhZGl1czogMCxcbiAgICAgICAgICBib3JkZXJDb2xvcjogJ3JnYmEoNzUsMTkyLDE5MiwxKScsXG4gICAgICAgICAgYm9yZGVyV2lkdGg6IDEsXG4gICAgICAgICAgbGluZVRlbnNpb246IDAsXG4gICAgICAgICAgcG9pbnRIaXRSYWRpdXM6IDhcbiAgICAgICAgfV1cbiAgICAgIH0sXG4gICAgICBvcHRpb25zOiB7XG4gICAgICAgIGFuaW1hdGlvbjoge2R1cmF0aW9uOiAwfSxcbiAgICAgICAgcmVzcG9uc2l2ZTogZmFsc2UsXG4gICAgICAgIHNjYWxlczoge1xuICAgICAgICAgIHhBeGVzOiBbe3R5cGU6ICdsaW5lYXInLCBwb3NpdGlvbjogJ2JvdHRvbSd9XSxcbiAgICAgICAgICB5QXhlczogW3tcbiAgICAgICAgICAgIHRpY2tzOiB7XG4gICAgICAgICAgICAgIG1heCxcbiAgICAgICAgICAgICAgbWluLFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1dXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc3BsYXlCYXRjaGVzVHJhaW5lZCh0b3RhbEJhdGNoZXNUcmFpbmVkOiBudW1iZXIpIHtcbiAgICB0aGlzLmV4YW1wbGVzVHJhaW5lZCA9IEJBVENIX1NJWkUgKiB0b3RhbEJhdGNoZXNUcmFpbmVkO1xuICB9XG5cbiAgZGlzcGxheUNvc3QoYXZnQ29zdDogU2NhbGFyKSB7XG4gICAgdGhpcy5jb3N0Q2hhcnREYXRhLnB1c2goXG4gICAgICAgIHt4OiB0aGlzLmdyYXBoUnVubmVyLmdldFRvdGFsQmF0Y2hlc1RyYWluZWQoKSwgeTogYXZnQ29zdC5nZXQoKX0pO1xuICAgIHRoaXMuY29zdENoYXJ0LnVwZGF0ZSgpO1xuICB9XG5cbiAgZGlzcGxheUFjY3VyYWN5KGFjY3VyYWN5OiBTY2FsYXIpIHtcbiAgICB0aGlzLmFjY3VyYWN5Q2hhcnREYXRhLnB1c2goe1xuICAgICAgeDogdGhpcy5ncmFwaFJ1bm5lci5nZXRUb3RhbEJhdGNoZXNUcmFpbmVkKCksXG4gICAgICB5OiBhY2N1cmFjeS5nZXQoKSAqIDEwMFxuICAgIH0pO1xuICAgIHRoaXMuYWNjdXJhY3lDaGFydC51cGRhdGUoKTtcbiAgfVxuXG4gIGRpc3BsYXlJbmZlcmVuY2VFeGFtcGxlc1BlclNlYyhleGFtcGxlc1BlclNlYzogbnVtYmVyKSB7XG4gICAgdGhpcy5pbmZlcmVuY2VzUGVyU2VjID1cbiAgICAgICAgdGhpcy5zbW9vdGhFeGFtcGxlc1BlclNlYyh0aGlzLmluZmVyZW5jZXNQZXJTZWMsIGV4YW1wbGVzUGVyU2VjKTtcbiAgICB0aGlzLmluZmVyZW5jZUR1cmF0aW9uID0gTnVtYmVyKCgxMDAwIC8gZXhhbXBsZXNQZXJTZWMpLnRvUHJlY2lzaW9uKDMpKTtcbiAgfVxuXG4gIGRpc3BsYXlFeGFtcGxlc1BlclNlYyhleGFtcGxlc1BlclNlYzogbnVtYmVyKSB7XG4gICAgdGhpcy5leGFtcGxlc1BlclNlY0NoYXJ0RGF0YS5wdXNoKFxuICAgICAgICB7eDogdGhpcy5ncmFwaFJ1bm5lci5nZXRUb3RhbEJhdGNoZXNUcmFpbmVkKCksIHk6IGV4YW1wbGVzUGVyU2VjfSk7XG4gICAgdGhpcy5leGFtcGxlc1BlclNlY0NoYXJ0LnVwZGF0ZSgpO1xuICAgIHRoaXMuZXhhbXBsZXNQZXJTZWMgPVxuICAgICAgICB0aGlzLnNtb290aEV4YW1wbGVzUGVyU2VjKHRoaXMuZXhhbXBsZXNQZXJTZWMsIGV4YW1wbGVzUGVyU2VjKTtcbiAgfVxuXG4gIHByaXZhdGUgc21vb3RoRXhhbXBsZXNQZXJTZWMoXG4gICAgICBsYXN0RXhhbXBsZXNQZXJTZWM6IG51bWJlciwgbmV4dEV4YW1wbGVzUGVyU2VjOiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBOdW1iZXIoKEVYQU1QTEVfU0VDX1NUQVRfU01PT1RISU5HX0ZBQ1RPUiAqIGxhc3RFeGFtcGxlc1BlclNlYyArXG4gICAgICAgICAgICAgICAgICAgKDEgLSBFWEFNUExFX1NFQ19TVEFUX1NNT09USElOR19GQUNUT1IpICogbmV4dEV4YW1wbGVzUGVyU2VjKVxuICAgICAgICAgICAgICAgICAgICAgIC50b1ByZWNpc2lvbigzKSk7XG4gIH1cblxuICBkaXNwbGF5SW5mZXJlbmNlRXhhbXBsZXNPdXRwdXQoXG4gICAgICBpbnB1dEZlZWRzOiBGZWVkRW50cnlbXVtdLCBpbmZlcmVuY2VPdXRwdXRzOiBOREFycmF5W10pIHtcbiAgICBsZXQgaW1hZ2VzOiBBcnJheTNEW10gPSBbXTtcbiAgICBjb25zdCBsb2dpdHM6IEFycmF5MURbXSA9IFtdO1xuICAgIGNvbnN0IGxhYmVsczogQXJyYXkxRFtdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dEZlZWRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpbWFnZXMucHVzaChpbnB1dEZlZWRzW2ldW0lNQUdFX0RBVEFfSU5ERVhdLmRhdGEgYXMgQXJyYXkzRCk7XG4gICAgICBsYWJlbHMucHVzaChpbnB1dEZlZWRzW2ldW0xBQkVMX0RBVEFfSU5ERVhdLmRhdGEgYXMgQXJyYXkxRCk7XG4gICAgICBsb2dpdHMucHVzaChpbmZlcmVuY2VPdXRwdXRzW2ldIGFzIEFycmF5MUQpO1xuICAgIH1cblxuICAgIGltYWdlcyA9XG4gICAgICAgIHRoaXMuZGF0YVNldC51bm5vcm1hbGl6ZUV4YW1wbGVzKGltYWdlcywgSU1BR0VfREFUQV9JTkRFWCkgYXMgQXJyYXkzRFtdO1xuXG4gICAgLy8gRHJhdyB0aGUgaW1hZ2VzLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5wdXRGZWVkcy5sZW5ndGg7IGkrKykge1xuICAgICAgdGhpcy5pbnB1dE5EQXJyYXlWaXN1YWxpemVyc1tpXS5zYXZlSW1hZ2VEYXRhRnJvbU5EQXJyYXkoaW1hZ2VzW2ldKTtcbiAgICB9XG5cbiAgICAvLyBEcmF3IHRoZSBsb2dpdHMuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dEZlZWRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBzb2Z0bWF4TG9naXRzID0gdGhpcy5tYXRoLnNvZnRtYXgobG9naXRzW2ldKTtcblxuICAgICAgdGhpcy5vdXRwdXROREFycmF5VmlzdWFsaXplcnNbaV0uZHJhd0xvZ2l0cyhcbiAgICAgICAgICBzb2Z0bWF4TG9naXRzLCBsYWJlbHNbaV0sXG4gICAgICAgICAgdGhpcy54aHJEYXRhc2V0Q29uZmlnc1t0aGlzLnNlbGVjdGVkRGF0YXNldE5hbWVdLmxhYmVsQ2xhc3NOYW1lcyk7XG4gICAgICB0aGlzLmlucHV0TkRBcnJheVZpc3VhbGl6ZXJzW2ldLmRyYXcoKTtcblxuICAgICAgc29mdG1heExvZ2l0cy5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG5cbiAgYWRkTGF5ZXIoKTogTW9kZWxMYXllciB7XG4gICAgY29uc3QgbW9kZWxMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ21vZGVsLWxheWVyJykgYXMgTW9kZWxMYXllcjtcbiAgICBtb2RlbExheWVyLmNsYXNzTmFtZSA9ICdsYXllcic7XG4gICAgdGhpcy5sYXllcnNDb250YWluZXIuYXBwZW5kQ2hpbGQobW9kZWxMYXllcik7XG5cbiAgICBjb25zdCBsYXN0SGlkZGVuTGF5ZXIgPSB0aGlzLmhpZGRlbkxheWVyc1t0aGlzLmhpZGRlbkxheWVycy5sZW5ndGggLSAxXTtcbiAgICBjb25zdCBsYXN0T3V0cHV0U2hhcGUgPSBsYXN0SGlkZGVuTGF5ZXIgIT0gbnVsbCA/XG4gICAgICAgIGxhc3RIaWRkZW5MYXllci5nZXRPdXRwdXRTaGFwZSgpIDpcbiAgICAgICAgdGhpcy5pbnB1dFNoYXBlO1xuICAgIHRoaXMuaGlkZGVuTGF5ZXJzLnB1c2gobW9kZWxMYXllcik7XG4gICAgbW9kZWxMYXllci5pbml0aWFsaXplKHRoaXMsIGxhc3RPdXRwdXRTaGFwZSk7XG4gICAgcmV0dXJuIG1vZGVsTGF5ZXI7XG4gIH1cblxuICByZW1vdmVMYXllcihtb2RlbExheWVyOiBNb2RlbExheWVyKSB7XG4gICAgdGhpcy5sYXllcnNDb250YWluZXIucmVtb3ZlQ2hpbGQobW9kZWxMYXllcik7XG4gICAgdGhpcy5oaWRkZW5MYXllcnMuc3BsaWNlKHRoaXMuaGlkZGVuTGF5ZXJzLmluZGV4T2YobW9kZWxMYXllciksIDEpO1xuICAgIHRoaXMubGF5ZXJQYXJhbUNoYW5nZWQoKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlQWxsTGF5ZXJzKCkge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5oaWRkZW5MYXllcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHRoaXMubGF5ZXJzQ29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMuaGlkZGVuTGF5ZXJzW2ldKTtcbiAgICB9XG4gICAgdGhpcy5oaWRkZW5MYXllcnMgPSBbXTtcbiAgICB0aGlzLmxheWVyUGFyYW1DaGFuZ2VkKCk7XG4gIH1cblxuICBwcml2YXRlIHZhbGlkYXRlTW9kZWwoKSB7XG4gICAgbGV0IHZhbGlkID0gdHJ1ZTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuaGlkZGVuTGF5ZXJzLmxlbmd0aDsgKytpKSB7XG4gICAgICB2YWxpZCA9IHZhbGlkICYmIHRoaXMuaGlkZGVuTGF5ZXJzW2ldLmlzVmFsaWQoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuaGlkZGVuTGF5ZXJzLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IGxhc3RMYXllciA9IHRoaXMuaGlkZGVuTGF5ZXJzW3RoaXMuaGlkZGVuTGF5ZXJzLmxlbmd0aCAtIDFdO1xuICAgICAgdmFsaWQgPSB2YWxpZCAmJlxuICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5sYWJlbFNoYXBlLCBsYXN0TGF5ZXIuZ2V0T3V0cHV0U2hhcGUoKSk7XG4gICAgfVxuICAgIHRoaXMuaXNWYWxpZCA9IHZhbGlkICYmICh0aGlzLmhpZGRlbkxheWVycy5sZW5ndGggPiAwKTtcbiAgfVxuXG4gIGxheWVyUGFyYW1DaGFuZ2VkKCkge1xuICAgIC8vIEdvIHRocm91Z2ggZWFjaCBvZiB0aGUgbW9kZWwgbGF5ZXJzIGFuZCBwcm9wYWdhdGUgc2hhcGVzLlxuICAgIGxldCBsYXN0T3V0cHV0U2hhcGUgPSB0aGlzLmlucHV0U2hhcGU7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmhpZGRlbkxheWVycy5sZW5ndGg7IGkrKykge1xuICAgICAgbGFzdE91dHB1dFNoYXBlID0gdGhpcy5oaWRkZW5MYXllcnNbaV0uc2V0SW5wdXRTaGFwZShsYXN0T3V0cHV0U2hhcGUpO1xuICAgIH1cbiAgICB0aGlzLnZhbGlkYXRlTW9kZWwoKTtcblxuICAgIGlmICh0aGlzLmlzVmFsaWQpIHtcbiAgICAgIHRoaXMuY3JlYXRlTW9kZWwoKTtcbiAgICAgIHRoaXMuc3RhcnRJbmZlcmVuY2UoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGRvd25sb2FkTW9kZWwoKSB7XG4gICAgY29uc3QgbW9kZWxKc29uID0gdGhpcy5nZXRNb2RlbEFzSnNvbigpO1xuICAgIGNvbnN0IGJsb2IgPSBuZXcgQmxvYihbbW9kZWxKc29uXSwge3R5cGU6ICd0ZXh0L2pzb24nfSk7XG4gICAgY29uc3QgdGV4dEZpbGUgPSB3aW5kb3cuVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTtcblxuICAgIC8vIEZvcmNlIGEgZG93bmxvYWQuXG4gICAgY29uc3QgYSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGEpO1xuICAgIGEuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICBhLmhyZWYgPSB0ZXh0RmlsZTtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgKGEgYXMgYW55KS5kb3dubG9hZCA9IHRoaXMuc2VsZWN0ZWREYXRhc2V0TmFtZSArICdfbW9kZWwnO1xuICAgIGEuY2xpY2soKTtcblxuICAgIGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoYSk7XG4gICAgd2luZG93LlVSTC5yZXZva2VPYmplY3RVUkwodGV4dEZpbGUpO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGxvYWRNb2RlbCgpIHtcbiAgICAodGhpcy5xdWVyeVNlbGVjdG9yKCcjbW9kZWwtZmlsZScpIGFzIEhUTUxJbnB1dEVsZW1lbnQpLmNsaWNrKCk7XG4gIH1cblxuICBwcml2YXRlIHNldHVwVXBsb2FkTW9kZWxCdXR0b24oKSB7XG4gICAgLy8gU2hvdyBhbmQgc2V0dXAgdGhlIGxvYWQgdmlldyBidXR0b24uXG4gICAgY29uc3QgZmlsZUlucHV0ID0gdGhpcy5xdWVyeVNlbGVjdG9yKCcjbW9kZWwtZmlsZScpIGFzIEhUTUxJbnB1dEVsZW1lbnQ7XG4gICAgZmlsZUlucHV0LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIGV2ZW50ID0+IHtcbiAgICAgIGNvbnN0IGZpbGUgPSBmaWxlSW5wdXQuZmlsZXMhWzBdO1xuICAgICAgLy8gQ2xlYXIgb3V0IHRoZSB2YWx1ZSBvZiB0aGUgZmlsZSBjaG9vc2VyLiBUaGlzIGVuc3VyZXMgdGhhdCBpZiB0aGUgdXNlclxuICAgICAgLy8gc2VsZWN0cyB0aGUgc2FtZSBmaWxlLCB3ZSdsbCByZS1yZWFkIGl0LlxuICAgICAgZmlsZUlucHV0LnZhbHVlID0gJyc7XG4gICAgICBjb25zdCBmaWxlUmVhZGVyID0gbmV3IEZpbGVSZWFkZXIoKTtcbiAgICAgIGZpbGVSZWFkZXIub25sb2FkID0gKGV2dCkgPT4ge1xuICAgICAgICB0aGlzLnJlbW92ZUFsbExheWVycygpO1xuICAgICAgICBjb25zdCBtb2RlbEpzb246IHN0cmluZyA9IGZpbGVSZWFkZXIucmVzdWx0O1xuICAgICAgICB0aGlzLmxvYWRNb2RlbEZyb21Kc29uKG1vZGVsSnNvbik7XG4gICAgICB9O1xuICAgICAgZmlsZVJlYWRlci5yZWFkQXNUZXh0KGZpbGUpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRNb2RlbEFzSnNvbigpOiBzdHJpbmcge1xuICAgIGNvbnN0IGxheWVyQnVpbGRlcnM6IExheWVyQnVpbGRlcltdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmhpZGRlbkxheWVycy5sZW5ndGg7IGkrKykge1xuICAgICAgbGF5ZXJCdWlsZGVycy5wdXNoKHRoaXMuaGlkZGVuTGF5ZXJzW2ldLmxheWVyQnVpbGRlcik7XG4gICAgfVxuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShsYXllckJ1aWxkZXJzKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9hZE1vZGVsRnJvbUpzb24obW9kZWxKc29uOiBzdHJpbmcpIHtcbiAgICBsZXQgbGFzdE91dHB1dFNoYXBlID0gdGhpcy5pbnB1dFNoYXBlO1xuXG4gICAgY29uc3QgbGF5ZXJCdWlsZGVycyA9IEpTT04ucGFyc2UobW9kZWxKc29uKSBhcyBMYXllckJ1aWxkZXJbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxheWVyQnVpbGRlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG1vZGVsTGF5ZXIgPSB0aGlzLmFkZExheWVyKCk7XG4gICAgICBtb2RlbExheWVyLmxvYWRQYXJhbXNGcm9tTGF5ZXJCdWlsZGVyKGxhc3RPdXRwdXRTaGFwZSwgbGF5ZXJCdWlsZGVyc1tpXSk7XG4gICAgICBsYXN0T3V0cHV0U2hhcGUgPSB0aGlzLmhpZGRlbkxheWVyc1tpXS5zZXRJbnB1dFNoYXBlKGxhc3RPdXRwdXRTaGFwZSk7XG4gICAgfVxuICAgIHRoaXMudmFsaWRhdGVNb2RlbCgpO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGxvYWRXZWlnaHRzKCkge1xuICAgICh0aGlzLnF1ZXJ5U2VsZWN0b3IoJyN3ZWlnaHRzLWZpbGUnKSBhcyBIVE1MSW5wdXRFbGVtZW50KS5jbGljaygpO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXR1cFVwbG9hZFdlaWdodHNCdXR0b24oKSB7XG4gICAgLy8gU2hvdyBhbmQgc2V0dXAgdGhlIGxvYWQgdmlldyBidXR0b24uXG4gICAgY29uc3QgZmlsZUlucHV0ID0gdGhpcy5xdWVyeVNlbGVjdG9yKCcjd2VpZ2h0cy1maWxlJykgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICBmaWxlSW5wdXQuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgZXZlbnQgPT4ge1xuICAgICAgY29uc3QgZmlsZSA9IGZpbGVJbnB1dC5maWxlcyFbMF07XG4gICAgICAvLyBDbGVhciBvdXQgdGhlIHZhbHVlIG9mIHRoZSBmaWxlIGNob29zZXIuIFRoaXMgZW5zdXJlcyB0aGF0IGlmIHRoZSB1c2VyXG4gICAgICAvLyBzZWxlY3RzIHRoZSBzYW1lIGZpbGUsIHdlJ2xsIHJlLXJlYWQgaXQuXG4gICAgICBmaWxlSW5wdXQudmFsdWUgPSAnJztcbiAgICAgIGNvbnN0IGZpbGVSZWFkZXIgPSBuZXcgRmlsZVJlYWRlcigpO1xuICAgICAgZmlsZVJlYWRlci5vbmxvYWQgPSAoZXZ0KSA9PiB7XG4gICAgICAgIGNvbnN0IHdlaWdodHNKc29uOiBzdHJpbmcgPSBmaWxlUmVhZGVyLnJlc3VsdDtcbiAgICAgICAgdGhpcy5sb2FkV2VpZ2h0c0Zyb21Kc29uKHdlaWdodHNKc29uKTtcbiAgICAgICAgdGhpcy5jcmVhdGVNb2RlbCgpO1xuICAgICAgICB0aGlzLnN0YXJ0SW5mZXJlbmNlKCk7XG4gICAgICB9O1xuICAgICAgZmlsZVJlYWRlci5yZWFkQXNUZXh0KGZpbGUpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2FkV2VpZ2h0c0Zyb21Kc29uKHdlaWdodHNKc29uOiBzdHJpbmcpIHtcbiAgICB0aGlzLmxvYWRlZFdlaWdodHMgPSBKU09OLnBhcnNlKHdlaWdodHNKc29uKSBhcyBMYXllcldlaWdodHNEaWN0W107XG4gIH1cbn1cblxuZG9jdW1lbnQucmVnaXN0ZXJFbGVtZW50KE1vZGVsQnVpbGRlci5wcm90b3R5cGUuaXMsIE1vZGVsQnVpbGRlcik7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R3JhcGgsIFRlbnNvcn0gZnJvbSAnLi4vbGVhcm5qcyc7XG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tdW51c2VkLXZhcmlhYmxlXG5pbXBvcnQge1BvbHltZXJFbGVtZW50LCBQb2x5bWVySFRNTEVsZW1lbnR9IGZyb20gJy4uL3BvbHltZXItc3BlYyc7XG5cbmltcG9ydCAqIGFzIGxheWVyX2J1aWxkZXIgZnJvbSAnLi9sYXllcl9idWlsZGVyJztcbmltcG9ydCB7TGF5ZXJCdWlsZGVyLCBMYXllck5hbWUsIExheWVyV2VpZ2h0c0RpY3R9IGZyb20gJy4vbGF5ZXJfYnVpbGRlcic7XG5pbXBvcnQge01vZGVsQnVpbGRlcn0gZnJvbSAnLi9tb2RlbC1idWlsZGVyJztcbmltcG9ydCAqIGFzIG1vZGVsX2J1aWxkZXJfdXRpbCBmcm9tICcuL21vZGVsX2J1aWxkZXJfdXRpbCc7XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTp2YXJpYWJsZS1uYW1lXG5leHBvcnQgbGV0IE1vZGVsTGF5ZXJQb2x5bWVyID0gUG9seW1lckVsZW1lbnQoe1xuICBpczogJ21vZGVsLWxheWVyJyxcbiAgcHJvcGVydGllczoge1xuICAgIGxheWVyTmFtZTogU3RyaW5nLFxuICAgIGlucHV0U2hhcGVEaXNwbGF5OiBTdHJpbmcsXG4gICAgb3V0cHV0U2hhcGVEaXNwbGF5OiBTdHJpbmcsXG4gICAgaXNTdGF0aWM6IHt0eXBlOiBCb29sZWFuLCB2YWx1ZTogZmFsc2V9LFxuICAgIGxheWVyTmFtZXM6IEFycmF5LFxuICAgIHNlbGVjdGVkTGF5ZXJOYW1lOiBTdHJpbmcsXG4gICAgaGFzRXJyb3I6IHt0eXBlOiBCb29sZWFuLCB2YWx1ZTogZmFsc2V9LFxuICAgIGVycm9yTWVzc2FnZXM6IEFycmF5LFxuICB9XG59KTtcblxuZXhwb3J0IGNsYXNzIE1vZGVsTGF5ZXIgZXh0ZW5kcyBNb2RlbExheWVyUG9seW1lciB7XG4gIC8vIFBvbHltZXIgcHJvcGVydGllcy5cbiAgaW5wdXRTaGFwZURpc3BsYXk6IHN0cmluZztcbiAgb3V0cHV0U2hhcGVEaXNwbGF5OiBzdHJpbmc7XG4gIHByaXZhdGUgbGF5ZXJOYW1lczogTGF5ZXJOYW1lW107XG4gIHByaXZhdGUgc2VsZWN0ZWRMYXllck5hbWU6IExheWVyTmFtZTtcbiAgcHJpdmF0ZSBoYXNFcnJvcjogYm9vbGVhbjtcbiAgcHJpdmF0ZSBlcnJvck1lc3NhZ2VzOiBzdHJpbmdbXTtcblxuICBwcml2YXRlIG1vZGVsQnVpbGRlcjogTW9kZWxCdWlsZGVyO1xuICBsYXllckJ1aWxkZXI6IExheWVyQnVpbGRlcjtcbiAgcHJpdmF0ZSBpbnB1dFNoYXBlOiBudW1iZXJbXTtcbiAgcHJpdmF0ZSBvdXRwdXRTaGFwZTogbnVtYmVyW107XG5cbiAgcHJpdmF0ZSBwYXJhbUNvbnRhaW5lcjogSFRNTERpdkVsZW1lbnQ7XG5cbiAgaW5pdGlhbGl6ZShtb2RlbEJ1aWxkZXI6IE1vZGVsQnVpbGRlciwgaW5wdXRTaGFwZTogbnVtYmVyW10pIHtcbiAgICB0aGlzLm1vZGVsQnVpbGRlciA9IG1vZGVsQnVpbGRlcjtcbiAgICB0aGlzLnBhcmFtQ29udGFpbmVyID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcucGFyYW0tY29udGFpbmVyJykgYXMgSFRNTERpdkVsZW1lbnQ7XG4gICAgdGhpcy5sYXllck5hbWVzID0gW1xuICAgICAgJ0Z1bGx5IGNvbm5lY3RlZCcsICdSZUxVJywgJ0NvbnZvbHV0aW9uJywgJ01heCBwb29sJywgJ1Jlc2hhcGUnLCAnRmxhdHRlbidcbiAgICBdO1xuICAgIHRoaXMuaW5wdXRTaGFwZSA9IGlucHV0U2hhcGU7XG4gICAgdGhpcy5idWlsZFBhcmFtc1VJKCdGdWxseSBjb25uZWN0ZWQnLCB0aGlzLmlucHV0U2hhcGUpO1xuXG4gICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcuZHJvcGRvd24tY29udGVudCcpIS5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgICdpcm9uLWFjdGl2YXRlJywgKGV2ZW50OiBhbnkpID0+IHtcbiAgICAgICAgICB0aGlzLmJ1aWxkUGFyYW1zVUkoXG4gICAgICAgICAgICAgIGV2ZW50LmRldGFpbC5zZWxlY3RlZCBhcyBMYXllck5hbWUsIHRoaXMuaW5wdXRTaGFwZSk7XG4gICAgICAgIH0pO1xuXG4gICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjcmVtb3ZlLWxheWVyJykhLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGV2ZW50KSA9PiB7XG4gICAgICBtb2RlbEJ1aWxkZXIucmVtb3ZlTGF5ZXIodGhpcyk7XG4gICAgfSk7XG4gIH1cblxuICBzZXRJbnB1dFNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgICB0aGlzLmlucHV0U2hhcGUgPSBzaGFwZTtcbiAgICB0aGlzLmlucHV0U2hhcGVEaXNwbGF5ID1cbiAgICAgICAgbW9kZWxfYnVpbGRlcl91dGlsLmdldERpc3BsYXlTaGFwZSh0aGlzLmlucHV0U2hhcGUpO1xuXG4gICAgY29uc3QgZXJyb3JzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGNvbnN0IHZhbGlkYXRpb25FcnJvcnMgPSB0aGlzLmxheWVyQnVpbGRlci52YWxpZGF0ZSh0aGlzLmlucHV0U2hhcGUpO1xuICAgIGlmICh2YWxpZGF0aW9uRXJyb3JzICE9IG51bGwpIHtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsaWRhdGlvbkVycm9ycy5sZW5ndGg7IGkrKykge1xuICAgICAgICBlcnJvcnMucHVzaCgnRXJyb3I6ICcgKyB2YWxpZGF0aW9uRXJyb3JzW2ldKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgdGhpcy5vdXRwdXRTaGFwZSA9IHRoaXMubGF5ZXJCdWlsZGVyLmdldE91dHB1dFNoYXBlKHRoaXMuaW5wdXRTaGFwZSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgZXJyb3JzLnB1c2goZSk7XG4gICAgfVxuICAgIHRoaXMub3V0cHV0U2hhcGVEaXNwbGF5ID1cbiAgICAgICAgbW9kZWxfYnVpbGRlcl91dGlsLmdldERpc3BsYXlTaGFwZSh0aGlzLm91dHB1dFNoYXBlKTtcblxuICAgIGlmIChlcnJvcnMubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5oYXNFcnJvciA9IHRydWU7XG4gICAgICB0aGlzLmVycm9yTWVzc2FnZXMgPSBlcnJvcnM7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaGFzRXJyb3IgPSBmYWxzZTtcbiAgICAgIHRoaXMuZXJyb3JNZXNzYWdlcyA9IFtdO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLm91dHB1dFNoYXBlO1xuICB9XG5cbiAgaXNWYWxpZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gIXRoaXMuaGFzRXJyb3I7XG4gIH1cblxuICBnZXRPdXRwdXRTaGFwZSgpOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIHRoaXMub3V0cHV0U2hhcGU7XG4gIH1cblxuICBhZGRMYXllcihcbiAgICAgIGc6IEdyYXBoLCBuZXR3b3JrOiBUZW5zb3IsIGluZGV4OiBudW1iZXIsXG4gICAgICB3ZWlnaHRzOiBMYXllcldlaWdodHNEaWN0fG51bGwpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmxheWVyQnVpbGRlci5hZGRMYXllcihcbiAgICAgICAgZywgbmV0d29yaywgdGhpcy5pbnB1dFNoYXBlLCBpbmRleCwgd2VpZ2h0cyk7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgcGFyYW1ldGVycyBmb3IgdGhlIFVJIGZvciBhIGdpdmVuIG9wIHR5cGUuIFRoaXMgaXMgY2FsbGVkIHdoZW4gdGhlXG4gICAqIG9wIGlzIGFkZGVkLCBhbmQgd2hlbiB0aGUgb3AgdHlwZSBjaGFuZ2VzLlxuICAgKi9cbiAgYnVpbGRQYXJhbXNVSShcbiAgICAgIGxheWVyTmFtZTogTGF5ZXJOYW1lLCBpbnB1dFNoYXBlOiBudW1iZXJbXSxcbiAgICAgIGxheWVyQnVpbGRlckpzb24/OiBMYXllckJ1aWxkZXIpIHtcbiAgICB0aGlzLnNlbGVjdGVkTGF5ZXJOYW1lID0gbGF5ZXJOYW1lO1xuXG4gICAgdGhpcy5sYXllckJ1aWxkZXIgPVxuICAgICAgICBsYXllcl9idWlsZGVyLmdldExheWVyQnVpbGRlcihsYXllck5hbWUsIGxheWVyQnVpbGRlckpzb24pO1xuXG4gICAgLy8gQ2xlYXIgYW55IGV4aXN0aW5nIHBhcmFtZXRlcnMuXG4gICAgdGhpcy5wYXJhbUNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcblxuICAgIC8vIEFkZCBhbGwgdGhlIHBhcmFtZXRlcnMgdG8gdGhlIFVJLlxuICAgIGNvbnN0IGxheWVyUGFyYW1zID0gdGhpcy5sYXllckJ1aWxkZXIuZ2V0TGF5ZXJQYXJhbXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxheWVyUGFyYW1zLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBpbml0aWFsVmFsdWUgPSBsYXllckJ1aWxkZXJKc29uICE9IG51bGwgP1xuICAgICAgICAgIGxheWVyUGFyYW1zW2ldLmdldFZhbHVlKCkgOlxuICAgICAgICAgIGxheWVyUGFyYW1zW2ldLmluaXRpYWxWYWx1ZShpbnB1dFNoYXBlKTtcbiAgICAgIHRoaXMuYWRkUGFyYW1GaWVsZChcbiAgICAgICAgICBsYXllclBhcmFtc1tpXS5sYWJlbCwgaW5pdGlhbFZhbHVlLCBsYXllclBhcmFtc1tpXS5zZXRWYWx1ZSxcbiAgICAgICAgICBsYXllclBhcmFtc1tpXS50eXBlLCBsYXllclBhcmFtc1tpXS5taW4sIGxheWVyUGFyYW1zW2ldLm1heCk7XG4gICAgfVxuICAgIHRoaXMubW9kZWxCdWlsZGVyLmxheWVyUGFyYW1DaGFuZ2VkKCk7XG4gIH1cblxuICBsb2FkUGFyYW1zRnJvbUxheWVyQnVpbGRlcihcbiAgICAgIGlucHV0U2hhcGU6IG51bWJlcltdLCBsYXllckJ1aWxkZXJKc29uOiBMYXllckJ1aWxkZXIpIHtcbiAgICB0aGlzLmJ1aWxkUGFyYW1zVUkoXG4gICAgICAgIGxheWVyQnVpbGRlckpzb24ubGF5ZXJOYW1lLCBpbnB1dFNoYXBlLCBsYXllckJ1aWxkZXJKc29uKTtcbiAgfVxuXG4gIHByaXZhdGUgYWRkUGFyYW1GaWVsZChcbiAgICAgIGxhYmVsOiBzdHJpbmcsIGluaXRpYWxWYWx1ZTogbnVtYmVyfHN0cmluZyxcbiAgICAgIHNldFZhbHVlOiAodmFsdWU6IG51bWJlcnxzdHJpbmcpID0+IHZvaWQsIHR5cGU6ICdudW1iZXInfCd0ZXh0JyxcbiAgICAgIG1pbj86IG51bWJlciwgbWF4PzogbnVtYmVyKSB7XG4gICAgY29uc3QgaW5wdXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdwYXBlci1pbnB1dCcpO1xuICAgIGlucHV0LnNldEF0dHJpYnV0ZSgnYWx3YXlzLWZsb2F0LWxhYmVsJywgJ3RydWUnKTtcbiAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ2xhYmVsJywgbGFiZWwpO1xuICAgIGlucHV0LnNldEF0dHJpYnV0ZSgndmFsdWUnLCAnJyArIGluaXRpYWxWYWx1ZSk7XG4gICAgaW5wdXQuc2V0QXR0cmlidXRlKCd0eXBlJywgdHlwZSk7XG4gICAgaWYgKHR5cGUgPT09ICdudW1iZXInKSB7XG4gICAgICBpbnB1dC5zZXRBdHRyaWJ1dGUoJ21pbicsICcnICsgbWluKTtcbiAgICAgIGlucHV0LnNldEF0dHJpYnV0ZSgnbWF4JywgJycgKyBtYXgpO1xuICAgIH1cbiAgICBpbnB1dC5jbGFzc05hbWUgPSAncGFyYW0taW5wdXQnO1xuICAgIHRoaXMucGFyYW1Db250YWluZXIuYXBwZW5kQ2hpbGQoaW5wdXQpO1xuXG4gICAgLy8gVXBkYXRlIHRoZSBwYXJlbnQgd2hlbiB0aGlzIGNoYW5nZXMuXG4gICAgaW5wdXQuYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCAoZXZlbnQpID0+IHtcbiAgICAgIGlmICh0eXBlID09PSAnbnVtYmVyJykge1xuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHNldFZhbHVlKChldmVudC50YXJnZXQgYXMgYW55KS52YWx1ZUFzTnVtYmVyIGFzIG51bWJlcik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHNldFZhbHVlKChldmVudC50YXJnZXQgYXMgYW55KS52YWx1ZSBhcyBzdHJpbmcpO1xuICAgICAgfVxuICAgICAgdGhpcy5tb2RlbEJ1aWxkZXIubGF5ZXJQYXJhbUNoYW5nZWQoKTtcbiAgICB9KTtcbiAgICBzZXRWYWx1ZShpbml0aWFsVmFsdWUpO1xuICB9XG59XG5cbmRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChNb2RlbExheWVyLnByb3RvdHlwZS5pcywgTW9kZWxMYXllcik7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREaXNwbGF5U2hhcGUoc2hhcGU6IG51bWJlcltdKSB7XG4gIHJldHVybiAnWycgKyBzaGFwZSArICddJztcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7Q29udm9sdXRpb24yRExheWVyQnVpbGRlciwgTGF5ZXJCdWlsZGVyLCBNYXhQb29sTGF5ZXJCdWlsZGVyfSBmcm9tICcuL2xheWVyX2J1aWxkZXInO1xuXG5leHBvcnQgZW51bSBOb3JtYWxpemF0aW9uIHtcbiAgTk9STUFMSVpBVElPTl9ORUdBVElWRV9PTkVfVE9fT05FLFxuICBOT1JNQUxJWkFUSU9OX1pFUk9fVE9fT05FLFxuICBOT1JNQUxJWkFUSU9OX05PTkVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlUHl0aG9uKFxuICAgIGRhdGFzZXROYW1lOiBzdHJpbmcsIG5vcm1hbGl6YXRpb25TdHJhdGVneTogbnVtYmVyLCBpbnB1dFNoYXBlOiBudW1iZXJbXSxcbiAgICBtb2RlbExheWVyczogTGF5ZXJCdWlsZGVyW10pOiBzdHJpbmcge1xuICBjb25zdCBsb2FkRGF0YSA9IGdlbmVyYXRlTG9hZERhdGEoZGF0YXNldE5hbWUsIG5vcm1hbGl6YXRpb25TdHJhdGVneSk7XG4gIGNvbnN0IGJ1aWxkTW9kZWwgPSBnZW5lcmF0ZUJ1aWxkTW9kZWwoaW5wdXRTaGFwZSwgbW9kZWxMYXllcnMpO1xuICBjb25zdCBjYXB0dXJlV2VpZ2h0cyA9IGdlbmVyYXRlQ2FwdHVyZVdlaWdodHMobW9kZWxMYXllcnMpO1xuICByZXR1cm4gW2xvYWREYXRhLCBidWlsZE1vZGVsLCBjYXB0dXJlV2VpZ2h0c10uam9pbignXFxuXFxuJyk7XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlTG9hZERhdGEoXG4gICAgZGF0YXNldE5hbWU6IHN0cmluZywgbm9ybWFsaXphdGlvblN0cmF0ZWd5OiBudW1iZXIpOiBzdHJpbmcge1xuICBsZXQgbG9hZEZ1bmN0aW9uOiBzdHJpbmc7XG4gIHN3aXRjaCAoZGF0YXNldE5hbWUpIHtcbiAgICBjYXNlICdDSUZBUiAxMCc6IHtcbiAgICAgIGxvYWRGdW5jdGlvbiA9ICdjaWZhcjEwJztcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBjYXNlICdNTklTVCc6IHtcbiAgICAgIGxvYWRGdW5jdGlvbiA9ICdtbmlzdCc7XG4gICAgICBicmVhaztcbiAgICB9XG4gICAgZGVmYXVsdDoge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdkYXRhc2V0TmFtZSBtdXN0IGJlIFxcJ0NJRkFSIDEwXFwnIG9yIFxcJ01OSVNUXFwnJyk7XG4gICAgfVxuICB9XG5cbiAgbGV0IG5vcm1TdHJpbmc6IHN0cmluZztcbiAgc3dpdGNoIChub3JtYWxpemF0aW9uU3RyYXRlZ3kpIHtcbiAgICBjYXNlIE5vcm1hbGl6YXRpb24uTk9STUFMSVpBVElPTl9ORUdBVElWRV9PTkVfVE9fT05FOiB7XG4gICAgICBub3JtU3RyaW5nID0gJ05PUk1BTElaQVRJT05fTkVHQVRJVkVfT05FX1RPX09ORSc7XG4gICAgICBicmVhaztcbiAgICB9XG4gICAgY2FzZSBOb3JtYWxpemF0aW9uLk5PUk1BTElaQVRJT05fWkVST19UT19PTkU6IHtcbiAgICAgIG5vcm1TdHJpbmcgPSAnTk9STUFMSVpBVElPTl9aRVJPX1RPX09ORSc7XG4gICAgICBicmVhaztcbiAgICB9XG4gICAgY2FzZSBOb3JtYWxpemF0aW9uLk5PUk1BTElaQVRJT05fTk9ORToge1xuICAgICAgbm9ybVN0cmluZyA9ICdOT1JNQUxJWkFUSU9OX05PTkUnO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGRlZmF1bHQ6IHsgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkIG5vcm1hbGl6YXRpb25TdHJhdGVneSB2YWx1ZScpOyB9XG4gIH1cblxuICByZXR1cm4gYGRlZiBsb2FkX2RhdGEoKTpcbiAgcmV0dXJuIGxlYXJuanNfY29sYWIubG9hZF8ke2xvYWRGdW5jdGlvbn0obGVhcm5qc19jb2xhYi4ke25vcm1TdHJpbmd9KVxuYDtcbn1cblxuZnVuY3Rpb24gZ2VuZXJhdGVCdWlsZE1vZGVsTGF5ZXIoXG4gICAgbGF5ZXJJbmRleDogbnVtYmVyLCBpbnB1dFNoYXBlOiBudW1iZXJbXSwgbGF5ZXI6IExheWVyQnVpbGRlcik6IHN0cmluZyB7XG4gIGxldCBzcmMgPSAnJztcbiAgY29uc3QgVyA9ICdXXycgKyBsYXllckluZGV4O1xuICBjb25zdCBiID0gJ2JfJyArIGxheWVySW5kZXg7XG4gIGNvbnN0IG91dHB1dFNoYXBlID0gbGF5ZXIuZ2V0T3V0cHV0U2hhcGUoaW5wdXRTaGFwZSk7XG4gIHN3aXRjaCAobGF5ZXIubGF5ZXJOYW1lKSB7XG4gICAgY2FzZSAnRnVsbHkgY29ubmVjdGVkJzoge1xuICAgICAgY29uc3Qgc2hhcGUgPSBbaW5wdXRTaGFwZVswXSwgb3V0cHV0U2hhcGVdLmpvaW4oJywgJyk7XG5cbiAgICAgIHNyYyA9IGAgICR7V30gPSB0Zi5WYXJpYWJsZSh0Zi50cnVuY2F0ZWRfbm9ybWFsKFske3NoYXBlfV0sXG4gICAgICAgICAgICAgICAgICAgIHN0ZGRldiA9IDEuMCAvIG1hdGguc3FydCgke291dHB1dFNoYXBlWzBdfSkpKVxuICAke2J9ID0gdGYuVmFyaWFibGUodGYudHJ1bmNhdGVkX25vcm1hbChbJHtvdXRwdXRTaGFwZVswXX1dLCBzdGRkZXYgPSAwLjEpKVxuICBsYXllcnMuYXBwZW5kKHsgJ3gnOiBsYXllcnNbLTFdWyd5J10sXG4gICAgICAgICAgICAgICAgICAnVyc6ICR7V30sXG4gICAgICAgICAgICAgICAgICAnYic6ICR7Yn0sXG4gICAgICAgICAgICAgICAgICAneSc6IHRmLmFkZCh0Zi5tYXRtdWwobGF5ZXJzWy0xXVsneSddLCAke1d9KSwgJHtifSkgfSlgO1xuICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgY2FzZSAnUmVMVSc6IHtcbiAgICAgIHNyYyA9IGAgIGxheWVycy5hcHBlbmQoeyAneCc6IGxheWVyc1stMV1bJ3knXSxcbiAgICAgICAgICAgICAgICAgICd5JzogdGYubm4ucmVsdShsYXllcnNbLTFdWyd5J10pIH0pYDtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGNhc2UgJ0NvbnZvbHV0aW9uJzoge1xuICAgICAgY29uc3QgY29udiA9IGxheWVyIGFzIENvbnZvbHV0aW9uMkRMYXllckJ1aWxkZXI7XG4gICAgICBjb25zdCBmID0gY29udi5maWVsZFNpemU7XG4gICAgICBjb25zdCBkMSA9IGlucHV0U2hhcGVbaW5wdXRTaGFwZS5sZW5ndGggLSAxXTtcbiAgICAgIGNvbnN0IGQyID0gb3V0cHV0U2hhcGVbb3V0cHV0U2hhcGUubGVuZ3RoIC0gMV07XG4gICAgICBjb25zdCB3U2hhcGUgPSAnWycgKyBmICsgJywgJyArIGYgKyAnLCAnICsgZDEgKyAnLCAnICsgZDIgKyAnXSc7XG4gICAgICBjb25zdCBzdHJpZGUgPSAnWzEsICcgKyBjb252LnN0cmlkZSArICcsICcgKyBjb252LnN0cmlkZSArICcsIDFdJztcbiAgICAgIHNyYyA9IGAgICR7V30gPSB0Zi5WYXJpYWJsZSh0Zi50cnVuY2F0ZWRfbm9ybWFsKCR7d1NoYXBlfSwgc3RkZGV2ID0gMC4xKSlcbiAgJHtifSA9IHRmLlZhcmlhYmxlKHRmLnRydW5jYXRlZF9ub3JtYWwoWyR7ZDJ9XSwgc3RkZGV2ID0gMC4xKSlcbiAgbGF5ZXJzLmFwcGVuZCh7ICd4JzogbGF5ZXJzWy0xXVsneSddLFxuICAgICAgICAgICAgICAgICAgJ1cnOiAke1d9LFxuICAgICAgICAgICAgICAgICAgJ2InOiAke2J9LFxuICAgICAgICAgICAgICAgICAgJ3knOiB0Zi5hZGQodGYubm4uY29udjJkKGxheWVyc1stMV1bJ3knXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAke1d9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmlkZXMgPSAke3N0cmlkZX0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFkZGluZyA9ICdTQU1FJyksICR7Yn0pIH0pYDtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGNhc2UgJ01heCBwb29sJzoge1xuICAgICAgY29uc3QgbXAgPSBsYXllciBhcyBNYXhQb29sTGF5ZXJCdWlsZGVyO1xuICAgICAgY29uc3QgZmllbGQgPSAnWzEsICcgKyBtcC5maWVsZFNpemUgKyAnLCAnICsgbXAuZmllbGRTaXplICsgJywgMV0nO1xuICAgICAgY29uc3Qgc3RyaWRlID0gJ1sxLCAnICsgbXAuc3RyaWRlICsgJywgJyArIG1wLnN0cmlkZSArICcsIDFdJztcbiAgICAgIHNyYyA9IGAgIGxheWVycy5hcHBlbmQoeyAneCc6IGxheWVyc1stMV1bJ3knXSxcbiAgICAgICAgICAgICAgICAgICd5JzogdGYubm4ubWF4X3Bvb2wobGF5ZXJzWy0xXVsneSddLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAke2ZpZWxkfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHtzdHJpZGV9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWRkaW5nID0gJ1NBTUUnKSB9KWA7XG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBjYXNlICdSZXNoYXBlJzoge1xuICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgY2FzZSAnRmxhdHRlbic6IHtcbiAgICAgIHNyYyA9IGAgIGxheWVycy5hcHBlbmQoeyAneCc6IGxheWVyc1stMV1bJ3knXSxcbiAgICAgICAgICAgICAgICAgICd5JzogdGYucmVzaGFwZShsYXllcnNbLTFdWyd5J10sIFstMSwgJHtvdXRwdXRTaGFwZVswXX1dKSB9KWA7XG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBkZWZhdWx0OiB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Vua25vd24gbGF5ZXIgdHlwZSBcXCcnICsgbGF5ZXIubGF5ZXJOYW1lICsgJ1xcJycpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzcmM7XG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlQnVpbGRNb2RlbChcbiAgICBpbnB1dFNoYXBlOiBudW1iZXJbXSwgbW9kZWxMYXllcnM6IExheWVyQnVpbGRlcltdKTogc3RyaW5nIHtcbiAgY29uc3QgaW5wdXRTaGFwZVN0ciA9IGlucHV0U2hhcGUuam9pbignLCAnKTtcbiAgY29uc3Qgc291cmNlczogc3RyaW5nW10gPSBbXTtcblxuICBzb3VyY2VzLnB1c2goYGRlZiBidWlsZF9tb2RlbCgpOlxuICBsYXllcnMgPSBbXVxuXG4gIGxheWVycy5hcHBlbmQoeyAneSc6IHRmLnBsYWNlaG9sZGVyKHRmLmZsb2F0MzIsIFtOb25lLCAke2lucHV0U2hhcGVTdHJ9XSksXG4gICAgICAgICAgICAgICAgICAneV9sYWJlbCc6IHRmLnBsYWNlaG9sZGVyKHRmLmZsb2F0MzIsIFtOb25lLCAxMF0pIH0pYCk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtb2RlbExheWVycy5sZW5ndGg7ICsraSkge1xuICAgIHNvdXJjZXMucHVzaChnZW5lcmF0ZUJ1aWxkTW9kZWxMYXllcihpICsgMSwgaW5wdXRTaGFwZSwgbW9kZWxMYXllcnNbaV0pKTtcbiAgICBpbnB1dFNoYXBlID0gbW9kZWxMYXllcnNbaV0uZ2V0T3V0cHV0U2hhcGUoaW5wdXRTaGFwZSk7XG4gIH1cblxuICBzb3VyY2VzLnB1c2goJyAgcmV0dXJuIGxheWVyc1xcbicpO1xuICByZXR1cm4gc291cmNlcy5qb2luKCdcXG5cXG4nKTtcbn1cblxuZnVuY3Rpb24gZ2VuZXJhdGVDYXB0dXJlV2VpZ2h0cyhtb2RlbExheWVyczogTGF5ZXJCdWlsZGVyW10pOiBzdHJpbmcge1xuICBjb25zdCBzb3VyY2VzOiBzdHJpbmdbXSA9IFtdO1xuICBzb3VyY2VzLnB1c2goYGRlZiBjYXB0dXJlX3dlaWdodHMoKTpcbiAgd2VpZ2h0cyA9IFtdYCk7XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtb2RlbExheWVycy5sZW5ndGg7ICsraSkge1xuICAgIGNvbnN0IGxheWVyID0gbW9kZWxMYXllcnNbaV07XG4gICAgY29uc3QgaW5kZXggPSBpICsgMTtcbiAgICBsZXQgc3JjID0gJyc7XG4gICAgY29uc3QgVyA9ICdcXCdXXFwnOiBtb2RlbFsnICsgaW5kZXggKyAnXVtcXCdXXFwnXSc7XG4gICAgY29uc3QgYiA9ICdcXCdiXFwnOiBtb2RlbFsnICsgaW5kZXggKyAnXVtcXCdiXFwnXSc7XG4gICAgc3dpdGNoIChsYXllci5sYXllck5hbWUpIHtcbiAgICAgIGNhc2UgJ0Z1bGx5IGNvbm5lY3RlZCc6IHtcbiAgICAgICAgc3JjID0gYCAgd2VpZ2h0cy5hcHBlbmQoeyAke1d9LmV2YWwoKS5mbGF0dGVuKCkudG9saXN0KCksXG4gICAgICAgICAgICAgICAgICAgJHtifS5ldmFsKCkuZmxhdHRlbigpLnRvbGlzdCgpIH0pYDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIGNhc2UgJ0NvbnZvbHV0aW9uJzoge1xuICAgICAgICBzcmMgPSBgICB3ZWlnaHRzLmFwcGVuZCh7ICR7V30uZXZhbCgpLnRyYW5zcG9zZSgpLmZsYXR0ZW4oKS50b2xpc3QoKSxcbiAgICAgICAgICAgICAgICAgICAke2J9LmV2YWwoKS5mbGF0dGVuKCkudG9saXN0KCkgfSlgO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgZGVmYXVsdDogeyBzcmMgPSAnICB3ZWlnaHRzLmFwcGVuZCh7fSknOyB9XG4gICAgfVxuXG4gICAgc3JjICs9ICcgIyAnICsgbGF5ZXIubGF5ZXJOYW1lO1xuICAgIHNvdXJjZXMucHVzaChzcmMpO1xuICB9XG5cbiAgc291cmNlcy5wdXNoKCcgIHJldHVybiB3ZWlnaHRzJyk7XG4gIHJldHVybiBzb3VyY2VzLmpvaW4oJ1xcbicpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tdW51c2VkLXZhcmlhYmxlXG5pbXBvcnQge0FycmF5M0R9IGZyb20gJy4uL3NyYy9tYXRoL25kYXJyYXknO1xuXG5pbXBvcnQge1BvbHltZXJFbGVtZW50LCBQb2x5bWVySFRNTEVsZW1lbnR9IGZyb20gJy4vcG9seW1lci1zcGVjJztcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lXG5leHBvcnQgbGV0IE5EQXJyYXlJbWFnZVZpc3VhbGl6ZXJQb2x5bWVyID1cbiAgICBQb2x5bWVyRWxlbWVudCh7aXM6ICduZGFycmF5LWltYWdlLXZpc3VhbGl6ZXInLCBwcm9wZXJ0aWVzOiB7fX0pO1xuXG5leHBvcnQgY2xhc3MgTkRBcnJheUltYWdlVmlzdWFsaXplciBleHRlbmRzIE5EQXJyYXlJbWFnZVZpc3VhbGl6ZXJQb2x5bWVyIHtcbiAgcHJpdmF0ZSBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50O1xuICBwcml2YXRlIGNhbnZhc0NvbnRleHQ6IENhbnZhc1JlbmRlcmluZ0NvbnRleHQyRDtcbiAgcHJpdmF0ZSBpbWFnZURhdGE6IEltYWdlRGF0YTtcblxuICByZWFkeSgpIHtcbiAgICB0aGlzLmNhbnZhcyA9IHRoaXMucXVlcnlTZWxlY3RvcignI2NhbnZhcycpIGFzIEhUTUxDYW52YXNFbGVtZW50O1xuICAgIHRoaXMuY2FudmFzLndpZHRoID0gMDtcbiAgICB0aGlzLmNhbnZhcy5oZWlnaHQgPSAwO1xuICAgIHRoaXMuY2FudmFzQ29udGV4dCA9XG4gICAgICAgIHRoaXMuY2FudmFzLmdldENvbnRleHQoJzJkJykgYXMgQ2FudmFzUmVuZGVyaW5nQ29udGV4dDJEO1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gIH1cblxuICBzZXRTaGFwZShzaGFwZTogbnVtYmVyW10pIHtcbiAgICB0aGlzLmNhbnZhcy53aWR0aCA9IHNoYXBlWzFdO1xuICAgIHRoaXMuY2FudmFzLmhlaWdodCA9IHNoYXBlWzBdO1xuICB9XG5cbiAgc2V0U2l6ZSh3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLndpZHRoID0gd2lkdGggKyAncHgnO1xuICAgIHRoaXMuY2FudmFzLnN0eWxlLmhlaWdodCA9IGhlaWdodCArICdweCc7XG4gIH1cblxuICBzYXZlSW1hZ2VEYXRhRnJvbU5EQXJyYXkobmRhcnJheTogQXJyYXkzRCkge1xuICAgIHRoaXMuaW1hZ2VEYXRhID0gdGhpcy5jYW52YXNDb250ZXh0LmNyZWF0ZUltYWdlRGF0YShcbiAgICAgICAgdGhpcy5jYW52YXMud2lkdGgsIHRoaXMuY2FudmFzLmhlaWdodCk7XG4gICAgaWYgKG5kYXJyYXkuc2hhcGVbMl0gPT09IDEpIHtcbiAgICAgIHRoaXMuZHJhd0dyYXlzY2FsZUltYWdlRGF0YShuZGFycmF5KTtcbiAgICB9IGVsc2UgaWYgKG5kYXJyYXkuc2hhcGVbMl0gPT09IDMpIHtcbiAgICAgIHRoaXMuZHJhd1JHQkltYWdlRGF0YShuZGFycmF5KTtcbiAgICB9XG4gIH1cblxuICBkcmF3UkdCSW1hZ2VEYXRhKG5kYXJyYXk6IEFycmF5M0QpIHtcbiAgICBsZXQgcGl4ZWxPZmZzZXQgPSAwO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmRhcnJheS5zaGFwZVswXTsgaSsrKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IG5kYXJyYXkuc2hhcGVbMV07IGorKykge1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gbmRhcnJheS5nZXQoaSwgaiwgMCk7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSBuZGFycmF5LmdldChpLCBqLCAxKTtcbiAgICAgICAgdGhpcy5pbWFnZURhdGEuZGF0YVtwaXhlbE9mZnNldCsrXSA9IG5kYXJyYXkuZ2V0KGksIGosIDIpO1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gMjU1O1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGRyYXdHcmF5c2NhbGVJbWFnZURhdGEobmRhcnJheTogQXJyYXkzRCkge1xuICAgIGxldCBwaXhlbE9mZnNldCA9IDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZGFycmF5LnNoYXBlWzBdOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgbmRhcnJheS5zaGFwZVsxXTsgaisrKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gbmRhcnJheS5nZXQoaSwgaiwgMCk7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSB2YWx1ZTtcbiAgICAgICAgdGhpcy5pbWFnZURhdGEuZGF0YVtwaXhlbE9mZnNldCsrXSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmltYWdlRGF0YS5kYXRhW3BpeGVsT2Zmc2V0KytdID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaW1hZ2VEYXRhLmRhdGFbcGl4ZWxPZmZzZXQrK10gPSAyNTU7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgZHJhdygpIHtcbiAgICB0aGlzLmNhbnZhcy5zdHlsZS5kaXNwbGF5ID0gJyc7XG4gICAgdGhpcy5jYW52YXNDb250ZXh0LnB1dEltYWdlRGF0YSh0aGlzLmltYWdlRGF0YSwgMCwgMCk7XG4gIH1cbn1cbmRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChcbiAgICBOREFycmF5SW1hZ2VWaXN1YWxpemVyLnByb3RvdHlwZS5pcywgTkRBcnJheUltYWdlVmlzdWFsaXplcik7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby11bnVzZWQtdmFyaWFibGVcbmltcG9ydCB7TkRBcnJheU1hdGhDUFV9IGZyb20gJy4uL3NyYy9tYXRoL21hdGhfY3B1JztcbmltcG9ydCB7QXJyYXkxRH0gZnJvbSAnLi4vc3JjL21hdGgvbmRhcnJheSc7XG5cbmltcG9ydCB7UG9seW1lckVsZW1lbnQsIFBvbHltZXJIVE1MRWxlbWVudH0gZnJvbSAnLi9wb2x5bWVyLXNwZWMnO1xuXG5jb25zdCBUT1BfSyA9IDM7XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZVxuZXhwb3J0IGxldCBOREFycmF5TG9naXRzVmlzdWFsaXplclBvbHltZXIgPVxuICAgIFBvbHltZXJFbGVtZW50KHtpczogJ25kYXJyYXktbG9naXRzLXZpc3VhbGl6ZXInLCBwcm9wZXJ0aWVzOiB7fX0pO1xuXG5leHBvcnQgY2xhc3MgTkRBcnJheUxvZ2l0c1Zpc3VhbGl6ZXIgZXh0ZW5kcyBOREFycmF5TG9naXRzVmlzdWFsaXplclBvbHltZXIge1xuICBwcml2YXRlIGxvZ2l0TGFiZWxFbGVtZW50czogSFRNTEVsZW1lbnRbXTtcbiAgcHJpdmF0ZSBsb2dpdFZpekVsZW1lbnRzOiBIVE1MRWxlbWVudFtdO1xuICBwcml2YXRlIHdpZHRoOiBudW1iZXI7XG5cbiAgaW5pdGlhbGl6ZSh3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMud2lkdGggPSB3aWR0aDtcbiAgICB0aGlzLmxvZ2l0TGFiZWxFbGVtZW50cyA9IFtdO1xuICAgIHRoaXMubG9naXRWaXpFbGVtZW50cyA9IFtdO1xuICAgIGNvbnN0IGNvbnRhaW5lciA9IHRoaXMucXVlcnlTZWxlY3RvcignLmxvZ2l0cy1jb250YWluZXInKSBhcyBIVE1MRWxlbWVudDtcbiAgICBjb250YWluZXIuc3R5bGUuaGVpZ2h0ID0gaGVpZ2h0ICsgJ3B4JztcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgVE9QX0s7IGkrKykge1xuICAgICAgY29uc3QgbG9naXRDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIGxvZ2l0Q29udGFpbmVyLnN0eWxlLmhlaWdodCA9IGhlaWdodCAvIChUT1BfSyArIDEpICsgJ3B4JztcbiAgICAgIGxvZ2l0Q29udGFpbmVyLnN0eWxlLm1hcmdpbiA9XG4gICAgICAgICAgaGVpZ2h0IC8gKCgyICogVE9QX0spICogKFRPUF9LICsgMSkpICsgJ3B4IDAnO1xuICAgICAgbG9naXRDb250YWluZXIuY2xhc3NOYW1lID1cbiAgICAgICAgICAnc2luZ2xlLWxvZ2l0LWNvbnRhaW5lciBuZGFycmF5LWxvZ2l0cy12aXN1YWxpemVyJztcblxuICAgICAgY29uc3QgbG9naXRMYWJlbEVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIGxvZ2l0TGFiZWxFbGVtZW50LmNsYXNzTmFtZSA9ICdsb2dpdC1sYWJlbCBuZGFycmF5LWxvZ2l0cy12aXN1YWxpemVyJztcbiAgICAgIHRoaXMubG9naXRMYWJlbEVsZW1lbnRzLnB1c2gobG9naXRMYWJlbEVsZW1lbnQpO1xuXG4gICAgICBjb25zdCBsb2dpdFZpek91dGVyRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgbG9naXRWaXpPdXRlckVsZW1lbnQuY2xhc3NOYW1lID1cbiAgICAgICAgICAnbG9naXQtdml6LW91dGVyIG5kYXJyYXktbG9naXRzLXZpc3VhbGl6ZXInO1xuXG4gICAgICBjb25zdCBsb2dpdFZpc0lubmVyRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgbG9naXRWaXNJbm5lckVsZW1lbnQuY2xhc3NOYW1lID1cbiAgICAgICAgICAnbG9naXQtdml6LWlubmVyIG5kYXJyYXktbG9naXRzLXZpc3VhbGl6ZXInO1xuICAgICAgbG9naXRWaXNJbm5lckVsZW1lbnQuaW5uZXJIVE1MID0gJyZuYnNwOyc7XG4gICAgICBsb2dpdFZpek91dGVyRWxlbWVudC5hcHBlbmRDaGlsZChsb2dpdFZpc0lubmVyRWxlbWVudCk7XG5cbiAgICAgIHRoaXMubG9naXRWaXpFbGVtZW50cy5wdXNoKGxvZ2l0VmlzSW5uZXJFbGVtZW50KTtcblxuICAgICAgbG9naXRDb250YWluZXIuYXBwZW5kQ2hpbGQobG9naXRMYWJlbEVsZW1lbnQpO1xuICAgICAgbG9naXRDb250YWluZXIuYXBwZW5kQ2hpbGQobG9naXRWaXpPdXRlckVsZW1lbnQpO1xuICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGxvZ2l0Q29udGFpbmVyKTtcbiAgICB9XG4gIH1cblxuICBkcmF3TG9naXRzKFxuICAgICAgcHJlZGljdGVkTG9naXRzOiBBcnJheTFELCBsYWJlbExvZ2l0czogQXJyYXkxRCxcbiAgICAgIGxhYmVsQ2xhc3NOYW1lcz86IHN0cmluZ1tdKSB7XG4gICAgY29uc3QgbWF0aENwdSA9IG5ldyBOREFycmF5TWF0aENQVSgpO1xuICAgIGNvbnN0IGxhYmVsQ2xhc3MgPSBtYXRoQ3B1LmFyZ01heChsYWJlbExvZ2l0cykuZ2V0KCk7XG5cbiAgICBjb25zdCB0b3BrID0gbWF0aENwdS50b3BLKHByZWRpY3RlZExvZ2l0cywgVE9QX0spO1xuICAgIGNvbnN0IHRvcGtJbmRpY2VzID0gdG9way5pbmRpY2VzLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHRvcGtWYWx1ZXMgPSB0b3BrLnZhbHVlcy5nZXRWYWx1ZXMoKTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdG9wa0luZGljZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGluZGV4ID0gdG9wa0luZGljZXNbaV07XG4gICAgICB0aGlzLmxvZ2l0TGFiZWxFbGVtZW50c1tpXS5pbm5lclRleHQgPVxuICAgICAgICAgIGxhYmVsQ2xhc3NOYW1lcyA/IGxhYmVsQ2xhc3NOYW1lc1tpbmRleF0gOiBpbmRleCArICcnO1xuICAgICAgdGhpcy5sb2dpdExhYmVsRWxlbWVudHNbaV0uc3R5bGUud2lkdGggPVxuICAgICAgICAgIGxhYmVsQ2xhc3NOYW1lcyAhPSBudWxsID8gJzEwMHB4JyA6ICcyMHB4JztcbiAgICAgIHRoaXMubG9naXRWaXpFbGVtZW50c1tpXS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSBpbmRleCA9PT0gbGFiZWxDbGFzcyA/XG4gICAgICAgICAgJ3JnYmEoMTIwLCAxODUsIDUwLCAuODQpJyA6XG4gICAgICAgICAgJ3JnYmEoMjIwLCAxMCwgMTAsIDAuODQpJztcbiAgICAgIHRoaXMubG9naXRWaXpFbGVtZW50c1tpXS5zdHlsZS53aWR0aCA9XG4gICAgICAgICAgTWF0aC5mbG9vcigxMDAgKiB0b3BrVmFsdWVzW2ldKSArICclJztcbiAgICAgIHRoaXMubG9naXRWaXpFbGVtZW50c1tpXS5pbm5lclRleHQgPVxuICAgICAgICAgIGAkeygxMDAgKiB0b3BrVmFsdWVzW2ldKS50b0ZpeGVkKDEpfSVgO1xuICAgIH1cbiAgfVxufVxuXG5kb2N1bWVudC5yZWdpc3RlckVsZW1lbnQoXG4gICAgTkRBcnJheUxvZ2l0c1Zpc3VhbGl6ZXIucHJvdG90eXBlLmlzLCBOREFycmF5TG9naXRzVmlzdWFsaXplcik7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbi8qKlxuICogQGZpbGVvdmVydmlld1xuICpcbiAqIERlZmluZXMgYW4gaW50ZXJmYWNlIGZvciBjcmVhdGluZyBQb2x5bWVyIGVsZW1lbnRzIGluIFR5cGVzY3JpcHQgd2l0aCB0aGVcbiAqIGNvcnJlY3QgdHlwaW5ncy4gQSBQb2x5bWVyIGVsZW1lbnQgc2hvdWxkIGJlIGRlZmluZWQgbGlrZSB0aGlzOlxuICpcbiAqIGBgYFxuICogbGV0IE15RWxlbWVudFBvbHltZXIgPSBQb2x5bWVyRWxlbWVudCh7XG4gKiAgIGlzOiAnbXktcG9seW1lci1lbGVtZW50JyxcbiAqICAgcHJvcGVydGllczoge1xuICogICAgIGZvbzogc3RyaW5nLFxuICogICAgIGJhcjogQXJyYXlcbiAqICAgfVxuICogfSk7XG4gKlxuICogY2xhc3MgTXlFbGVtZW50IGV4dGVuZHMgTXlFbGVtZW50UG9seW1lciB7XG4gKiAgIGZvbzogc3RyaW5nO1xuICogICBiYXI6IG51bWJlcltdO1xuICpcbiAqICAgcmVhZHkoKSB7XG4gKiAgICAgY29uc29sZS5sb2coJ015RWxlbWVudCBpbml0aWFsaXplZCEnKTtcbiAqICAgfVxuICogfVxuICpcbiAqIGRvY3VtZW50LnJlZ2lzdGVyRWxlbWVudChNeUVsZW1lbnQucHJvdG90eXBlLmlzLCBNeUVsZW1lbnQpO1xuICogYGBgXG4gKi9cblxuZXhwb3J0IHR5cGUgU3BlYyA9IHtcbiAgaXM6IHN0cmluZzsgcHJvcGVydGllczoge1xuICAgIFtrZXk6IHN0cmluZ106IChGdW5jdGlvbnx7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICB0eXBlOiBGdW5jdGlvbiwgdmFsdWU/OiBhbnk7XG4gICAgICByZWZsZWN0VG9BdHRyaWJ1dGU/OiBib29sZWFuO1xuICAgICAgcmVhZG9ubHk/OiBib29sZWFuO1xuICAgICAgbm90aWZ5PzogYm9vbGVhbjtcbiAgICAgIGNvbXB1dGVkPzogc3RyaW5nO1xuICAgICAgb2JzZXJ2ZXI/OiBzdHJpbmc7XG4gICAgfSlcbiAgfTtcbiAgb2JzZXJ2ZXJzPzogc3RyaW5nW107XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gUG9seW1lckVsZW1lbnQoc3BlYzogU3BlYykge1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIHJldHVybiBQb2x5bWVyLkNsYXNzKHNwZWMgYXMgYW55KSBhcyB7bmV3ICgpOiBQb2x5bWVySFRNTEVsZW1lbnR9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFBvbHltZXJIVE1MRWxlbWVudCBleHRlbmRzIEhUTUxFbGVtZW50LCBwb2x5bWVyLkJhc2Uge31cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtJbk1lbW9yeURhdGFzZXR9IGZyb20gJy4uL3NyYy9kYXRhc2V0JztcbmltcG9ydCB7QXJyYXkxRCwgTkRBcnJheX0gZnJvbSAnLi4vc3JjL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3NyYy91dGlsJztcblxuY29uc3QgUEFSU0lOR19JTUFHRV9DQU5WQVNfSEVJR0hUX1BYID0gMTAwMDtcblxuZXhwb3J0IGludGVyZmFjZSBOREFycmF5SW5mbyB7XG4gIHBhdGg6IHN0cmluZztcbiAgbmFtZTogc3RyaW5nO1xuICBkYXRhVHlwZTogJ3VpbnQ4J3wnZmxvYXQzMid8J3BuZyc7XG4gIHNoYXBlOiBudW1iZXJbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBYaHJEYXRhc2V0Q29uZmlnIHtcbiAgZGF0YTogTkRBcnJheUluZm9bXTtcblxuICBsYWJlbENsYXNzTmFtZXM/OiBzdHJpbmdbXTtcbiAgLy8gUGF0aHMgdG8gcHJlLWJ1aWx0IG1vZGVscy5cbiAgbW9kZWxDb25maWdzOiB7W21vZGVsTmFtZTogc3RyaW5nXTogWGhyTW9kZWxDb25maWd9O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFhock1vZGVsQ29uZmlnIHtcbiAgcGF0aDogc3RyaW5nO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0WGhyRGF0YXNldENvbmZpZyhqc29uQ29uZmlnUGF0aDogc3RyaW5nKTpcbiAgICBQcm9taXNlPHtbZGF0YXNldE5hbWU6IHN0cmluZ106IFhockRhdGFzZXRDb25maWd9PiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZTx7W2RhdGFzZXROYW1lOiBzdHJpbmddOiBYaHJEYXRhc2V0Q29uZmlnfT4oXG4gICAgICAocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICB4aHIub3BlbignR0VUJywganNvbkNvbmZpZ1BhdGgpO1xuXG4gICAgICAgIHhoci5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZShKU09OLnBhcnNlKFxuICAgICAgICAgICAgICB4aHIucmVzcG9uc2VUZXh0KSBhcyB7W2RhdGFzZXROYW1lOiBzdHJpbmddOiBYaHJEYXRhc2V0Q29uZmlnfSk7XG4gICAgICAgIH07XG4gICAgICAgIHhoci5vbmVycm9yID0gKGVycm9yKSA9PiB7XG4gICAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgICAgfTtcbiAgICAgICAgeGhyLnNlbmQoKTtcbiAgICAgIH0pO1xufVxuXG5leHBvcnQgY2xhc3MgWGhyRGF0YXNldCBleHRlbmRzIEluTWVtb3J5RGF0YXNldCB7XG4gIHByb3RlY3RlZCB4aHJEYXRhc2V0Q29uZmlnOiBYaHJEYXRhc2V0Q29uZmlnO1xuXG4gIGNvbnN0cnVjdG9yKHhockRhdGFzZXRDb25maWc6IFhockRhdGFzZXRDb25maWcpIHtcbiAgICBzdXBlcih4aHJEYXRhc2V0Q29uZmlnLmRhdGEubWFwKHggPT4geC5zaGFwZSkpO1xuICAgIHRoaXMueGhyRGF0YXNldENvbmZpZyA9IHhockRhdGFzZXRDb25maWc7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0TkRBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oaW5mbzogTkRBcnJheUluZm8pOiBQcm9taXNlPFRbXT4ge1xuICAgIGNvbnN0IGRhdGFQcm9taXNlID0gaW5mby5kYXRhVHlwZSA9PT0gJ3BuZycgP1xuICAgICAgICBwYXJzZVR5cGVkQXJyYXlGcm9tUG5nKGluZm8sIGluZm8uc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdKSA6XG4gICAgICAgIHBhcnNlVHlwZWRBcnJheUZyb21CaW5hcnkoaW5mbyk7XG5cbiAgICByZXR1cm4gZGF0YVByb21pc2UudGhlbihkYXRhID0+IHtcbiAgICAgIGNvbnN0IGlucHV0U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShpbmZvLnNoYXBlKTtcbiAgICAgIGNvbnN0IG5kYXJyYXlzOiBUW10gPSBbXTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGggLyBpbnB1dFNpemU7IGkrKykge1xuICAgICAgICBjb25zdCB2YWx1ZXMgPSBkYXRhLnN1YmFycmF5KGkgKiBpbnB1dFNpemUsIChpICsgMSkgKiBpbnB1dFNpemUpO1xuICAgICAgICBjb25zdCBuZGFycmF5ID1cbiAgICAgICAgICAgIE5EQXJyYXkubWFrZTxUPihpbmZvLnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcyl9KTtcbiAgICAgICAgbmRhcnJheXMucHVzaChuZGFycmF5KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBuZGFycmF5cztcbiAgICB9KTtcbiAgfVxuXG4gIGZldGNoRGF0YSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgcHJvbWlzZXMgPSB0aGlzLnhockRhdGFzZXRDb25maWcuZGF0YS5tYXAoeCA9PiB0aGlzLmdldE5EQXJyYXkoeCkpO1xuICAgICAgUHJvbWlzZS5hbGwocHJvbWlzZXMpLnRoZW4oKGRhdGE6IE5EQXJyYXlbXVtdKSA9PiB7XG4gICAgICAgIHRoaXMuZGF0YXNldCA9IGRhdGE7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG59XG5cbmZ1bmN0aW9uIHBhcnNlVHlwZWRBcnJheUZyb21CaW5hcnkoaW5mbzogTkRBcnJheUluZm8pOlxuICAgIFByb21pc2U8RmxvYXQzMkFycmF5fFVpbnQ4QXJyYXk+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlPEZsb2F0MzJBcnJheXxVaW50OEFycmF5PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XG4gICAgeGhyLm9wZW4oJ0dFVCcsIGluZm8ucGF0aCk7XG4gICAgeGhyLnJlc3BvbnNlVHlwZSA9ICdhcnJheWJ1ZmZlcic7XG4gICAgeGhyLm9ubG9hZCA9IGV2ZW50ID0+IHtcbiAgICAgIGNvbnN0IGRhdGEgPSAoaW5mby5kYXRhVHlwZSA9PT0gJ2Zsb2F0MzInKSA/XG4gICAgICAgICAgbmV3IEZsb2F0MzJBcnJheSh4aHIucmVzcG9uc2UpIDpcbiAgICAgICAgICBuZXcgVWludDhBcnJheSh4aHIucmVzcG9uc2UpO1xuICAgICAgcmVzb2x2ZShkYXRhKTtcbiAgICB9O1xuICAgIHhoci5vbmVycm9yID0gZXJyID0+IHJlamVjdChlcnIpO1xuICAgIHhoci5zZW5kKCk7XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZUdyYXlzY2FsZUltYWdlRGF0YShcbiAgICBkYXRhOiBVaW50OEFycmF5fFVpbnQ4Q2xhbXBlZEFycmF5LCByZXN1bHQ6IFVpbnQ4QXJyYXksXG4gICAgcmVzdWx0T2Zmc2V0OiBudW1iZXIpOiB2b2lkIHtcbiAgbGV0IGlkeCA9IHJlc3VsdE9mZnNldDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgaSArPSA0KSB7XG4gICAgcmVzdWx0W2lkeCsrXSA9IGRhdGFbaV07XG4gIH1cbn1cblxuZnVuY3Rpb24gcGFyc2VSR0JJbWFnZURhdGEoXG4gICAgZGF0YTogVWludDhBcnJheXxVaW50OENsYW1wZWRBcnJheSwgcmVzdWx0OiBVaW50OEFycmF5LFxuICAgIHJlc3VsdE9mZnNldDogbnVtYmVyKTogdm9pZCB7XG4gIGxldCBpZHggPSByZXN1bHRPZmZzZXQ7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZGF0YS5sZW5ndGg7IGkgKz0gNCkge1xuICAgIHJlc3VsdFtpZHhdID0gZGF0YVtpXTtcbiAgICByZXN1bHRbaWR4ICsgMV0gPSBkYXRhW2kgKyAxXTtcbiAgICByZXN1bHRbaWR4ICsgMl0gPSBkYXRhW2kgKyAyXTtcbiAgICBpZHggKz0gMztcbiAgfVxufVxuXG5mdW5jdGlvbiBwYXJzZUltYWdlKFxuICAgIGltZzogSFRNTEltYWdlRWxlbWVudCwgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IFVpbnQ4QXJyYXkge1xuICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykhO1xuICBjb25zdCBOID0gaW1nLmhlaWdodDtcbiAgY29uc3QgaW5wdXRTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHNoYXBlKTtcbiAgY29uc3QgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkoTiAqIGlucHV0U2l6ZSk7XG4gIGlmIChpbWcud2lkdGggIT09IHNoYXBlWzBdICogc2hhcGVbMV0pIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBJbWFnZSB3aWR0aCAoJHtpbWcud2lkdGh9KSBtdXN0IGJlIG11bHRpcGxlIG9mIGAgK1xuICAgICAgICBgcm93cypjb2x1bW5zICgke3NoYXBlWzBdfSoke3NoYXBlWzFdfSkgb2YgdGhlIG5kYXJyYXlgKTtcbiAgfVxuICAvLyBUT0RPKHNtaWxrb3YpOiBDYW52YXMgaGFzIG1heCB3aWR0aCBvZiAzMiw3NjdweC4gVGhpcyBhcHByb2FjaFxuICAvLyAoY2FudmFzLndpZHRoID0gc2hhcGVbMF0gKiBzaGFwZVsxXSkgd29ya3Mgd2l0aCBleGFtcGxlcyB1cCB0byAxODF4MTgxcHguXG4gIC8vIENvbnNpZGVyIGhhdmluZyB0aGUgY2FudmFzIGluIHVuLWZsYXQgZm9ybWF0LCBpLmUuXG4gIC8vIGNhbnZhcy53aWR0aCA9IHNoYXBlWzFdOyBjYW52YXMuaGVpZ2h0ID0gRFJBV19CQVRDSCAqIHNoYXBlWzBdO1xuICBjYW52YXMud2lkdGggPSBpbWcud2lkdGg7XG5cbiAgLy8gSWRlYWxseSB3ZSB3YW50IGNhbnZhcy5oZWlnaHQ9aW1nLmhlaWdodCAod2hpY2ggaXMgTiksIGJ1dCBjYW52YXMgc2l6ZSBpc1xuICAvLyBsaW1pdGVkIGJ5IHRoZSBicm93c2VyLCBzbyB3ZSBkbyBtdWx0aXBsZSBwYXNzZXMgd2l0aCBhIHNtYWxsZXIgY2FudmFzLlxuICBjYW52YXMuaGVpZ2h0ID0gUEFSU0lOR19JTUFHRV9DQU5WQVNfSEVJR0hUX1BYO1xuICBjb25zdCBzeCA9IDA7XG4gIGNvbnN0IHNXaWR0aCA9IGNhbnZhcy53aWR0aDtcbiAgbGV0IHNIZWlnaHQgPSBjYW52YXMuaGVpZ2h0O1xuICBjb25zdCBkeCA9IDA7XG4gIGNvbnN0IGR5ID0gMDtcbiAgY29uc3QgZFdpZHRoID0gc1dpZHRoO1xuICBsZXQgZEhlaWdodCA9IHNIZWlnaHQ7XG4gIGNvbnN0IGRlcHRoID0gc2hhcGVbMl07XG4gIGxldCBvZmZzZXQgPSAwO1xuICBjb25zdCBudW1QYXNzZXMgPSBNYXRoLmNlaWwoTiAvIGNhbnZhcy5oZWlnaHQpO1xuICBmb3IgKGxldCBwYXNzID0gMDsgcGFzcyA8IG51bVBhc3NlczsgKytwYXNzKSB7XG4gICAgY29uc3Qgc3kgPSBwYXNzICogY2FudmFzLmhlaWdodDtcbiAgICBpZiAoKHBhc3MgPT09IG51bVBhc3NlcyAtIDEpICYmIChOICUgY2FudmFzLmhlaWdodCA+IDApKSB7XG4gICAgICAvLyBMYXN0IHBhc3MgaXMgYSBzcGVjaWFsIGNhc2UuXG4gICAgICBjYW52YXMuaGVpZ2h0ID0gTiAlIGNhbnZhcy5oZWlnaHQ7XG4gICAgICBzSGVpZ2h0ID0gY2FudmFzLmhlaWdodDtcbiAgICAgIGRIZWlnaHQgPSBzSGVpZ2h0O1xuICAgIH1cbiAgICBjdHguZHJhd0ltYWdlKGltZywgc3gsIHN5LCBzV2lkdGgsIHNIZWlnaHQsIGR4LCBkeSwgZFdpZHRoLCBkSGVpZ2h0KTtcbiAgICBjb25zdCBkYXRhID0gY3R4LmdldEltYWdlRGF0YSgwLCAwLCBjYW52YXMud2lkdGgsIGNhbnZhcy5oZWlnaHQpLmRhdGE7XG4gICAgKGRlcHRoID09PSAxKSA/IHBhcnNlR3JheXNjYWxlSW1hZ2VEYXRhKGRhdGEsIHJlc3VsdCwgb2Zmc2V0KSA6XG4gICAgICAgICAgICAgICAgICAgIHBhcnNlUkdCSW1hZ2VEYXRhKGRhdGEsIHJlc3VsdCwgb2Zmc2V0KTtcbiAgICBvZmZzZXQgKz0gY2FudmFzLmhlaWdodCAqIGlucHV0U2l6ZTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5mdW5jdGlvbiBwYXJzZVR5cGVkQXJyYXlGcm9tUG5nKFxuICAgIGluZm86IE5EQXJyYXlJbmZvLCBzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZTxVaW50OEFycmF5PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgbGV0IGltZyA9IG5ldyBJbWFnZSgpO1xuICAgIGltZy5zZXRBdHRyaWJ1dGUoJ2Nyb3NzT3JpZ2luJywgJycpO1xuICAgIGltZy5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICBjb25zdCByZXN1bHQgPSBwYXJzZUltYWdlKGltZywgc2hhcGUpO1xuICAgICAgaW1nLnNyYyA9ICcnO1xuICAgICAgaW1nID0gbnVsbCE7XG4gICAgICByZXNvbHZlKHJlc3VsdCk7XG4gICAgfTtcbiAgICBpbWcuc3JjID0gaW5mby5wYXRoO1xuICB9KTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgaW50ZXJmYWNlIENoZWNrcG9pbnRWYXJpYWJsZSB7XG4gIGZpbGVuYW1lOiBzdHJpbmc7XG4gIHNoYXBlOiBudW1iZXJbXTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCB0eXBlIENoZWNrcG9pbnRNYW5pZmVzdCA9IHtcbiAgW3Zhck5hbWU6IHN0cmluZ106IENoZWNrcG9pbnRWYXJpYWJsZVxufTtcblxuY29uc3QgTUFOSUZFU1RfRklMRSA9ICdtYW5pZmVzdC5qc29uJztcblxuZXhwb3J0IGNsYXNzIENoZWNrcG9pbnRMb2FkZXIge1xuICBwcml2YXRlIGNoZWNrcG9pbnRNYW5pZmVzdDogQ2hlY2twb2ludE1hbmlmZXN0O1xuICBwcml2YXRlIHZhcmlhYmxlczoge1t2YXJOYW1lOiBzdHJpbmddOiBOREFycmF5fTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHVybFBhdGg6IHN0cmluZykge1xuICAgIGlmICh0aGlzLnVybFBhdGguY2hhckF0KHRoaXMudXJsUGF0aC5sZW5ndGggLSAxKSAhPT0gJy8nKSB7XG4gICAgICB0aGlzLnVybFBhdGggKz0gJy8nO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgbG9hZE1hbmlmZXN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICAgIHhoci5vcGVuKCdHRVQnLCB0aGlzLnVybFBhdGggKyBNQU5JRkVTVF9GSUxFKTtcblxuICAgICAgeGhyLm9ubG9hZCA9ICgpID0+IHtcbiAgICAgICAgdGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPSBKU09OLnBhcnNlKHhoci5yZXNwb25zZVRleHQpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9O1xuICAgICAgeGhyLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYCR7TUFOSUZFU1RfRklMRX0gbm90IGZvdW5kIGF0ICR7dGhpcy51cmxQYXRofS4gYCArIGVycm9yKTtcbiAgICAgIH07XG4gICAgICB4aHIuc2VuZCgpO1xuICAgIH0pO1xuICB9XG5cbiAgZ2V0Q2hlY2twb2ludE1hbmlmZXN0KCk6IFByb21pc2U8Q2hlY2twb2ludE1hbmlmZXN0PiB7XG4gICAgaWYgKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0ID09IG51bGwpIHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxDaGVja3BvaW50TWFuaWZlc3Q+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgdGhpcy5sb2FkTWFuaWZlc3QoKS50aGVuKCgpID0+IHtcbiAgICAgICAgICByZXNvbHZlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPENoZWNrcG9pbnRNYW5pZmVzdD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgcmVzb2x2ZSh0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRBbGxWYXJpYWJsZXMoKTogUHJvbWlzZTx7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9PiB7XG4gICAgaWYgKHRoaXMudmFyaWFibGVzICE9IG51bGwpIHtcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTx7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgIHJlc29sdmUodGhpcy52YXJpYWJsZXMpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMuZ2V0Q2hlY2twb2ludE1hbmlmZXN0KCkudGhlbihcbiAgICAgICAgICAoY2hlY2twb2ludERlZmluaXRpb246IENoZWNrcG9pbnRNYW5pZmVzdCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmFyaWFibGVOYW1lcyA9IE9iamVjdC5rZXlzKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcblxuICAgICAgICAgICAgY29uc3QgdmFyaWFibGVQcm9taXNlczogQXJyYXk8UHJvbWlzZTxOREFycmF5Pj4gPSBbXTtcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFyaWFibGVOYW1lcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICB2YXJpYWJsZVByb21pc2VzLnB1c2godGhpcy5nZXRWYXJpYWJsZSh2YXJpYWJsZU5hbWVzW2ldKSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIFByb21pc2UuYWxsKHZhcmlhYmxlUHJvbWlzZXMpLnRoZW4odmFyaWFibGVzID0+IHtcbiAgICAgICAgICAgICAgdGhpcy52YXJpYWJsZXMgPSB7fTtcbiAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YXJpYWJsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICB0aGlzLnZhcmlhYmxlc1t2YXJpYWJsZU5hbWVzW2ldXSA9IHZhcmlhYmxlc1tpXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICByZXNvbHZlKHRoaXMudmFyaWFibGVzKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgZ2V0VmFyaWFibGUodmFyTmFtZTogc3RyaW5nKTogUHJvbWlzZTxOREFycmF5PiB7XG4gICAgaWYgKCEodmFyTmFtZSBpbiB0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGxvYWQgbm9uLWV4aXN0YW50IHZhcmlhYmxlICcgKyB2YXJOYW1lKTtcbiAgICB9XG5cbiAgICBjb25zdCB2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kID1cbiAgICAgICAgKHJlc29sdmU6IChuZGFycmF5OiBOREFycmF5KSA9PiB2b2lkLCByZWplY3Q6ICgpID0+IHZvaWQpID0+IHtcbiAgICAgICAgICBjb25zdCB4aHIgPSBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcbiAgICAgICAgICB4aHIucmVzcG9uc2VUeXBlID0gJ2FycmF5YnVmZmVyJztcbiAgICAgICAgICBjb25zdCBmbmFtZSA9IHRoaXMuY2hlY2twb2ludE1hbmlmZXN0W3Zhck5hbWVdLmZpbGVuYW1lO1xuICAgICAgICAgIHhoci5vcGVuKCdHRVQnLCB0aGlzLnVybFBhdGggKyBmbmFtZSk7XG5cbiAgICAgICAgICB4aHIub25sb2FkID0gKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh4aHIucmVzcG9uc2UpO1xuICAgICAgICAgICAgY29uc3QgbmRhcnJheSA9XG4gICAgICAgICAgICAgICAgTkRBcnJheS5tYWtlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0W3Zhck5hbWVdLnNoYXBlLCB7dmFsdWVzfSk7XG4gICAgICAgICAgICByZXNvbHZlKG5kYXJyYXkpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgeGhyLm9uZXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAnQ291bGQgbm90IGZldGNoIHZhcmlhYmxlICcgKyB2YXJOYW1lICsgJzogJyArIGVycm9yKTtcbiAgICAgICAgICB9O1xuICAgICAgICAgIHhoci5zZW5kKCk7XG4gICAgICAgIH07XG5cbiAgICBpZiAodGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPE5EQXJyYXk+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgdGhpcy5sb2FkTWFuaWZlc3QoKS50aGVuKCgpID0+IHtcbiAgICAgICAgICBuZXcgUHJvbWlzZTxOREFycmF5Pih2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kKS50aGVuKHJlc29sdmUpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFByb21pc2U8TkRBcnJheT4odmFyaWFibGVSZXF1ZXN0UHJvbWlzZU1ldGhvZCk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbmNvbnN0IFNUQVRTX1NBTVBMRV9QRVJDRU5UQUdFID0gMC4xO1xuXG5leHBvcnQgaW50ZXJmYWNlIERhdGFTdGF0cyB7XG4gIGV4YW1wbGVDb3VudDogbnVtYmVyO1xuICBpbnB1dE1pbjogbnVtYmVyO1xuICBpbnB1dE1heDogbnVtYmVyO1xuICBzaGFwZTogbnVtYmVyW107XG59XG5cbmludGVyZmFjZSBOb3JtYWxpemF0aW9uSW5mbyB7XG4gIGlzTm9ybWFsaXplZDogYm9vbGVhbjtcbiAgLy8gQm91bmRzIG9mIHRoZSBub3JtYWxpemF0aW9uIGlmIG5vcm1hbGl6ZWQuXG4gIGxvd2VyQm91bmQ/OiBudW1iZXI7XG4gIHVwcGVyQm91bmQ/OiBudW1iZXI7XG4gIC8vIE1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIGZvciBlYWNoIGRpbWVuc2lvbiBvZiB0aGUgb3JpZ2luYWwgZGF0YS4gVGhlc2VcbiAgLy8gYXJlIHRoZSBzYW1lIHNpemUgYXMgYW4gaW5wdXQgZXhhbXBsZS4gVGhlc2UgYXJlIGNvbXB1dGVkIGxhemlseSwgb25seSBpZlxuICAvLyBub3JtYWxpemF0aW9uIGlzIHJlcXVlc3RlZC4gSWYgdGhlIGRhdGEgaXMgdW4tbm9ybWFsaXplZCwgdGhlc2UgYXJlIGtlcHRcbiAgLy8gYXJvdW5kIHNvIHRoZXkgZG9uJ3QgaGF2ZSB0byBiZSByZWNvbXB1dGVkLlxuICBtaW5WYWx1ZXM6IEZsb2F0MzJBcnJheTtcbiAgbWF4VmFsdWVzOiBGbG9hdDMyQXJyYXk7XG59XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBJbk1lbW9yeURhdGFzZXQge1xuICBwcm90ZWN0ZWQgZGF0YXNldDogTkRBcnJheVtdW118bnVsbDtcblxuICAvLyBDb250YWlucyBpbmZvcm1hdGlvbiBuZWNlc3NhcnkgZm9yIHJlY29uc3RydWN0aW9uIG9mIHRoZSBvcmlnaW5hbCBkYXRhXG4gIC8vIGFmdGVyIG5vcm1hbGl6YXRpb24uXG4gIHByaXZhdGUgbm9ybWFsaXphdGlvbkluZm86IHtbZGF0YUluZGV4OiBudW1iZXJdOiBOb3JtYWxpemF0aW9uSW5mb307XG5cbiAgY29uc3RydWN0b3IocHJvdGVjdGVkIGRhdGFTaGFwZXM6IG51bWJlcltdW10pIHtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvID0ge307XG4gIH1cblxuICBnZXREYXRhU2hhcGUoZGF0YUluZGV4OiBudW1iZXIpOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YVNoYXBlc1tkYXRhSW5kZXhdO1xuICB9XG5cbiAgYWJzdHJhY3QgZmV0Y2hEYXRhKCk6IFByb21pc2U8dm9pZD47XG5cbiAgZ2V0RGF0YSgpOiBOREFycmF5W11bXXxudWxsIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhc2V0O1xuICB9XG5cbiAgZ2V0U3RhdHMoKTogRGF0YVN0YXRzW10ge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZGF0YXNldC5tYXAoZCA9PiB0aGlzLmdldFN0YXRzRm9yRGF0YShkKSk7XG4gIH1cblxuICAvLyBDb21wdXRlcyBzdGF0cyBhY3Jvc3MgYSBzYW1wbGVkIHBvcnRpb24gb2YgdGhlIGRhdGEuXG4gIHByaXZhdGUgZ2V0U3RhdHNGb3JEYXRhKGRhdGE6IE5EQXJyYXlbXSk6IERhdGFTdGF0cyB7XG4gICAgbGV0IGlucHV0TWluID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIGxldCBpbnB1dE1heCA9IE51bWJlci5ORUdBVElWRV9JTkZJTklUWTtcblxuICAgIGxldCBleGFtcGxlSW5kaWNlcyA9IGRhdGEubWFwKChleGFtcGxlLCBpKSA9PiBpKTtcbiAgICB1dGlsLnNodWZmbGUoZXhhbXBsZUluZGljZXMpO1xuICAgIGV4YW1wbGVJbmRpY2VzID1cbiAgICAgICAgZXhhbXBsZUluZGljZXMuc2xpY2UoZXhhbXBsZUluZGljZXMubGVuZ3RoICogU1RBVFNfU0FNUExFX1BFUkNFTlRBR0UpO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBleGFtcGxlSW5kaWNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaW5wdXRWYWx1ZXMgPSBkYXRhW2V4YW1wbGVJbmRpY2VzW2ldXS5nZXRWYWx1ZXMoKTtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgaW5wdXRWYWx1ZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgaW5wdXRNaW4gPSBNYXRoLm1pbihpbnB1dE1pbiwgaW5wdXRWYWx1ZXNbal0pO1xuICAgICAgICBpbnB1dE1heCA9IE1hdGgubWF4KGlucHV0TWF4LCBpbnB1dFZhbHVlc1tqXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlucHV0TWluLFxuICAgICAgaW5wdXRNYXgsXG4gICAgICBleGFtcGxlQ291bnQ6IGRhdGEubGVuZ3RoLFxuICAgICAgc2hhcGU6IGRhdGFbMF0uc2hhcGUsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0gZXhhbXBsZXMgTkRBcnJheXMgdG8gYmUgbm9ybWFsaXplZC5cbiAgICogQHBhcmFtIGN1ckxvd2VyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG1pbmltdW0gdmFsdWUgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uIG9yIGEgZml4ZWQgbWluaW11bSB2YWx1ZS5cbiAgICogQHBhcmFtIGN1clVwcGVyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG1heGltdW0gdmFsdWUgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uIG9yIGEgZml4ZWQgbWF4aW11bSB2YWx1ZS5cbiAgICogQHBhcmFtIG5ld0xvd2VyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgbmV3IG1pbmltdW0gdmFsdWVzIGZvciBlYWNoXG4gICAqIGRpbWVuc2lvbiwgb3IgYSBmaXhlZCBtaW51bXVtIHZhbHVlIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSB0by5cbiAgICogQHBhcmFtIG5ld1VwcGVyQm91bmRzIEFuIGFycmF5IGNvbnRhaW5pbmcgbmV3IG1heGltdW0gdmFsdWVzIGZvciBlYWNoXG4gICAqIGRpbWVuc2lvbiwgb3IgYSBmaXhlZCBtYXhpbXVtIHZhbHVlIHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSB0by5cbiAgICovXG4gIHByaXZhdGUgbm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgZXhhbXBsZXM6IE5EQXJyYXlbXSwgY3VyTG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXIsXG4gICAgICBjdXJVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlciwgbmV3TG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXIsXG4gICAgICBuZXdVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcik6IE5EQXJyYXlbXSB7XG4gICAgY29uc3QgY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gPVxuICAgICAgICAoY3VyVXBwZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkgJiZcbiAgICAgICAgIGN1ckxvd2VyQm91bmRzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KTtcbiAgICBjb25zdCBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA9XG4gICAgICAgIChuZXdMb3dlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSAmJlxuICAgICAgICAgbmV3VXBwZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpO1xuXG4gICAgY29uc3QgaW5wdXRTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKGV4YW1wbGVzWzBdLnNoYXBlKTtcbiAgICBjb25zdCBuZXdFeGFtcGxlczogTkRBcnJheVtdID0gW107XG5cbiAgICBleGFtcGxlcy5mb3JFYWNoKGV4YW1wbGUgPT4ge1xuICAgICAgY29uc3QgaW5wdXRWYWx1ZXMgPSBleGFtcGxlLmdldFZhbHVlcygpO1xuICAgICAgY29uc3Qgbm9ybWFsaXplZFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoaW5wdXRTaXplKTtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgaW5wdXRTaXplOyBqKyspIHtcbiAgICAgICAgY29uc3QgY3VyTG93ZXJCb3VuZCA9IGN1ckJvdW5kc0lzUGVyRGltZW5zaW9uID9cbiAgICAgICAgICAgIChjdXJMb3dlckJvdW5kcyBhcyBGbG9hdDMyQXJyYXkpW2pdIDpcbiAgICAgICAgICAgIGN1ckxvd2VyQm91bmRzIGFzIG51bWJlcjtcbiAgICAgICAgY29uc3QgY3VyVXBwZXJCb3VuZCA9IGN1ckJvdW5kc0lzUGVyRGltZW5zaW9uID9cbiAgICAgICAgICAgIChjdXJVcHBlckJvdW5kcyBhcyBGbG9hdDMyQXJyYXkpW2pdIDpcbiAgICAgICAgICAgIGN1clVwcGVyQm91bmRzIGFzIG51bWJlcjtcbiAgICAgICAgY29uc3QgY3VyUmFuZ2UgPSBjdXJVcHBlckJvdW5kIC0gY3VyTG93ZXJCb3VuZDtcblxuICAgICAgICBjb25zdCBuZXdMb3dlckJvdW5kID0gbmV3Qm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKG5ld0xvd2VyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgbmV3TG93ZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBuZXdVcHBlckJvdW5kID0gbmV3Qm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKG5ld1VwcGVyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgbmV3VXBwZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBuZXdSYW5nZSA9IG5ld1VwcGVyQm91bmQgLSBuZXdMb3dlckJvdW5kO1xuXG4gICAgICAgIGlmIChjdXJSYW5nZSA9PT0gMCkge1xuICAgICAgICAgIG5vcm1hbGl6ZWRWYWx1ZXNbal0gPSBuZXdMb3dlckJvdW5kO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG5vcm1hbGl6ZWRWYWx1ZXNbal0gPSBuZXdMb3dlckJvdW5kICtcbiAgICAgICAgICAgICAgbmV3UmFuZ2UgKiAoaW5wdXRWYWx1ZXNbal0gLSBjdXJMb3dlckJvdW5kKSAvIGN1clJhbmdlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBuZXdFeGFtcGxlcy5wdXNoKE5EQXJyYXkubWFrZShleGFtcGxlLnNoYXBlLCB7dmFsdWVzOiBub3JtYWxpemVkVmFsdWVzfSkpO1xuICAgIH0pO1xuICAgIHJldHVybiBuZXdFeGFtcGxlcztcbiAgfVxuXG4gIHByaXZhdGUgY29tcHV0ZUJvdW5kcyhkYXRhSW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuXG4gICAgY29uc3Qgc2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLmRhdGFzZXRbZGF0YUluZGV4XVswXS5zaGFwZSk7XG5cbiAgICAvLyBDb21wdXRlIG1pbiBhbmQgbWF4IHZhbHVlcyBmb3IgZXZlcnkgZGltZW5zaW9uLlxuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XSA9IHtcbiAgICAgIGlzTm9ybWFsaXplZDogZmFsc2UsXG4gICAgICBtaW5WYWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSksXG4gICAgICBtYXhWYWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSlcbiAgICB9O1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNbaV0gPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWF4VmFsdWVzW2ldID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIH1cblxuICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdLmZvckVhY2goZXhhbXBsZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGV4YW1wbGUuZ2V0VmFsdWVzKCk7XG4gICAgICBmb3IgKGxldCBrID0gMDsgayA8IHNpemU7IGsrKykge1xuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzW2tdID0gTWF0aC5taW4oXG4gICAgICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzW2tdLCBpbnB1dFZhbHVlc1trXSk7XG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNba10gPSBNYXRoLm1heChcbiAgICAgICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNba10sIGlucHV0VmFsdWVzW2tdKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG5vcm1hbGl6ZVdpdGhpbkJvdW5kcyhcbiAgICAgIGRhdGFJbmRleDogbnVtYmVyLCBsb3dlckJvdW5kOiBudW1iZXIsIHVwcGVyQm91bmQ6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhIGlzIG51bGwuJyk7XG4gICAgfVxuICAgIGlmIChkYXRhSW5kZXggPj0gdGhpcy5kYXRhc2V0Lmxlbmd0aCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdkYXRhSW5kZXggb3V0IG9mIGJvdW5kcy4nKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdID09IG51bGwpIHtcbiAgICAgIHRoaXMuY29tcHV0ZUJvdW5kcyhkYXRhSW5kZXgpO1xuICAgIH1cblxuICAgIC8vIGN1ckxvd2VyL1VwcGVyQm91bmRzIG9mIHRoZSBjdXJyZW50IGRhdGEgc2V0IGNhbiBlaXRoZXIgYmUgZml4ZWQgbnVtYmVyc1xuICAgIC8vIGlmIHRoZSBkYXRhIGhhcyBhbHJlYWR5IGJlZW4gbm9ybWFsaXplZCwgb3IgY3VyTG93ZXIvVXBwZXIgZm9yIGVhY2hcbiAgICAvLyBkaW1lbnNpb24gaWYgaXQgaGFzbid0IGJlZW4gbm9ybWFsaXplZCB5ZXQuXG4gICAgbGV0IGN1ckxvd2VyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyO1xuICAgIGxldCBjdXJVcHBlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcjtcblxuICAgIGlmICh0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkKSB7XG4gICAgICBjdXJMb3dlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5sb3dlckJvdW5kITtcbiAgICAgIGN1clVwcGVyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQhO1xuICAgIH0gZWxzZSB7XG4gICAgICBjdXJMb3dlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXM7XG4gICAgICBjdXJVcHBlckJvdW5kcyA9IHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXM7XG4gICAgfVxuXG4gICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0gPSB0aGlzLm5vcm1hbGl6ZUV4YW1wbGVzVG9SYW5nZShcbiAgICAgICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0sIGN1ckxvd2VyQm91bmRzLCBjdXJVcHBlckJvdW5kcywgbG93ZXJCb3VuZCxcbiAgICAgICAgdXBwZXJCb3VuZCk7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmlzTm9ybWFsaXplZCA9IHRydWU7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQgPSBsb3dlckJvdW5kO1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS51cHBlckJvdW5kID0gdXBwZXJCb3VuZDtcbiAgfVxuXG4gIHByaXZhdGUgaXNOb3JtYWxpemVkKGRhdGFJbmRleDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubm9ybWFsaXphdGlvbkluZm8gIT0gbnVsbCAmJlxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkO1xuICB9XG5cbiAgcmVtb3ZlTm9ybWFsaXphdGlvbihkYXRhSW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmRhdGFzZXQgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcmFpbmluZyBvciB0ZXN0IGRhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuaXNOb3JtYWxpemVkKGRhdGFJbmRleCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSA9IHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSwgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXMsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXMpO1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQgPSBmYWxzZTtcbiAgfVxuXG4gIHVubm9ybWFsaXplRXhhbXBsZXMoZXhhbXBsZXM6IE5EQXJyYXlbXSwgZGF0YUluZGV4OiBudW1iZXIpOiBOREFycmF5W10ge1xuICAgIGlmICghdGhpcy5pc05vcm1hbGl6ZWQoZGF0YUluZGV4KSkge1xuICAgICAgcmV0dXJuIGV4YW1wbGVzO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLm5vcm1hbGl6ZUV4YW1wbGVzVG9SYW5nZShcbiAgICAgICAgZXhhbXBsZXMsIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5sb3dlckJvdW5kISxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWluVmFsdWVzLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubWF4VmFsdWVzKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmRhdGFzZXQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgdGhpcy5kYXRhc2V0W2ldLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHRoaXMuZGF0YXNldFtpXVtqXS5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZGF0YXNldCA9IFtdO1xuICB9XG59XG5cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHcmFwaExheWVyc30gZnJvbSAnLi9ncmFwaF9sYXllcnMnO1xuaW1wb3J0ICogYXMgY29uY2F0M2RfdXRpbCBmcm9tICcuL21hdGgvY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi9tYXRoL2NvbnZfdXRpbCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEdyYXBoIGlzIHRoZSBwcmltYXJ5IGNvbnRhaW5lciBzdHJ1Y3R1cmUgZm9yIGxlYXJuLmpzIG9wZXJhdGlvbnMuIEdyYXBoXG4gKiBob2xkcyB0aGUgdG9wb2xvZ3kgb2Ygb3BlcmF0aW9uIG5vZGVzIGFuZCB0aGUgY29ubmVjdGl2aXR5IGJldHdlZW4gdGhlbS5cbiAqL1xuZXhwb3J0IGNsYXNzIEdyYXBoIHtcbiAgbGF5ZXJzOiBHcmFwaExheWVycztcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmxheWVycyA9IG5ldyBHcmFwaExheWVycyh0aGlzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmFtZWQgdmFyaWFibGUuIFZhcmlhYmxlcyBhcmUgdGVuc29ycyB0aGF0IG1haW50YWluIHN0YXRlIGFjcm9zc1xuICAgKiBzZXNzaW9uIGNhbGxzIGFuZCB3aG9zZSB2YWx1ZXMgYXJlIGFkanVzdGVkIGR1cmluZyBiYWNrcHJvcGFnYXRpb25cbiAgICogdHJhaW5pbmcuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgdmFyaWFibGUuXG4gICAqIEBwYXJhbSBkYXRhIFRoZSBOREFycmF5IHRvIGFzc29jaWF0ZSB3aXRoIHRoaXMgdmFyaWFibGUgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSB2YXJpYWJsZS5cbiAgICovXG4gIHZhcmlhYmxlKG5hbWU6IHN0cmluZywgZGF0YTogTkRBcnJheSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgVmFyaWFibGVOb2RlKHRoaXMsIG5hbWUsIGRhdGEpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnNlcnRzIGEgcGxhY2Vob2xkZXIgZm9yIGEgdGVuc29yIHRoYXQgd2lsbCBiZSBhbHdheXMgZmVkLiBQbGFjZWhvbGRlcnNcbiAgICogYXJlIGlucHV0IHRlbnNvcnMgd2hvc2UgdmFsdWVzIGFyZSBwcm92aWRlZCBieSB0aGUgY2xpZW50IHZpYSBmZWVkXG4gICAqIGRpY3Rpb25hcmllcy4gUGxhY2Vob2xkZXJzIGFyZSBub3QgdXBkYXRlZCBhcyBwYXJ0IG9mIHRyYWluaW5nOyB0aGV5IGFyZVxuICAgKiBvbmx5IHVzZWQgYXMgaW1tdXRhYmxlIGlucHV0LlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGlzIHBsYWNlaG9sZGVyLlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBwbGFjZWhvbGRlciB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHBsYWNlaG9sZGVyLlxuICAgKi9cbiAgcGxhY2Vob2xkZXIobmFtZTogc3RyaW5nLCBzaGFwZTogbnVtYmVyW10pOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFBsYWNlaG9sZGVyTm9kZSh0aGlzLCBuYW1lLCBzaGFwZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnN0YW50IHZhbHVlIHRoYXQgcGVyc2lzdHMgYWNyb3NzIHNlc3Npb24gY2FsbHMuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgdmFsdWUgdG8gcmV0dXJuLlxuICAgKiBAcmV0dXJuIEEgbm9kZSBvdXRwdXRpbmcgdGhlIGNvbnN0YW50IHZhbHVlLlxuICAgKi9cbiAgY29uc3RhbnQodmFsdWU6IEFycmF5RGF0YSk6IFRlbnNvciB7XG4gICAgbGV0IGZpbmFsVmFsdWU6IE5EQXJyYXk7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicpIHtcbiAgICAgIGZpbmFsVmFsdWUgPSBTY2FsYXIubmV3KHZhbHVlKTtcbiAgICB9IGVsc2UgaWYgKHZhbHVlIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgZmluYWxWYWx1ZSA9IHZhbHVlO1xuICAgIH0gZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgY29uc3QgdmFscyA9IG5ldyBGbG9hdDMyQXJyYXkodXRpbC5mbGF0dGVuKHZhbHVlKSk7XG4gICAgICBmaW5hbFZhbHVlID0gTkRBcnJheS5tYWtlKHV0aWwuaW5mZXJTaGFwZSh2YWx1ZSksIHt2YWx1ZXM6IHZhbHN9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd1bmltcGxlbWVudGVkIGNvbnN0YW50IHR5cGUuJyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbnN0YW50Tm9kZSh0aGlzLCBmaW5hbFZhbHVlKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVzaGFwZSB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIGJlIHJlc2hhcGVkLlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBvdXRwdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSByZXNoYXBlIG9wZXJhdGlvbi5cbiAgICovXG4gIHJlc2hhcGUoeDogVGVuc29yLCBzaGFwZTogbnVtYmVyW10pOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBSZXNoYXBlTm9kZSh0aGlzLCAnUmVzaGFwZScsIHgsIHNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBmdXNlZCBsaW5lYXIgY29tYmluYXRpb24gb2YgdHdvIHRlbnNvcnMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuIFNhbWUgc2hhcGUgYXMgdDEuXG4gICAqIEBwYXJhbSBjMSBDb2VmZmljaWVudCBvZiB0MS4gTXVzdCBiZSBzaXplIDEuXG4gICAqIEBwYXJhbSBjMiBDb2VmZmljaWVudCBvZiB0Mi4gTXVzdCBiZSBzaXplIDEuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgYzEqdDErYzIqdDIuXG4gICAqL1xuICBmdXNlZExpbmVhckNvbWJpbmF0aW9uKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IsIGMxOiBUZW5zb3IsIGMyOiBUZW5zb3IpOlxuICAgICAgVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUodGhpcywgeDEsIHgyLCBjMSwgYzIpKTtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEFkZHMgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxK3QyLlxuICAgKi9cbiAgYWRkKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFkZE5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogU3VidHJhY3RzIHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MS10Mi5cbiAgICovXG4gIHN1YnRyYWN0KHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFN1YnRyYWN0Tm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNdWx0aXBseSB0d28gdGVuc29ycyAoZWxlbWVudHdpc2UpLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdDEqdDIuXG4gICAqL1xuICBtdWx0aXBseSh4MTogVGVuc29yLCB4MjogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBNdWx0aXBseU5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogRGl2aWRlIHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MSAvIHQyLlxuICAgKi9cbiAgZGl2aWRlKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IERpdmlkZU5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHN1bSBvZiBlbGVtZW50cyBpbiB0aGUgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yLlxuICAgKi9cbiAgcmVkdWNlU3VtKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUmVkdWNlU3VtTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uY2F0cyB0d28gM0QgdGVuc29ycyBhbG9uZyBhIGdpdmVuIGF4aXMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgY29uY2F0IG9mIHR3byB0ZW5zb3JzIGFsb25nIGF4aXMuXG4gICAqL1xuICBjb25jYXQzZCh4MTogVGVuc29yLCB4MjogVGVuc29yLCBheGlzOiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbmNhdDNETm9kZSh0aGlzLCB4MSwgeDIsIGF4aXMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3QgYmV0d2VlbiB0d28gbWF0cmljZXMuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGRvdCBwcm9kdWN0IG9mIHgxIGFuZCB4Mi5cbiAgICovXG4gIG1hdG11bCh4MTogVGVuc29yLCB4MjogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBNYXRNdWxOb2RlKHRoaXMsIHgxLCB4MikpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgMkQgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHcgVGhlIHdlaWdodCB0ZW5zb3IgdXNlZCBieSB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYiBUaGUgYmlhcyB0ZW5zb3IgdXNlZCBieSB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBjb252b2x1dGlvbmFsIGtlcm5lbC5cbiAgICogQHBhcmFtIG91dHB1dERlcHRoIFRoZSBvdXRwdXQgZGVwdGggb2YgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSBhbW91bnQgb2YgemVybyBwYWRkaW5nIG9uIGFsbCBzaWRlcyBvZiB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqL1xuICBjb252MmQoXG4gICAgICB4OiBUZW5zb3IsIHc6IFRlbnNvciwgYjogVGVuc29yLCBmaWVsZFNpemU6IG51bWJlciwgb3V0cHV0RGVwdGg6IG51bWJlcixcbiAgICAgIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IENvbnZvbHV0aW9uMkROb2RlKFxuICAgICAgICB0aGlzLCB4LCB3LCBiLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIDJEIG1heCBwb29sIG9mIHguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIG1heCBwb29sIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIGZpZWxkU2l6ZSBUaGUgc2l6ZSBvZiB0aGUgY29udm9sdXRpb25hbCBrZXJuZWwuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gemVyb1BhZCBUaGUgYW1vdW50IG9mIHplcm8gcGFkZGluZyBvbiBhbGwgc2lkZXMgb2YgdGhlIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbWF4IHBvb2wgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWF4UG9vbCh4OiBUZW5zb3IsIGZpZWxkU2l6ZTogbnVtYmVyLCBzdHJpZGUgPSAxLCB6ZXJvUGFkPzogbnVtYmVyKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgTWF4UG9vbE5vZGUodGhpcywgeCwgZmllbGRTaXplLCBzdHJpZGUsIHplcm9QYWQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBleHBvbmVudGlhbCBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgZXhwLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBlIF4geCBvcGVyYXRpb24uXG4gICAqL1xuICBleHAoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBFeHBOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBsb2cgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIGxvZy5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbG4oeCkgb3BlcmF0aW9uLlxuICAgKi9cbiAgbG9nKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgTG9nTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgUmVMVSBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgUmVMVS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgUmVMVSBvcGVyYXRpb24uXG4gICAqL1xuICByZWx1KHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUmVMVU5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIFRhbkggb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIFRhbkguXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIFRhbkggb3BlcmF0aW9uLlxuICAgKi9cbiAgdGFuaCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFRhbkhOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBTaWdtb2lkIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBzaWdtb2lkLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBzaWdtb2lkIG9wZXJhdGlvbi5cbiAgICovXG4gIHNpZ21vaWQoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTaWdtb2lkTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgc3F1YXJlIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBzcXVhcmUuXG4gICAqL1xuICBzcXVhcmUoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTcXVhcmVOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzb2Z0bWF4IHByb2JhYmlsaXRpZXMgZnJvbSBsb2dpdHMuXG4gICAqXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBsb2dpdHMuXG4gICAqIEByZXR1cm4gVGhlIHNvZnRtYXggcHJvYmFiaWxpdGllcy5cbiAgICovXG4gIHNvZnRtYXgoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBTb2Z0bWF4Tm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gY2xhc3NpZnkuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvbi5cbiAgICovXG4gIHNvZnRtYXhDcm9zc0VudHJvcHlDb3N0KHg6IFRlbnNvciwgdGFyZ2V0OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUodGhpcywgeCwgdGFyZ2V0KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG1lYW4tc3F1YXJlZCBjb3N0IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSBsYWJlbCBUaGUgbGFiZWwgdGVuc29yLlxuICAgKiBAcGFyYW0gcHJlZGljdGlvbiBUaGUgcHJlZGljdGlvbiB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIG1lYW4tc3F1YXJlZCBjb3N0IG9wZXJhdGlvbi5cbiAgICovXG4gIG1lYW5TcXVhcmVkQ29zdChsYWJlbDogVGVuc29yLCBwcmVkaWN0aW9uOiBUZW5zb3IpIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KFxuICAgICAgICBuZXcgTWVhblNxdWFyZWRDb3N0Tm9kZSh0aGlzLCBsYWJlbCwgcHJlZGljdGlvbikpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWF4aW11bSBlbnRyeSBpbiB0aGUgdGVuc29yLlxuICAgKiBAcGFyYW0geCBUaGUgdGVuc29yIHdpdGggdGhlIHZhbHVlLlxuICAgKiBAcmV0dXJuIEEgU2NhbGFyIHRlbnNvciB3aXRoIHRoZSBpbmRleCBvZiB0aGUgbWF4aW11bSBlbnRyeS5cbiAgICovXG4gIGFyZ21heCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFyZ01heE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYW4gYXJnbWF4IGVxdWFscyBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICAgKiBAcGFyYW0geDEgRmlyc3QgaW5wdXQgdGVuc29yIHRvIGNoZWNrIGFnYWluc3QuXG4gICAqIEBwYXJhbSB4MiBTZWNvbmQgaW5wdXQgdGVuc29yIHRvIGNoZWNrIGFnYWluc3QuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGFyZ21heCBlcXVhbHMgb3BlcmF0aW9uLlxuICAgKi9cbiAgYXJnbWF4RXF1YWxzKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEFyZ01heEVxdWFsc05vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICBwcml2YXRlIGFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobm9kZTogTm9kZSk6IFRlbnNvciB7XG4gICAgdGhpcy5ub2Rlcy5wdXNoKG5vZGUpO1xuICAgIG5vZGUudmFsaWRhdGUoKTtcbiAgICByZXR1cm4gbm9kZS5vdXRwdXQ7XG4gIH1cblxuICBnZXROb2RlcygpOiBOb2RlW10ge1xuICAgIHJldHVybiB0aGlzLm5vZGVzO1xuICB9XG5cbiAgcHJpdmF0ZSBub2RlczogTm9kZVtdID0gW107XG59XG5cbi8qKlxuICogVGVuc29yIHJlcHJlc2VudHMgdGhlIG91dHB1dCBvZiBhbiBvcGVyYXRpb24gbm9kZSBpbiB0aGUgZ3JhcGguXG4gKiBUZW5zb3JzIGhhdmUgbm8gZGF0YSBhc3NvY2lhdGVkIHdpdGggdGhlbSwgYnV0IG1haW50YWluIGEgc2hhcGUgYXJyYXlcbiAqIHRvIGRldGVybWluZSBvcGVyYXRpb24gY29tcGF0aWJpbGl0eS4gQWxsIGdyYXBoIG1ldGhvZHMgdGhhdCBjcmVhdGUgZ3JhcGhcbiAqIG9wZXJhdGlvbnMgcmV0dXJuIFRlbnNvciBvYmplY3RzLCB3aGljaCBjYW4gYmUgdGhvdWdodCBvZiBhcyAnaGFuZGxlcycgdG9cbiAqIG9wZXJhdGlvbnMuXG4gKi9cbmV4cG9ydCBjbGFzcyBUZW5zb3Ige1xuICBub2RlOiBOb2RlO1xuICBpZDogbnVtYmVyO1xuICAvKipcbiAgICogQHBhcmFtIHNoYXBlIFRoZSBzaGFwZSBvZiB0aGlzIHRlbnNvciwgaW4gZGltZW5zaW9uIHNpemVzLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHVibGljIHNoYXBlOiBudW1iZXJbXSkge1xuICAgIHRoaXMuaWQgPSBUZW5zb3IubmV4dElEKys7XG4gIH1cbiAgcHJpdmF0ZSBzdGF0aWMgbmV4dElEID0gMDtcbn1cblxuLyoqXG4gKiBOb2RlIGlzIHRoZSBjb25jcmV0ZSBiYXNlIGNsYXNzIGZvciBhbGwgb3BlcmF0aW9ucyBpbiB0aGUgZ3JhcGguXG4gKiBVc2VycyBnZW5lcmFsbHkgZG9uJ3QgbmVlZCB0byBpbnRlcmFjdCBkaXJlY3RseSB3aXRoIE5vZGUgaW5zdGFuY2VzLCBidXQgdGhleVxuICogYXJlIHByb3ZpZGVkIGZvciBpbmZvcm1hdGlvbmFsIGFuZCBpbnRyb3NwZWN0aW9uIHB1cnBvc2VzLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE5vZGUge1xuICAvKipcbiAgICogQHBhcmFtIGdyYXBoIFRoZSBncmFwaCBjb250YWluaW5nIHRoaXMgbm9kZVxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZSBvZiB0aGlzIG5vZGVcbiAgICogQHBhcmFtIGlucHV0cyBBIGRpY3Rpb25hcnkgb2YgbmFtZWQgVGVuc29ycyB0aGF0IGNvbXByaXNlIHRoaXMgbm9kZSdzXG4gICAqIGlucHV0cy5cbiAgICogQHBhcmFtIG91dHB1dCBUaGlzIG5vZGUncyBvdXRwdXQgVGVuc29yXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHB1YmxpYyBncmFwaDogR3JhcGgsIHB1YmxpYyBuYW1lOiBzdHJpbmcsXG4gICAgICBwdWJsaWMgaW5wdXRzOiB7W25hbWU6IHN0cmluZ106IFRlbnNvcn0sIHB1YmxpYyBvdXRwdXQ6IFRlbnNvcikge1xuICAgIHRoaXMuaWQgPSBOb2RlLm5leHRJRCsrO1xuICAgIG91dHB1dC5ub2RlID0gdGhpcztcbiAgfVxuICBhYnN0cmFjdCB2YWxpZGF0ZSgpOiB2b2lkO1xuICBpZDogbnVtYmVyO1xuICBwcml2YXRlIHN0YXRpYyBuZXh0SUQgPSAwO1xufVxuXG4vKipcbiAqIFZhcmlhYmxlTm9kZSByZXByZXNlbnRzIGEgdmFyaWFibGUsIGEgdXNlci1wcm92aWRlZCBOREFycmF5IHRoYXQnc1xuICogYWRqdXN0ZWQgZHVyaW5nIGJhY2twcm9wYWdhdGlvbiB0cmFpbmluZy5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBWYXJpYWJsZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBuYW1lOiBzdHJpbmcsIHB1YmxpYyBkYXRhOiBOREFycmF5KSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt9LCBuZXcgVGVuc29yKGRhdGEuc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5kYXRhICE9IG51bGwsXG4gICAgICAgICdFcnJvciBhZGRpbmcgdmFyaWFibGUgb3A6IERhdGEgZm9yIHZhcmlhYmxlIFxcJycgKyB0aGlzLm5hbWUgK1xuICAgICAgICAgICAgJ1xcJyBpcyBudWxsIG9yIHVuZGVmaW5lZCcpO1xuICB9XG59XG5cbi8qKlxuICogUGxhY2Vob2xkZXJOb2RlIHJlcHJlc2VudHMgYSBwbGFjZWhvbGRlciwgYSB1c2VyLXByb3ZpZGVkIE5EQXJyYXlcbiAqIHRoYXQncyB1c2VkIGFzIGltbXV0YWJsZSBpbnB1dCBkdXJpbmcgaW5mZXJlbmNlIGFuZCB0cmFpbmluZy5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBQbGFjZWhvbGRlck5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBuYW1lOiBzdHJpbmcsIHNoYXBlOiBudW1iZXJbXSkge1xuICAgIHN1cGVyKGdyYXBoLCBuYW1lLCB7fSwgbmV3IFRlbnNvcihzaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBDb25zdGFudE5vZGUgcmVwcmVzZW50cyBhIGNvbnN0YW50IHZhbHVlIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25zdGFudE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwdWJsaWMgZGF0YTogTkRBcnJheSkge1xuICAgIHN1cGVyKGdyYXBoLCAnQ29uc3RhbnQnLCB7fSwgbmV3IFRlbnNvcihkYXRhLnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuZGF0YSAhPSBudWxsLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnN0YW50OiBkYXRhIGZvciBwbGFjZWhvbGRlciBcXCcnICsgdGhpcy5uYW1lICtcbiAgICAgICAgICAgICdcXCcgaXMgbnVsbCBvciB1bmRlZmluZWQnKTtcbiAgfVxufVxuXG4vKipcbiAqIFJlc2hhcGVOb2RlIHJlcHJlc2VudHMgYSByZXNoYXBlIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVzaGFwZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwdWJsaWMgbmFtZTogc3RyaW5nLCBwcml2YXRlIHg6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt4fSwgbmV3IFRlbnNvcihzaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIGNvbnN0IHhTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMueC5zaGFwZSk7XG4gICAgY29uc3Qgc2hhcGVTaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMuc2hhcGUpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4U2l6ZSA9PT0gc2hhcGVTaXplLFxuICAgICAgICAnRXJyb3IgbWFraW5nIHJlc2hhcGUgb3BlcmF0aW9uOiBpbnB1dCBUZW5zb3IgdG8gcmVzaGFwZSBcXCcnICtcbiAgICAgICAgICAgIHRoaXMubmFtZSArICdcXCcgb2Ygc2hhcGUgKCcgKyB0aGlzLnguc2hhcGUgK1xuICAgICAgICAgICAgJykgZG9lcyBub3QgbWF0Y2ggc2l6ZSBvZiByZXF1ZXN0ZWQgc2hhcGUgJyArIHRoaXMuc2hhcGUgKyAnLicpO1xuICB9XG59XG5cbi8qKlxuICogTGluZWFyQ29tYmluYXRpb25Ob2RlIHJlcHJlc2VudHMgYSBsaW5lYXIgY29tYmluYXRpb24gb2YgdHdvIHRlbnNvcnMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgVDEgPSAndDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgVDIgPSAndDInO1xuICBzdGF0aWMgcmVhZG9ubHkgQzEgPSAnYzEnO1xuICBzdGF0aWMgcmVhZG9ubHkgQzIgPSAnYzInO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IsIHByaXZhdGUgYzE6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgYzI6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnTGluZWFyIENvbWJpbmF0aW9uJywge3QxLCB0MiwgYzEsIGMyfSwgbmV3IFRlbnNvcih0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKTtcbiAgICBpZiAoIXV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLmMxLnNoYXBlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgZnVzZWRMaW5lYXJDb21iaW5hdGlvbjogYzEgaXMgbm90IGEgc2NhbGFyLCBnb3QgJyArXG4gICAgICAgICAgJ3NoYXBlOiAnICsgdGhpcy5jMS5zaGFwZSk7XG4gICAgfVxuICAgIGlmICghdXRpbC5pc1NjYWxhclNoYXBlKHRoaXMuYzIuc2hhcGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBmdXNlZExpbmVhckNvbWJpbmF0aW9uOiBjMiBpcyBub3QgYSBzY2FsYXIsIGdvdCAnICtcbiAgICAgICAgICAnc2hhcGU6ICcgKyB0aGlzLmMyLnNoYXBlKTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBZGROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0FkZCcsIHt0MSwgdDJ9LFxuICAgICAgICBuZXcgVGVuc29yKHV0aWwuc2l6ZUZyb21TaGFwZSh0MS5zaGFwZSkgPT09IDEgPyB0Mi5zaGFwZSA6IHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50Mi5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy50MS5zaGFwZSwgdGhpcy50Mi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgYWRkIG9wZXJhdGlvbiBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3VidHJhY3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ1N1YnRyYWN0Jywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBzdWJ0cmFjdCBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTXVsdGlwbHlOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ011bHRpcGx5Jywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBtdWx0aXBseSBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRGl2aWRlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgVDEgPSAndDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgVDIgPSAndDInO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdEaXZpZGUnLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGRpdmlkZSBvcDogb25lIG9mIGlucHV0cyBtdXN0IGJlIHNjYWxhciBvciB0aGUgJyArXG4gICAgICAgICAgICAnc2hhcGVzICcgKyB0aGlzLnQxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMudDIuc2hhcGUgK1xuICAgICAgICAgICAgJyBtdXN0IG1hdGNoLicpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVkdWNlU3VtTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnUmVkdWNlU3VtJywge3h9LCBuZXcgVGVuc29yKFtdKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogQ29uY2F0M0ROb2RlIHJlcHJlc2VudHMgYSAzRCBjb25jYXRlbmF0aW9uIG9mIHR3byB0ZW5zb3JzIGFsb25nIGFuIGF4aXMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25jYXQzRE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFgxID0gJ3gxJztcbiAgc3RhdGljIHJlYWRvbmx5IFgyID0gJ3gyJztcbiAgc3RhdGljIHJlYWRvbmx5IEFYSVMgPSAnYXhpcyc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHgxOiBUZW5zb3IsIHByaXZhdGUgeDI6IFRlbnNvcixcbiAgICAgIHB1YmxpYyBheGlzOiBudW1iZXIpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdDb25jYXQzRCcsIHt4MSwgeDJ9LFxuICAgICAgICBuZXcgVGVuc29yKGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoXG4gICAgICAgICAgICB4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgY29uY2F0M2RfdXRpbC5hc3NlcnRDb25jYXQzRFNoYXBlc01hdGNoKFxuICAgICAgICB0aGlzLngxLnNoYXBlLCB0aGlzLngyLnNoYXBlLCB0aGlzLmF4aXMpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGdldE1hdE11bE91dHB1dFNoYXBlKHgxU2hhcGU6IG51bWJlcltdLCB4MlNoYXBlOiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgaWYgKHgxU2hhcGUubGVuZ3RoID09PSAxICYmIHgyU2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgcmV0dXJuIFsxXTtcbiAgfSBlbHNlIGlmICh4MVNoYXBlLmxlbmd0aCA9PT0gMSAmJiB4MlNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgIHJldHVybiBbeDJTaGFwZVsxXV07XG4gIH0gZWxzZSBpZiAoeDFTaGFwZS5sZW5ndGggPT09IDIgJiYgeDJTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gW3gxU2hhcGVbMF1dO1xuICB9XG4gIHJldHVybiBbeDFTaGFwZVswXSwgeDJTaGFwZVsxXV07XG59XG5cbi8qKlxuICogTWF0TXVsTm9kZSByZXByZXNlbnRzIGEgZnVsbHkgY29ubmVjdGVkIGxheWVyIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1hdE11bE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFgxID0gJ3gxJztcbiAgc3RhdGljIHJlYWRvbmx5IFgyID0gJ3gyJztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHgxOiBUZW5zb3IsIHByaXZhdGUgeDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ01hdE11bCcsIHt4MSwgeDJ9LFxuICAgICAgICBuZXcgVGVuc29yKGdldE1hdE11bE91dHB1dFNoYXBlKHgxLnNoYXBlLCB4Mi5zaGFwZSkpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIGlmICh0aGlzLngxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB0aGlzLngyLnNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy54MS5zaGFwZVsxXSA9PT0gdGhpcy54Mi5zaGFwZVswXSxcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIG1hdG11bCBvcDogaW5uZXIgc2hhcGVzIG9mIG1hdHJpY2VzIHdpdGggc2hhcGVzICcgK1xuICAgICAgICAgICAgICB0aGlzLngxLnNoYXBlICsgJyBhbmQgJyArIHRoaXMueDIuc2hhcGUgKyAnIG11c3QgbWF0Y2guJyk7XG4gICAgfSBlbHNlIGlmICh0aGlzLngxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB0aGlzLngyLnNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy54MS5zaGFwZVsxXSA9PT0gdGhpcy54Mi5zaGFwZVswXSxcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIG1hdG11bCBvcDogc2Vjb25kIGRpbWVuc2lvbiBvZiBtYXRyaXggd2l0aCBzaGFwZSAnICtcbiAgICAgICAgICAgICAgdGhpcy54MS5zaGFwZSArICcgbXVzdCBtYXRjaCBzaXplIG9mIHZlY3RvciB3aXRoIHNoYXBlICcgK1xuICAgICAgICAgICAgICB0aGlzLngyLnNoYXBlICsgJy4nKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAxICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzBdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBzaXplIG9mIHZlY3RvciB3aXRoIHNoYXBlICcgKyB0aGlzLngxLnNoYXBlICtcbiAgICAgICAgICAgICAgJyBtdXN0IG1hdGNoIGZpcnN0IGRpbWVuc2lvbiBvZiBtYXRyaXggd2l0aCAnICtcbiAgICAgICAgICAgICAgJ3NoYXBlICcgKyB0aGlzLngyLnNoYXBlICsgJy4nKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBpbnB1dHMgbXVzdCBiZSB2ZWN0b3JzIG9yIG1hdHJpY2VzLicpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIENvbnZvbHV0aW9uMkROb2RlIHJlcHJlc2VudHMgYSAyZCBjb252b2x1dGlvbiBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQ29udm9sdXRpb24yRE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIHN0YXRpYyByZWFkb25seSBXID0gJ3cnO1xuICBzdGF0aWMgcmVhZG9ubHkgQiA9ICdiJztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHByaXZhdGUgeDogVGVuc29yLCBwcml2YXRlIHc6IFRlbnNvciwgcHJpdmF0ZSBiOiBUZW5zb3IsXG4gICAgICBwdWJsaWMgZmllbGRTaXplOiBudW1iZXIsIHB1YmxpYyBvdXRwdXREZXB0aDogbnVtYmVyLCBwdWJsaWMgc3RyaWRlID0gMSxcbiAgICAgIHB1YmxpYyB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnQ29udm9sdXRpb24gMkQnLCB7eCwgdywgYn0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgICAgeC5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSxcbiAgICAgICAgICAgIHplcm9QYWQpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMueC5zaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBpbnB1dCBtdXN0IGJlIG9mIHJhbmsgMywgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLnguc2hhcGUgKyAnLicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLncuc2hhcGUubGVuZ3RoID09PSA0LFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnYyZCBvcDogd2VpZ2h0cyBtdXN0IGJlIG9mIHJhbmsgNCwgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLncuc2hhcGUgKyAnLicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLmIuc2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGNvbnYyZCBvcDogYmlhc2VzIG11c3QgYmUgb2YgcmFuayAxLCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMuYi5zaGFwZSArICcuJyk7XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlWzJdID09PSB0aGlzLncuc2hhcGVbMl0sXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBkZXB0aCBvZiBpbnB1dCAoJyArIHRoaXMueC5zaGFwZVsyXSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICgnICsgdGhpcy53LnNoYXBlWzJdICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBNYXhQb29sTm9kZSByZXByZXNlbnRzIGEgMmQgbWF4IHBvb2wgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1heFBvb2xOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHB1YmxpYyBmaWVsZFNpemU6IG51bWJlcixcbiAgICAgIHB1YmxpYyBzdHJpZGUgPSAxLCBwdWJsaWMgemVyb1BhZD86IG51bWJlcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ01heCBwb29sJywge3h9LFxuICAgICAgICBuZXcgVGVuc29yKGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgICAgIHguc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemUsIHguc2hhcGVbMl0sIHN0cmlkZSxcbiAgICAgICAgICAgIHplcm9QYWQpKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMueC5zaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICAgICdFcnJvciBhZGRpbmcgbWF4UG9vbCBvcDogaW5wdXQgbXVzdCBiZSBvZiByYW5rIDMsIGJ1dCBnb3Qgc2hhcGU6ICcgK1xuICAgICAgICAgICAgdGhpcy54LnNoYXBlICsgJy4nKTtcbiAgfVxufVxuXG4vKipcbiAqIFJlTFVOb2RlIHJlcHJlc2VudHMgYSBSZUxVIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZUxVTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1JlTFUnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBFeHBOb2RlIHJlcHJlc2VudHMgYSBFeHBvbmVudGlhdGlvbiBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRXhwTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0V4cCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIExvZ05vZGUgcmVwcmVzZW50cyBhIEV4cG9uZW50aWF0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2dOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnTG9nJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogVGFuSE5vZGUgcmVwcmVzZW50cyBhIHRhbmggb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFRhbkhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnVGFuSCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNpZ21vaWROb2RlIHJlcHJlc2VudHMgYSBzaWdtb2lkIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaWdtb2lkTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NpZ21vaWQnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBTcXVhcmUgbm9kZSByZXByZXNlbnRzIGFuIGVsZW1lbnQtd2lzZSBzcXVhcmUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNxdWFyZU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdTcXVhcmUnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUgcmVwcmVzZW50cyBhIHNvZnRtYXggY3Jvc3MtZW50cm9weSBjb3N0IG9wZXJhdGlvblxuICogaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBzdGF0aWMgcmVhZG9ubHkgVEFSR0VUID0gJ3RhcmdldCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgdGFyZ2V0OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NvZnRtYXhDcm9zc0VudHJvcHlDb3N0Jywge3gsIHRhcmdldH0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnguc2hhcGUsIHRoaXMudGFyZ2V0LnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBzb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdCBvcDogeCBzaGFwZSAoJyArIHRoaXMueC5zaGFwZSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIHRhcmdldCBzaGFwZSAoJyArIHRoaXMudGFyZ2V0LnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdTb2Z0bWF4Jywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMSxcbiAgICAgICAgJ1RoZSBpbnB1dCB0byBhIHNvZnRtYXggbXVzdCBiZSBhIDEtRCB0ZW5zb3InKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlWzBdID49IDIsXG4gICAgICAgICdUaGUgaW5wdXQgdG8gYSBzb2Z0bWF4IG11c3QgaGF2ZSBhdCBsZWFzdCAyIHZhbHVlcycpO1xuICB9XG59XG5cbi8qKlxuICogTWVhblNxdWFyZWRDb3N0Tm9kZSByZXByZXNlbnRzIGEgbWVhbiBzcXVhcmVkIGNvc3Qgb3BlcmF0aW9uXG4gKiBpbiB0aGUgZ3JhcGguXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWVhblNxdWFyZWRDb3N0Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgTEFCRUwgPSAnbGFiZWwnO1xuICBzdGF0aWMgcmVhZG9ubHkgUFJFRElDVElPTiA9ICdwcmVkaWN0aW9uJztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIGxhYmVsOiBUZW5zb3IsIHByaXZhdGUgcHJlZGljdGlvbjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdNZWFuIFNxdWFyZWQgQ29zdCcsIHtsYWJlbCwgcHJlZGljdGlvbn0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLmxhYmVsLnNoYXBlLCB0aGlzLnByZWRpY3Rpb24uc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIG1lYW5TcXVhcmVkQ29zdCBvcDogbGFiZWwgc2hhcGUgKCcgKyB0aGlzLmxhYmVsLnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggcHJlZGljdGlvbiBzaGFwZSAoJyArIHRoaXMucHJlZGljdGlvbi5zaGFwZSArICcpLicpO1xuICB9XG59XG5cbi8qKlxuICogQXJnTWF4Tm9kZSByZXByZXNlbnRzIGFuIGFyZ21heCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4Tm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwdWJsaWMgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdBcmdNYXgnLCB7eH0sIG5ldyBUZW5zb3IoWzFdKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnguc2hhcGUpID4gMCxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBhcmdtYXggb3A6IGlucHV0IHRlbnNvciBtdXN0IGhhdmUgYXQgbGVhc3Qgb25lIGVudHJ5LicpO1xuICB9XG59XG5cbi8qKlxuICogQXJnTWF4RXF1YWxzTm9kZSByZXByZXNlbnRzIGEgYXJnbWF4IGVxdWFscyBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4RXF1YWxzTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdBcmdNYXhFcXVhbHMnLCB7eDEsIHgyfSwgbmV3IFRlbnNvcihbMV0pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLngxLnNoYXBlLCB0aGlzLngyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBBcmdNYXhFcXVhbHMgb3A6IHgxIHNoYXBlICgnICsgdGhpcy54MS5zaGFwZSArXG4gICAgICAgICAgICAnKSBtdXN0IG1hdGNoIHgyIHNoYXBlICgnICsgdGhpcy54Mi5zaGFwZSArICcpLicpO1xuICB9XG59XG5cbi8qKlxuICogU3BsaXQgbm9kZXMgYXJlIHVzZWQgdG8gYWNjdW11bGF0ZSBiYWNrcHJvcCBkZXJpdmF0aXZlcyB3aGVuIGEgbm9kZSdzIG91dHB1dFxuICogdGVuc29yIGlzIGNvbnN1bWVkIGJ5IG11bHRpcGxlIG5vZGVzLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3BsaXROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIG91dHB1dHM6IFRlbnNvcltdID0gW107XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NwbGl0Tm9kZScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgbmV3IGNvbnN1bWVyIG9mIHRoaXMgc3BsaXQgbm9kZSwgaS5lLiBhIG5ldyBub2RlIHRoYXQgdXNlcyB0aGVcbiAgICogbm9kZSdzIG91dHB1dCB0ZW5zb3IuXG4gICAqL1xuICBnZXROZXdPdXRwdXRUZW5zb3IoKTogVGVuc29yIHtcbiAgICBjb25zdCBvdXRwdXQgPSBuZXcgVGVuc29yKHRoaXMuaW5wdXRzW1NwbGl0Tm9kZS5YXS5zaGFwZSk7XG4gICAgb3V0cHV0Lm5vZGUgPSB0aGlzO1xuICAgIHRoaXMub3V0cHV0cy5wdXNoKG91dHB1dCk7XG4gICAgcmV0dXJuIG91dHB1dDtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgdHlwZSBBcnJheURhdGEgPVxuICAgIE5EQXJyYXl8bnVtYmVyfG51bWJlcltdfG51bWJlcltdW118bnVtYmVyW11bXVtdfG51bWJlcltdW11bXVtdO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtJbml0aWFsaXplciwgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIsIFplcm9zSW5pdGlhbGl6ZXJ9IGZyb20gJy4vaW5pdGlhbGl6ZXJzJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIEEgbGF5ZXJzIHN1Z2FyIGNsYXNzIGFyb3VuZCB0aGUgZ3JhcGggdGhhdCBpbml0aWFsaXplcyB2YXJpYWJsZXNcbiAqIGF1dG9tYXRpY2FsbHkgZm9yIGxheWVycy5cbiAqL1xuZXhwb3J0IGNsYXNzIEdyYXBoTGF5ZXJzIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBnOiBHcmFwaCkge31cblxuICBkZW5zZShcbiAgICAgIG5hbWU6IHN0cmluZywgeDogVGVuc29yLCB1bml0czogbnVtYmVyLFxuICAgICAgYWN0aXZhdGlvbjogKCh4OiBUZW5zb3IpID0+IFRlbnNvcil8bnVsbCA9IG51bGwsIHVzZUJpYXMgPSB0cnVlLFxuICAgICAga2VybmVsSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyID0gbmV3IFZhcmlhbmNlU2NhbGluZ0luaXRpYWxpemVyKCksXG4gICAgICBiaWFzSW5pdGlhbGl6ZXI6IEluaXRpYWxpemVyID0gbmV3IFplcm9zSW5pdGlhbGl6ZXIoKSkge1xuICAgIGNvbnN0IHdlaWdodHMgPSB0aGlzLmcudmFyaWFibGUoXG4gICAgICAgIG5hbWUgKyAnLXdlaWdodHMnLFxuICAgICAgICBrZXJuZWxJbml0aWFsaXplci5pbml0aWFsaXplKFt4LnNoYXBlWzBdLCB1bml0c10sIHguc2hhcGVbMF0sIHVuaXRzKSk7XG5cbiAgICBsZXQgb3V0ID0gdGhpcy5nLm1hdG11bCh4LCB3ZWlnaHRzKTtcblxuICAgIGlmICh1c2VCaWFzKSB7XG4gICAgICBjb25zdCBiaWFzID0gdGhpcy5nLnZhcmlhYmxlKFxuICAgICAgICAgIG5hbWUgKyAnLWJpYXMnLFxuICAgICAgICAgIGJpYXNJbml0aWFsaXplci5pbml0aWFsaXplKFt1bml0c10sIHguc2hhcGVbMF0sIHVuaXRzKSk7XG4gICAgICBvdXQgPSB0aGlzLmcuYWRkKG91dCwgYmlhcyk7XG4gICAgfVxuXG4gICAgaWYgKGFjdGl2YXRpb24gIT0gbnVsbCkge1xuICAgICAgb3V0ID0gYWN0aXZhdGlvbihvdXQpO1xuICAgIH1cblxuICAgIHJldHVybiBvdXQ7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgZGF0YXNldCBmcm9tICcuL2RhdGFzZXQnO1xuaW1wb3J0IHtHcmFwaCwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXlNYXRoQ1BVfSBmcm9tICcuL21hdGgvbWF0aF9jcHUnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7T3B0aW1pemVyfSBmcm9tICcuL29wdGltaXplcic7XG5pbXBvcnQge0Nvc3RSZWR1Y3Rpb24sIEZlZWRFbnRyeSwgU2Vzc2lvbn0gZnJvbSAnLi9zZXNzaW9uJztcblxuY29uc3QgREVGQVVMVF9FVkFMX0lOVEVSVkFMX01TID0gMTUwMDtcbmNvbnN0IERFRkFVTFRfQ09TVF9JTlRFUlZBTF9NUyA9IDUwMDtcbmNvbnN0IERFRkFVTFRfSU5GRVJFTkNFX0VYQU1QTEVfSU5URVJWQUxfTVMgPSAzMDAwO1xuXG5leHBvcnQgaW50ZXJmYWNlIEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlciB7XG4gIGJhdGNoZXNUcmFpbmVkQ2FsbGJhY2s/OiAodG90YWxCYXRjaGVzVHJhaW5lZDogbnVtYmVyKSA9PiB2b2lkO1xuICBhdmdDb3N0Q2FsbGJhY2s/OiAoYXZnQ29zdDogU2NhbGFyKSA9PiB2b2lkO1xuICBtZXRyaWNDYWxsYmFjaz86IChtZXRyaWM6IE5EQXJyYXkpID0+IHZvaWQ7XG4gIGluZmVyZW5jZUV4YW1wbGVzQ2FsbGJhY2s/OlxuICAgICAgKGZlZWRzOiBGZWVkRW50cnlbXVtdLCBpbmZlcmVuY2VWYWx1ZXM6IE5EQXJyYXlbXSkgPT4gdm9pZDtcbiAgaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNDYWxsYmFjaz86IChleGFtcGxlc1BlclNlYzogbnVtYmVyKSA9PiB2b2lkO1xuICB0cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2s/OiAoZXhhbXBsZXNQZXJTZWM6IG51bWJlcikgPT4gdm9pZDtcbiAgdG90YWxUaW1lQ2FsbGJhY2s/OiAodG90YWxUaW1lU2VjOiBudW1iZXIpID0+IHZvaWQ7XG4gIGRvbmVUcmFpbmluZ0NhbGxiYWNrPzogKCkgPT4gdm9pZDtcbn1cblxuZXhwb3J0IGVudW0gTWV0cmljUmVkdWN0aW9uIHtcbiAgU1VNLFxuICBNRUFOXG59XG5cbi8qKlxuICogQSBjbGFzcyB0aGF0IGRyaXZlcyB0aGUgdHJhaW5pbmcgb2YgYSBncmFwaCBtb2RlbCBnaXZlbiBhIGRhdGFzZXQuIEl0IGFsbG93c1xuICogdGhlIHVzZXIgdG8gcHJvdmlkZSBhIHNldCBvZiBjYWxsYmFja3MgZm9yIG1lYXN1cmVtZW50cyBsaWtlIGNvc3QsIGFjY3VyYWN5LFxuICogYW5kIHNwZWVkIG9mIHRyYWluaW5nLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGhSdW5uZXIge1xuICBwcml2YXRlIGNvc3RUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSB0cmFpbkZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXTtcbiAgcHJpdmF0ZSBiYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBvcHRpbWl6ZXI6IE9wdGltaXplcjtcbiAgcHJpdmF0ZSBjdXJyZW50VHJhaW5Mb29wTnVtQmF0Y2hlczogbnVtYmVyfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBjb3N0SW50ZXJ2YWxNczogbnVtYmVyO1xuXG4gIHByaXZhdGUgbWV0cmljVGVuc29yOiBUZW5zb3J8dW5kZWZpbmVkO1xuICBwcml2YXRlIG1ldHJpY0ZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXXx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljQmF0Y2hTaXplOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIG1ldHJpY1JlZHVjdGlvbjogTWV0cmljUmVkdWN0aW9uO1xuICBwcml2YXRlIG1ldHJpY0ludGVydmFsTXM6IG51bWJlcjtcblxuICBwcml2YXRlIGluZmVyZW5jZVRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIGluZmVyZW5jZUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXXx1bmRlZmluZWQ7XG4gIHByaXZhdGUgaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXM6IG51bWJlcjtcbiAgcHJpdmF0ZSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ6IG51bWJlcjtcblxuICAvLyBSdW50aW1lIGluZm9ybWF0aW9uLlxuICBwcml2YXRlIGlzVHJhaW5pbmc6IGJvb2xlYW47XG4gIHByaXZhdGUgdG90YWxCYXRjaGVzVHJhaW5lZDogbnVtYmVyO1xuICBwcml2YXRlIGJhdGNoZXNUcmFpbmVkVGhpc1J1bjogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RDb21wdXRlZE1ldHJpYzogTkRBcnJheTtcblxuICBwcml2YXRlIGlzSW5mZXJyaW5nOiBib29sZWFuO1xuICBwcml2YXRlIGN1cnJlbnRJbmZlcmVuY2VMb29wTnVtUGFzc2VzOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIGluZmVyZW5jZVBhc3Nlc1RoaXNSdW46IG51bWJlcjtcblxuICBwcml2YXRlIHRyYWluU3RhcnRUaW1lc3RhbXA6IG51bWJlcjtcbiAgcHJpdmF0ZSBsYXN0Q29zdFRpbWVzdGFtcCA9IDA7XG4gIHByaXZhdGUgbGFzdEV2YWxUaW1lc3RhbXAgPSAwO1xuXG4gIHByaXZhdGUgbGFzdFN0b3BUaW1lc3RhbXA6IG51bWJlcnxudWxsO1xuICBwcml2YXRlIHRvdGFsSWRsZVRpbWVNcyA9IDA7XG5cbiAgcHJpdmF0ZSB6ZXJvU2NhbGFyOiBTY2FsYXI7XG4gIHByaXZhdGUgbWV0cmljQmF0Y2hTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIG1hdGg6IE5EQXJyYXlNYXRoLCBwcml2YXRlIHNlc3Npb246IFNlc3Npb24sXG4gICAgICBwcml2YXRlIGV2ZW50T2JzZXJ2ZXI6IEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlcikge1xuICAgIHRoaXMucmVzZXRTdGF0aXN0aWNzKCk7XG4gICAgdGhpcy56ZXJvU2NhbGFyID0gU2NhbGFyLm5ldygwKTtcbiAgfVxuXG4gIHJlc2V0U3RhdGlzdGljcygpIHtcbiAgICB0aGlzLnRvdGFsQmF0Y2hlc1RyYWluZWQgPSAwO1xuICAgIHRoaXMudG90YWxJZGxlVGltZU1zID0gMDtcbiAgICB0aGlzLmxhc3RTdG9wVGltZXN0YW1wID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCB0aGUgdHJhaW5pbmcgbG9vcCB3aXRoIGFuIG9wdGlvbmFsIG51bWJlciBvZiBiYXRjaGVzIHRvIHRyYWluIGZvci5cbiAgICogT3B0aW9uYWxseSB0YWtlcyBhIG1ldHJpYyB0ZW5zb3IgYW5kIGZlZWQgZW50cmllcyB0byBjb21wdXRlIHBlcmlvZGljYWxseS5cbiAgICogVGhpcyBjYW4gYmUgdXNlZCBmb3IgY29tcHV0aW5nIGFjY3VyYWN5LCBvciBhIHNpbWlsYXIgbWV0cmljLlxuICAgKi9cbiAgdHJhaW4oXG4gICAgICBjb3N0VGVuc29yOiBUZW5zb3IsIHRyYWluRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLCBiYXRjaFNpemU6IG51bWJlcixcbiAgICAgIG9wdGltaXplcjogT3B0aW1pemVyLCBudW1CYXRjaGVzPzogbnVtYmVyLCBtZXRyaWNUZW5zb3I/OiBUZW5zb3IsXG4gICAgICBtZXRyaWNGZWVkRW50cmllcz86IEZlZWRFbnRyeVtdLCBtZXRyaWNCYXRjaFNpemU/OiBudW1iZXIsXG4gICAgICBtZXRyaWNSZWR1Y3Rpb24gPSBNZXRyaWNSZWR1Y3Rpb24uTUVBTixcbiAgICAgIGV2YWxJbnRlcnZhbE1zID0gREVGQVVMVF9FVkFMX0lOVEVSVkFMX01TLFxuICAgICAgY29zdEludGVydmFsTXMgPSBERUZBVUxUX0NPU1RfSU5URVJWQUxfTVMpIHtcbiAgICB0aGlzLmNvc3RUZW5zb3IgPSBjb3N0VGVuc29yO1xuICAgIHRoaXMudHJhaW5GZWVkRW50cmllcyA9IHRyYWluRmVlZEVudHJpZXM7XG4gICAgdGhpcy5tZXRyaWNUZW5zb3IgPSBtZXRyaWNUZW5zb3I7XG4gICAgdGhpcy5tZXRyaWNGZWVkRW50cmllcyA9IG1ldHJpY0ZlZWRFbnRyaWVzO1xuICAgIGlmIChtZXRyaWNCYXRjaFNpemUgIT0gbnVsbCAmJiB0aGlzLm1ldHJpY0JhdGNoU2l6ZSAhPT0gbWV0cmljQmF0Y2hTaXplKSB7XG4gICAgICBpZiAodGhpcy5tZXRyaWNCYXRjaFNpemVTY2FsYXIgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhciA9IFNjYWxhci5uZXcobWV0cmljQmF0Y2hTaXplKTtcbiAgICB9XG4gICAgdGhpcy5tZXRyaWNCYXRjaFNpemUgPSBtZXRyaWNCYXRjaFNpemU7XG4gICAgdGhpcy5tZXRyaWNSZWR1Y3Rpb24gPSBtZXRyaWNSZWR1Y3Rpb247XG4gICAgdGhpcy5iYXRjaFNpemUgPSBiYXRjaFNpemU7XG4gICAgdGhpcy5vcHRpbWl6ZXIgPSBvcHRpbWl6ZXI7XG5cbiAgICB0aGlzLm1ldHJpY0ludGVydmFsTXMgPSBldmFsSW50ZXJ2YWxNcztcbiAgICB0aGlzLmNvc3RJbnRlcnZhbE1zID0gY29zdEludGVydmFsTXM7XG4gICAgdGhpcy5jdXJyZW50VHJhaW5Mb29wTnVtQmF0Y2hlcyA9IG51bUJhdGNoZXM7XG5cbiAgICB0aGlzLmJhdGNoZXNUcmFpbmVkVGhpc1J1biA9IDA7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnRyYWluU3RhcnRUaW1lc3RhbXAgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICB0aGlzLnRyYWluTmV0d29yaygpO1xuICB9XG5cbiAgc3RvcFRyYWluaW5nKCkge1xuICAgIHRoaXMuaXNUcmFpbmluZyA9IGZhbHNlO1xuICAgIHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgfVxuXG4gIHJlc3VtZVRyYWluaW5nKCkge1xuICAgIHRoaXMuaXNUcmFpbmluZyA9IHRydWU7XG4gICAgaWYgKHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgIT0gbnVsbCkge1xuICAgICAgdGhpcy50b3RhbElkbGVUaW1lTXMgKz0gcGVyZm9ybWFuY2Uubm93KCkgLSB0aGlzLmxhc3RTdG9wVGltZXN0YW1wO1xuICAgIH1cbiAgICB0aGlzLnRyYWluTmV0d29yaygpO1xuICB9XG5cbiAgcHJpdmF0ZSB0cmFpbk5ldHdvcmsoKSB7XG4gICAgaWYgKHRoaXMuYmF0Y2hlc1RyYWluZWRUaGlzUnVuID09PSB0aGlzLmN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzKSB7XG4gICAgICB0aGlzLnN0b3BUcmFpbmluZygpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc1RyYWluaW5nKSB7XG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmRvbmVUcmFpbmluZ0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmRvbmVUcmFpbmluZ0NhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICBjb25zdCBzaG91bGRDb21wdXRlQ29zdCA9IHRoaXMuZXZlbnRPYnNlcnZlci5hdmdDb3N0Q2FsbGJhY2sgIT0gbnVsbCAmJlxuICAgICAgICAoc3RhcnQgLSB0aGlzLmxhc3RDb3N0VGltZXN0YW1wID4gdGhpcy5jb3N0SW50ZXJ2YWxNcyk7XG4gICAgaWYgKHNob3VsZENvbXB1dGVDb3N0KSB7XG4gICAgICB0aGlzLmxhc3RDb3N0VGltZXN0YW1wID0gc3RhcnQ7XG4gICAgfVxuXG4gICAgY29uc3QgY29zdFJlZHVjdGlvbiA9XG4gICAgICAgIHNob3VsZENvbXB1dGVDb3N0ID8gQ29zdFJlZHVjdGlvbi5NRUFOIDogQ29zdFJlZHVjdGlvbi5OT05FO1xuXG4gICAgdGhpcy5tYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBhdmdDb3N0ID0gdGhpcy5zZXNzaW9uLnRyYWluKFxuICAgICAgICAgIHRoaXMuY29zdFRlbnNvciwgdGhpcy50cmFpbkZlZWRFbnRyaWVzLCB0aGlzLmJhdGNoU2l6ZSxcbiAgICAgICAgICB0aGlzLm9wdGltaXplciwgY29zdFJlZHVjdGlvbik7XG5cbiAgICAgIGlmIChzaG91bGRDb21wdXRlQ29zdCkge1xuICAgICAgICBjb25zdCB0cmFpblRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKSAtIHN0YXJ0O1xuXG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5hdmdDb3N0Q2FsbGJhY2shKGF2Z0Nvc3QpO1xuXG4gICAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIudHJhaW5FeGFtcGxlc1BlclNlY0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgICBjb25zdCBleGFtcGxlc1BlclNlYyA9ICh0aGlzLmJhdGNoU2l6ZSAqIDEwMDAgLyB0cmFpblRpbWUpO1xuICAgICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci50cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2soZXhhbXBsZXNQZXJTZWMpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIubWV0cmljQ2FsbGJhY2sgIT0gbnVsbCAmJlxuICAgICAgICAgIHRoaXMubWV0cmljRmVlZEVudHJpZXMgIT0gbnVsbCAmJlxuICAgICAgICAgIHN0YXJ0IC0gdGhpcy5sYXN0RXZhbFRpbWVzdGFtcCA+IHRoaXMubWV0cmljSW50ZXJ2YWxNcykge1xuICAgICAgICB0aGlzLmxhc3RFdmFsVGltZXN0YW1wID0gc3RhcnQ7XG5cbiAgICAgICAgaWYgKHRoaXMubGFzdENvbXB1dGVkTWV0cmljICE9IG51bGwpIHtcbiAgICAgICAgICB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYy5kaXNwb3NlKCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMgPSB0aGlzLmNvbXB1dGVNZXRyaWMoKTtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLm1ldHJpY0NhbGxiYWNrKHRoaXMubGFzdENvbXB1dGVkTWV0cmljKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci50b3RhbFRpbWVDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci50b3RhbFRpbWVDYWxsYmFjayhcbiAgICAgICAgICAgIChzdGFydCAtIHRoaXMudHJhaW5TdGFydFRpbWVzdGFtcCkgLyAxMDAwKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5iYXRjaGVzVHJhaW5lZFRoaXNSdW4rKztcbiAgICAgIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCsrO1xuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmJhdGNoZXNUcmFpbmVkQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIuYmF0Y2hlc1RyYWluZWRDYWxsYmFjayh0aGlzLnRvdGFsQmF0Y2hlc1RyYWluZWQpO1xuICAgICAgfVxuXG4gICAgfSk7XG4gICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnRyYWluTmV0d29yaygpKTtcbiAgfVxuXG4gIGluZmVyKFxuICAgICAgaW5mZXJlbmNlVGVuc29yOiBUZW5zb3IsIGluZmVyZW5jZUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSxcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVJbnRlcnZhbE1zID0gREVGQVVMVF9JTkZFUkVOQ0VfRVhBTVBMRV9JTlRFUlZBTF9NUyxcbiAgICAgIGluZmVyZW5jZUV4YW1wbGVDb3VudCA9IDUsIG51bVBhc3Nlcz86IG51bWJlcikge1xuICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjayA9PSBudWxsICYmXG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrID09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnQ2Fubm90IHN0YXJ0IGluZmVyZW5jZSBsb29wLCBubyBpbmZlcmVuY2UgZXhhbXBsZSBvciAnICtcbiAgICAgICAgICAnZXhhbXBsZXMvc2VjIG9ic2VydmVyIHByb3ZpZGVkLicpO1xuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgZmVlZCB2YWx1ZXMgYXJlIHByb3ZpZGVycywgYW5kIG5vdCBOREFycmF5cy5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGluZmVyZW5jZUZlZWRFbnRyaWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBmZWVkRW50cnkgPSBpbmZlcmVuY2VGZWVkRW50cmllc1tpXTtcblxuICAgICAgaWYgKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnQ2Fubm90IHN0YXJ0IGluZmVyZW5jZSBvbiB0aGUgbW9kZWwgcnVubmVyIHdpdGggZmVlZCBlbnRyaWVzIG9mICcgK1xuICAgICAgICAgICAgJ3R5cGUgTkRBcnJheS4gUGxlYXNlIHVzZSBJbnB1dFByb3ZpZGVycy4nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmluZmVyZW5jZUV4YW1wbGVJbnRlcnZhbE1zID0gaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXM7XG4gICAgdGhpcy5pbmZlcmVuY2VUZW5zb3IgPSBpbmZlcmVuY2VUZW5zb3I7XG4gICAgdGhpcy5pbmZlcmVuY2VGZWVkRW50cmllcyA9IGluZmVyZW5jZUZlZWRFbnRyaWVzO1xuICAgIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50ID0gaW5mZXJlbmNlRXhhbXBsZUNvdW50O1xuICAgIHRoaXMuY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXMgPSBudW1QYXNzZXM7XG4gICAgaWYgKCF0aGlzLmlzSW5mZXJyaW5nKSB7XG4gICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4gPSAwO1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmluZmVyTmV0d29yaygpKTtcbiAgICB9XG4gICAgdGhpcy5pc0luZmVycmluZyA9IHRydWU7XG4gIH1cblxuICBwcml2YXRlIGluZmVyTmV0d29yaygpIHtcbiAgICBpZiAoIXRoaXMuaXNJbmZlcnJpbmcgfHxcbiAgICAgICAgdGhpcy5pbmZlcmVuY2VQYXNzZXNUaGlzUnVuID09PSB0aGlzLmN1cnJlbnRJbmZlcmVuY2VMb29wTnVtUGFzc2VzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5tYXRoLnNjb3BlKChrZWVwLCB0cmFjaykgPT4ge1xuICAgICAgY29uc3QgZmVlZHM6IEZlZWRFbnRyeVtdW10gPSBbXTtcbiAgICAgIGNvbnN0IGluZmVyZW5jZVZhbHVlczogTkRBcnJheVtdID0gW107XG5cbiAgICAgIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50OyBpKyspIHtcbiAgICAgICAgLy8gUG9wdWxhdGUgYSBuZXcgRmVlZEVudHJ5W10gcG9wdWxhdGVkIHdpdGggTkRBcnJheXMuXG4gICAgICAgIGNvbnN0IG5kYXJyYXlGZWVkRW50cmllczogRmVlZEVudHJ5W10gPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmluZmVyZW5jZUZlZWRFbnRyaWVzIS5sZW5ndGg7IGorKykge1xuICAgICAgICAgIGNvbnN0IGZlZWRFbnRyeSA9IHRoaXMuaW5mZXJlbmNlRmVlZEVudHJpZXMhW2pdO1xuICAgICAgICAgIG5kYXJyYXlGZWVkRW50cmllcy5wdXNoKHtcbiAgICAgICAgICAgIHRlbnNvcjogZmVlZEVudHJ5LnRlbnNvcixcbiAgICAgICAgICAgIGRhdGE6XG4gICAgICAgICAgICAgICAgdHJhY2soKGZlZWRFbnRyeS5kYXRhIGFzIElucHV0UHJvdmlkZXIpLmdldE5leHRDb3B5KHRoaXMubWF0aCkpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgZmVlZHMucHVzaChuZGFycmF5RmVlZEVudHJpZXMpO1xuXG4gICAgICAgIGluZmVyZW5jZVZhbHVlcy5wdXNoKFxuICAgICAgICAgICAgdGhpcy5zZXNzaW9uLmV2YWwodGhpcy5pbmZlcmVuY2VUZW5zb3IsIG5kYXJyYXlGZWVkRW50cmllcykpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICAvLyBGb3JjZSBhIEdQVSBkb3dubG9hZCwgc2luY2UgaW5mZXJlbmNlIHJlc3VsdHMgYXJlIGdlbmVyYWxseSBuZWVkZWQgb25cbiAgICAgICAgLy8gdGhlIENQVSBhbmQgaXQncyBtb3JlIGZhaXIgdG8gaW5jbHVkZSBibG9ja2luZyBvbiB0aGUgR1BVIHRvIGNvbXBsZXRlXG4gICAgICAgIC8vIGl0cyB3b3JrIGZvciB0aGUgaW5mZXJlbmNlIG1lYXN1cmVtZW50LlxuICAgICAgICBpbmZlcmVuY2VWYWx1ZXNbaW5mZXJlbmNlVmFsdWVzLmxlbmd0aCAtIDFdLmdldFZhbHVlcygpO1xuXG4gICAgICAgIGNvbnN0IGluZmVyZW5jZUV4YW1wbGVzUGVyU2VjVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQ7XG5cbiAgICAgICAgY29uc3QgZXhhbXBsZXNQZXJTZWMgPVxuICAgICAgICAgICAgKHRoaXMuaW5mZXJlbmNlRXhhbXBsZUNvdW50ICogMTAwMCAvIGluZmVyZW5jZUV4YW1wbGVzUGVyU2VjVGltZSk7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrIShleGFtcGxlc1BlclNlYyk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrKGZlZWRzLCBpbmZlcmVuY2VWYWx1ZXMpO1xuICAgICAgfVxuICAgICAgdGhpcy5pbmZlcmVuY2VQYXNzZXNUaGlzUnVuKys7XG5cbiAgICB9KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuaW5mZXJOZXR3b3JrKCksIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMpO1xuICB9XG5cbiAgc3RvcEluZmVycmluZygpIHtcbiAgICB0aGlzLmlzSW5mZXJyaW5nID0gZmFsc2U7XG4gIH1cblxuICBpc0luZmVyZW5jZVJ1bm5pbmcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuaXNJbmZlcnJpbmc7XG4gIH1cblxuICBjb21wdXRlTWV0cmljKCk6IFNjYWxhciB7XG4gICAgaWYgKHRoaXMubWV0cmljRmVlZEVudHJpZXMgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgY29tcHV0ZSBtZXRyaWMsIG5vIG1ldHJpYyBGZWVkRW50cmllcyBwcm92aWRlZC4nKTtcbiAgICB9XG5cbiAgICBsZXQgbWV0cmljID0gdGhpcy56ZXJvU2NhbGFyO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm1ldHJpY0JhdGNoU2l6ZSE7IGkrKykge1xuICAgICAgICBjb25zdCBtZXRyaWNWYWx1ZSA9XG4gICAgICAgICAgICB0aGlzLnNlc3Npb24uZXZhbCh0aGlzLm1ldHJpY1RlbnNvciEsIHRoaXMubWV0cmljRmVlZEVudHJpZXMhKTtcblxuICAgICAgICBtZXRyaWMgPSB0aGlzLm1hdGguYWRkKG1ldHJpYywgbWV0cmljVmFsdWUpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5tZXRyaWNSZWR1Y3Rpb24gPT09IE1ldHJpY1JlZHVjdGlvbi5NRUFOKSB7XG4gICAgICAgIG1ldHJpYyA9IHRoaXMubWF0aC5kaXZpZGUobWV0cmljLCB0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhcik7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBtZXRyaWM7XG4gICAgfSk7XG4gIH1cblxuICBnZXRUb3RhbEJhdGNoZXNUcmFpbmVkKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZDtcbiAgfVxuXG4gIGdldExhc3RDb21wdXRlZE1ldHJpYygpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYztcbiAgfVxuXG4gIHNldE1hdGgobWF0aDogTkRBcnJheU1hdGgpIHtcbiAgICB0aGlzLm1hdGggPSBtYXRoO1xuICB9XG5cbiAgc2V0U2Vzc2lvbihzZXNzaW9uOiBTZXNzaW9uKSB7XG4gICAgdGhpcy5zZXNzaW9uID0gc2Vzc2lvbjtcbiAgfVxuXG4gIHNldEluZmVyZW5jZVRlbnNvcihpbmZlcmVuY2VUZW5zb3I6IFRlbnNvcikge1xuICAgIHRoaXMuaW5mZXJlbmNlVGVuc29yID0gaW5mZXJlbmNlVGVuc29yO1xuICB9XG5cbiAgc2V0SW5mZXJlbmNlRXhhbXBsZUNvdW50KGluZmVyZW5jZUV4YW1wbGVDb3VudDogbnVtYmVyKSB7XG4gICAgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgPSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtDb25zdGFudE5vZGUsIEdyYXBoLCBOb2RlLCBQbGFjZWhvbGRlck5vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIHByaW9yaXR5X3F1ZXVlIGZyb20gJy4vcHJpb3JpdHlfcXVldWUnO1xuaW1wb3J0IHtQcmlvcml0eVF1ZXVlfSBmcm9tICcuL3ByaW9yaXR5X3F1ZXVlJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5cbi8qKlxuICogR2l2ZW4gYSB0YXJnZXQgbm9kZSBpbiBhIGdyYXBoLCBhY2N1bXVsYXRlIHRoZSBzZXQgb2YgYWxsIG5vZGVzIHRoYXQgbmVlZCB0b1xuICogYmUgZXZhbHVhdGVkIGluIG9yZGVyIHRvIGV2YWx1YXRlIHRoZSB0YXJnZXQgZ3JhcGguIFRyYXZlcnNhbCBzdG9wcyBhbnl3aGVyZVxuICogYSBub2RlJ3MgdmFsdWVzIGFyZSBmZWQgaW4gZXh0ZXJuYWxseSB2aWEgXCJmZWVkIGRpY3RzXCIuXG4gKiBAcGFyYW0gbm9kZXMgVGhlIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSB0ZXJtaW5hdGluZ05vZGVzIFRoZSBzZXQgb2Ygbm9kZXMgdGhhdCBzdG9wIHRyYXZlcnNhbC5cbiAqIEByZXR1cm4gVGhlIHVub3JkZXJlZCBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvIGJlIGV2YWx1YXRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFVub3JkZXJlZEV2YWx1YXRpb25TZXQoXG4gICAgbm9kZXM6IE5vZGVbXSwgdGVybWluYXRpbmdOb2RlczogTm9kZVtdKTogTm9kZVtdIHtcbiAgY29uc3QgdGVybWluYXRpbmdOb2RlTWFwOiB7W2lkOiBudW1iZXJdOiBOb2RlfSA9IHt9O1xuICBjb25zdCBzZWVuOiB7W2lkOiBudW1iZXJdOiBOb2RlfSA9IHt9O1xuICBjb25zdCBzZXQ6IE5vZGVbXSA9IFtdO1xuICBjb25zdCB2aXNpdDogTm9kZVtdID0gbm9kZXMuc2xpY2UoKTtcbiAgdGVybWluYXRpbmdOb2Rlcy5mb3JFYWNoKG5vZGUgPT4gdGVybWluYXRpbmdOb2RlTWFwW25vZGUuaWRdID0gbm9kZSk7XG4gIC8qIEZsb29kIGZpbGw6IFdoaWxlIHRoZSAndG8gdmlzaXQnIHN0YWNrIGlzIG5vdCBlbXB0eSwgcG9wIGEgbm9kZSBvZmYgb2YgaXQuXG4gICAqIElmIHRoZSBub2RlIGhhcyBub3QgeWV0IGJlZW4gdmlzaXRlZCwgYWRkIGl0IHRvIHRoZSBzZXQsIG1hcmsgaXQgYXMgc2VlbixcbiAgICogYW5kIGVucXVldWUgYWxsIG9mIGl0cyBhbmNlc3RvciAoaW5wdXQpIG5vZGVzLiAqL1xuICB3aGlsZSAodmlzaXQubGVuZ3RoICE9PSAwKSB7XG4gICAgY29uc3QgY3VyID0gdmlzaXQucG9wKCkhO1xuICAgIGlmIChzZWVuW2N1ci5pZF0gPT0gbnVsbCkge1xuICAgICAgaWYgKHRlcm1pbmF0aW5nTm9kZU1hcFtjdXIuaWRdID09IG51bGwpIHtcbiAgICAgICAgT2JqZWN0LmtleXMoY3VyLmlucHV0cylcbiAgICAgICAgICAgIC5tYXAoaW5wdXROYW1lID0+IGN1ci5pbnB1dHNbaW5wdXROYW1lXSlcbiAgICAgICAgICAgIC5mb3JFYWNoKGlucHV0ID0+IHZpc2l0LnB1c2goaW5wdXQubm9kZSkpO1xuICAgICAgfVxuICAgICAgc2V0LnB1c2goY3VyKTtcbiAgICAgIHNlZW5bY3VyLmlkXSA9IGN1cjtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHNldDtcbn1cblxuLyoqXG4gKiBHaXZlbiBhIHNldCBvZiBub2RlcywgY29tcHV0ZSB0aGVpciBvcmRlciBzdWNoIHRoYXQgYWxsIGRlcGVuZGVudCBub2RlcyBhcmVcbiAqIGV2YWx1YXRlZCBhZnRlciB0aGVpciBkZXBlbmRlZXMuIFRoaXMgaXMgdGhlICdpbmZlcmVuY2Ugb3JkZXInIGZvciBub2RlcyBpblxuICogdGhlIG9wZXJhdGlvbiBncmFwaC5cbiAqIEBwYXJhbSB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0IFRoZSB1bm9yZGVyZWQgc2V0IG9mIG5vZGVzIHRoYXQgbmVlZCB0byBiZVxuICogZXZhbHVhdGVkLlxuICogQHJldHVybiBUaGUgaW5wdXQgbm9kZXMgaW4gZm9yd2FyZCBldmFsdWF0aW9uIG9yZGVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3JkZXJlZEV2YWx1YXRpb25TZXQodW5vcmRlcmVkRXZhbHVhdGlvblNldDogTm9kZVtdKTpcbiAgICBOb2RlW10ge1xuICAvKiBBIHByaW9yaXR5IHF1ZXVlIGlzIHVzZWQsIHdoZXJlIHRoZSBwcmlvcml0eSBpcyB0aGUgcmVtYWluaW5nIG51bWJlciBvZlxuICAgKiB1bmV2YWx1YXRlZCBub2RlcyB3aG9zZSBpbnB1dHMgY29tZSBmcm9tIHRoZSBlbGVtZW50IG5vZGUuIFRoaXMgZ3VhcmFudGVlc1xuICAgKiB0aGF0IGFsbCBkb3duc3RyZWFtIG5vZGVzIHdpbGwgYmUgZGVxdWV1ZWQgYmVmb3JlIHRoZWlyIGFuY2VzdG9ycy4gKi9cbiAgY29uc3Qgc2V0OiBOb2RlW10gPSBbXTtcbiAgY29uc3Qgbm9kZUluZGljZXM6IHtbaWQ6IG51bWJlcl06IG51bWJlcn0gPSB7fTtcbiAgY29uc3QgcGVuZGluZ0RlcGVuZGVuY2llczoge1tpZDogbnVtYmVyXTogbnVtYmVyfSA9IHt9O1xuXG4gIC8qIFRoZSBxdWV1ZSBwcmlvcml0eSBjYWxsYmFjayBsb29rcyBhdCB0aGUgbnVtYmVyIG9mIHBlbmRpbmcgZGVwZW5kZW5jaWVzIG9mXG4gICAqIGEgZ2l2ZW4gbm9kZS4gVGhlIHF1ZXVlIGluZGV4IG9ic2VydmVyIGNhbGxiYWNrIG1haW50YWlucyB0aGUgbG9jYXRpb24gb2ZcbiAgICogZWFjaCBub2RlIGluIHRoZSBhcnJheSwgZm9yIHByaW9yaXR5IHVwZGF0ZXMuICovXG4gIGNvbnN0IG5vZGVRdWV1ZSA9IG5ldyBQcmlvcml0eVF1ZXVlPE5vZGU+KFxuICAgICAgKGE6IE5vZGUsIGI6IE5vZGUpID0+IHByaW9yaXR5X3F1ZXVlLmRlZmF1bHRDb21wYXJlKFxuICAgICAgICAgIHBlbmRpbmdEZXBlbmRlbmNpZXNbYS5pZF0sIHBlbmRpbmdEZXBlbmRlbmNpZXNbYi5pZF0pLFxuICAgICAgKG5vZGU6IE5vZGUsIG5ld0luZGV4OiBudW1iZXIpID0+IG5vZGVJbmRpY2VzW25vZGUuaWRdID0gbmV3SW5kZXgpO1xuXG4gIHVub3JkZXJlZEV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHBlbmRpbmdEZXBlbmRlbmNpZXNbbm9kZS5pZF0gPSAwKTtcblxuICAvKiBGb3IgZXZlcnkgZGVzY2VuZGVudCBvZiBhIG5vZGUgKG91dHB1dCBvZiBhbmNlc3RvciBpcyBpbnB1dCB0byBkZXNjZW5kYW50KSxcbiAgICogaW5jcmVtZW50IHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBmb3IgdGhlIGFuY2VzdG9yLiBUaGlzIHByZXBhcmVzXG4gICAqIHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBhcyBhIHByaW9yaXR5IG1hcC4gKi9cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKFxuICAgICAgbm9kZSA9PiBPYmplY3Qua2V5cyhub2RlLmlucHV0cylcbiAgICAgICAgICAgICAgICAgIC5tYXAoa2V5ID0+IG5vZGUuaW5wdXRzW2tleV0pXG4gICAgICAgICAgICAgICAgICAuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmluZGV4T2YoaW5wdXQubm9kZSkgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgcGVuZGluZ0RlcGVuZGVuY2llc1tpbnB1dC5ub2RlLmlkXSsrO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9KSk7XG5cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4gbm9kZVF1ZXVlLmVucXVldWUobm9kZSkpO1xuXG4gIHdoaWxlICghbm9kZVF1ZXVlLmVtcHR5KCkpIHtcbiAgICBzZXQudW5zaGlmdChub2RlUXVldWUuZGVxdWV1ZSgpKTtcbiAgICAvKiBBcyBlYWNoIG5vZGUgaXMgdmlzaXRlZCwgZGVjcmVtZW50IHRoZSAncGVuZGluZyBkZXBlbmRlbmN5IGNvdW50JyBvZlxuICAgICAqIGVhY2ggYW5jZXN0b3IsIGFuZCB0ZWxsIHRoZSBwcmlvcml0eSBxdWV1ZSB0aGF0IHRoZSBwcmlvcml0eSBoYXMgY2hhbmdlZC5cbiAgICAgKi9cbiAgICBPYmplY3Qua2V5cyhzZXRbMF0uaW5wdXRzKS5tYXAoa2V5ID0+IHNldFswXS5pbnB1dHNba2V5XSkuZm9yRWFjaChpbnB1dCA9PiB7XG4gICAgICBpZiAodW5vcmRlcmVkRXZhbHVhdGlvblNldC5pbmRleE9mKGlucHV0Lm5vZGUpID09PSAtMSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBwZW5kaW5nRGVwZW5kZW5jaWVzW2lucHV0Lm5vZGUuaWRdLS07XG4gICAgICBub2RlUXVldWUudXBkYXRlKGlucHV0Lm5vZGUsIG5vZGVJbmRpY2VzW2lucHV0Lm5vZGUuaWRdKTtcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiBzZXQ7XG59XG5cbi8qKlxuICogQHJldHVybiBUcnVlIGlmZiB0aGUgbm9kZSBpcyBhbiBpbnB1dCBub2RlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNJbnB1dE5vZGUobm9kZTogTm9kZSk6IGJvb2xlYW4ge1xuICByZXR1cm4gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpLmxlbmd0aCA9PT0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNob3VsZEJhY2tQcm9wKHQ6IFRlbnNvcik6IGJvb2xlYW4ge1xuICByZXR1cm4gISh0Lm5vZGUgaW5zdGFuY2VvZiBDb25zdGFudE5vZGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNQYXNzdGhyb3VnaE5vZGUobm9kZTogTm9kZSwgbWFwOiBUZW5zb3JBcnJheU1hcCk6IGJvb2xlYW4ge1xuICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGtleXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBpbnB1dCA9IG5vZGUuaW5wdXRzW2tleXNbaV1dO1xuICAgIGlmIChtYXAuZ2V0KGlucHV0LCB0cnVlKSA9PT0gbWFwLmdldChub2RlLm91dHB1dCwgdHJ1ZSkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIGdwZ3B1X3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL2dwZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL3JlbmRlcl9uZGFycmF5X2dwdV91dGlsJztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi9tYXRoL3dlYmdsL3dlYmdsX3V0aWwnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG5leHBvcnQge0NoZWNrcG9pbnRMb2FkZXJ9IGZyb20gJy4vY2hlY2twb2ludF9sb2FkZXInO1xuZXhwb3J0IHtEYXRhU3RhdHMsIEluTWVtb3J5RGF0YXNldH0gZnJvbSAnLi9kYXRhc2V0JztcbmV4cG9ydCB7R3JhcGgsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5leHBvcnQge0dyYXBoUnVubmVyLCBHcmFwaFJ1bm5lckV2ZW50T2JzZXJ2ZXIsIE1ldHJpY1JlZHVjdGlvbn0gZnJvbSAnLi9ncmFwaF9ydW5uZXInO1xuZXhwb3J0IHtDb25zdGFudEluaXRpYWxpemVyLCBJbml0aWFsaXplciwgTkRBcnJheUluaXRpYWxpemVyLCBPbmVzSW5pdGlhbGl6ZXIsIFJhbmRvbU5vcm1hbEluaXRpYWxpemVyLCBSYW5kb21UcnVuY2F0ZWROb3JtYWxJbml0aWFsaXplciwgUmFuZG9tVW5pZm9ybUluaXRpYWxpemVyLCBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciwgWmVyb3NJbml0aWFsaXplcn0gZnJvbSAnLi9pbml0aWFsaXplcnMnO1xuZXhwb3J0IHtJbkNQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIsIEluR1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciwgSW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5leHBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuZXhwb3J0IHtOREFycmF5TWF0aENQVX0gZnJvbSAnLi9tYXRoL21hdGhfY3B1JztcbmV4cG9ydCB7TkRBcnJheU1hdGhHUFV9IGZyb20gJy4vbWF0aC9tYXRoX2dwdSc7XG5leHBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuZXhwb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vbWF0aC93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmV4cG9ydCB7T3B0aW1pemVyfSBmcm9tICcuL29wdGltaXplcic7XG5leHBvcnQge0Nvc3RSZWR1Y3Rpb24sIEZlZWRFbnRyeSwgU2Vzc2lvbn0gZnJvbSAnLi9zZXNzaW9uJztcbmV4cG9ydCB7U0dET3B0aW1pemVyfSBmcm9tICcuL3NnZF9vcHRpbWl6ZXInO1xuLy8gU2Vjb25kIGxldmVsIGV4cG9ydHMuXG5leHBvcnQge2NvbnZfdXRpbCwgZ3BncHVfdXRpbCwgcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwsIHV0aWwsIHdlYmdsX3V0aWx9O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcblxuLyoqXG4gKiBJbml0aWFsaXplciBpbnRlcmZhY2UsIGFsbCBpbml0aWFsaXplciBpbXBsZW1lbnQgdGhpcyBpbnRlcmZhY2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5pdGlhbGl6ZXIge1xuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5O1xufVxuXG5leHBvcnQgY2xhc3MgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBzY2FsZSA9IDEuMCxcbiAgICAgIHByaXZhdGUgbW9kZTogJ2Zhbl9pbid8J2Zhbl9vdXQnfCdmYW5fYXZnJyA9ICdmYW5faW4nLFxuICAgICAgcHJpdmF0ZSBkaXN0cmlidXRpb246ICd1bmlmb3JtJ3wnbm9ybWFsJyA9ICdub3JtYWwnKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIGxldCBuID0gMDtcbiAgICBpZiAodGhpcy5tb2RlID09PSAnZmFuX2luJykge1xuICAgICAgbiA9IGlucHV0VW5pdHM7XG4gICAgfSBlbHNlIGlmICh0aGlzLm1vZGUgPT09ICdmYW5fb3V0Jykge1xuICAgICAgbiA9IG91dHB1dFVuaXRzO1xuICAgIH0gZWxzZSBpZiAodGhpcy5tb2RlID09PSAnZmFuX2F2ZycpIHtcbiAgICAgIG4gPSAoaW5wdXRVbml0cyArIG91dHB1dFVuaXRzKSAvIDI7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnVW5leHBlY3RlZCBtb2RlIGZvciB2YXJpYW5jZSBzY2FsaW5nIGluaXRpYWxpemVyOiAnICsgdGhpcy5tb2RlKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5kaXN0cmlidXRpb24gPT09ICdub3JtYWwnKSB7XG4gICAgICByZXR1cm4gTkRBcnJheS5yYW5kVHJ1bmNhdGVkTm9ybWFsKFxuICAgICAgICAgIHdlaWdodHNTaGFwZSwgMC4wLCBNYXRoLnNxcnQodGhpcy5zY2FsZSAvIG4pKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuZGlzdHJpYnV0aW9uID09PSAndW5pZm9ybScpIHtcbiAgICAgIHJldHVybiBOREFycmF5LnJhbmRVbmlmb3JtKFxuICAgICAgICAgIHdlaWdodHNTaGFwZSwgMC4wLCBNYXRoLnNxcnQoMyAqIHRoaXMuc2NhbGUgLyBuKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnVW5leHBlY3RlZCBkaXN0cmlidXRpb24gZm9yIHZhcmlhbmNlIHNjYWxpbmcgaW5pdGlhbGl6ZXI6ICcgK1xuICAgICAgICAgIHRoaXMuZGlzdHJpYnV0aW9uKTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFplcm9zSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBPbmVzSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gICAgdmFsdWVzLmZpbGwoMSk7XG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQ29uc3RhbnRJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB2YWx1ZSA9IDApIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgY29uc3QgdmFsdWVzID0gTkRBcnJheS56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuICAgIHZhbHVlcy5maWxsKHRoaXMudmFsdWUpO1xuICAgIHJldHVybiB2YWx1ZXM7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBuZGFycmF5OiBOREFycmF5KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiB0aGlzLm5kYXJyYXk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFJhbmRvbU5vcm1hbEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1lYW4gPSAwLCBwcml2YXRlIHN0ZGV2ID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmROb3JtYWwod2VpZ2h0c1NoYXBlLCB0aGlzLm1lYW4sIHRoaXMuc3RkZXYpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSYW5kb21UcnVuY2F0ZWROb3JtYWxJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBtZWFuID0gMCwgcHJpdmF0ZSBzdGRldiA9IC4wNSkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICByZXR1cm4gTkRBcnJheS5yYW5kVHJ1bmNhdGVkTm9ybWFsKHdlaWdodHNTaGFwZSwgdGhpcy5tZWFuLCB0aGlzLnN0ZGV2KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmFuZG9tVW5pZm9ybUluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1pbnZhbCA9IC0uMDUsIHByaXZhdGUgbWF4dmFsID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmRVbmlmb3JtKHdlaWdodHNTaGFwZSwgdGhpcy5taW52YWwsIHRoaXMubWF4dmFsKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuLyoqXG4gKiBUaGUgaW50ZXJmYWNlIGZvciBpbnB1dCBwcm92aWRlcnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5wdXRQcm92aWRlciB7XG4gIC8qKlxuICAgKiBHZXQgdGhlIG5leHQgaW5wdXQgYXMgYSBjb3B5LiBUaGlzIGlzIGltcG9ydGFudCBiZWNhdXNlIHRoZSBkYXRhIG1pZ2h0XG4gICAqIGdldCB1cGxvYWRlZCB0byB0aGUgR1BVIGFuZCBtb2RpZnkgdGhlIG9yaWdpbmFsIGRhdGEuXG4gICAqIEBwYXJhbSBtYXRoIE5EQXJyYXlNYXRoXG4gICAqL1xuICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXk7XG4gIC8qKlxuICAgKiBEaXNwb3NlIHRoZSBpbnB1dCBjb3B5LlxuICAgKiBAcGFyYW0gbWF0aCBOREFycmF5TWF0aFxuICAgKiBAcGFyYW0gY29weSBUaGUgY29weSBwcm92aWRlZCBmcm9tIGdldE5leHRDb3B5XG4gICAqL1xuICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSk6IHZvaWQ7XG59XG5cbi8qKlxuICogQSBjb21tb24gaW50ZXJmYWNlIGZvciBzaHVmZmxlZCBpbnB1dCBwcm92aWRlciBidWlsZGVycy4gVGhpcyByZXR1cm5zXG4gKiBJbnB1dFByb3ZpZGVycyB0aGF0IGFyZSBzeW5jaHJvbml6ZWQuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXJzKCk6IElucHV0UHJvdmlkZXJbXTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgaW1wbGVtZW50c1xuICAgIFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBwcm90ZWN0ZWQgc2h1ZmZsZWRJbmRpY2VzOiBVaW50MzJBcnJheTtcbiAgcHJvdGVjdGVkIG51bUlucHV0czogbnVtYmVyO1xuXG4gIHByb3RlY3RlZCBpZHggPSAwO1xuICAvLyBDb3VudGVyIGZvciBob3cgbWFueSB0aW1lcyB0aGUgY3VycmVudCBpbmRleCBoYXMgYmVlbiBjYWxsZWQuIFJlc2V0cyB0byAwXG4gIC8vIHdoZW4gaXQgcmVhY2hlcyB0aGUgbnVtYmVyIG9mIGlucHV0cy5cbiAgcHJvdGVjdGVkIGlucHV0Q291bnRlciA9IDA7XG4gIHByb3RlY3RlZCBlcG9jaCA9IDA7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYW4gYEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyYC4gQWxsIG9mIHRoZSBpbnB1dHMgbXVzdCBiZVxuICAgKiBpbiBtZW1vcnkuXG4gICAqIEBwYXJhbSBpbnB1dHMgQWxsIG9mIHRoZSBpbnB1dHMsIHNpemU6IFtudW1iZXIgb2YgaW5wdXRzXVtudW1iZXIgb2ZcbiAgICogZXhhbXBsZXNdLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJvdGVjdGVkIGlucHV0czogTkRBcnJheVtdW10pIHtcbiAgICB0aGlzLnNodWZmbGVkSW5kaWNlcyA9IHV0aWwuY3JlYXRlU2h1ZmZsZWRJbmRpY2VzKGlucHV0c1swXS5sZW5ndGgpO1xuICAgIHRoaXMubnVtSW5wdXRzID0gaW5wdXRzLmxlbmd0aDtcblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgbnVtYmVyIG9mIGV4YW1wbGVzIGluIGVhY2ggaW5wdXQgbWF0Y2hlcy5cbiAgICBjb25zdCBudW1FeGFtcGxlcyA9IHRoaXMuaW5wdXRzWzBdLmxlbmd0aDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubnVtSW5wdXRzOyBpKyspIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMuaW5wdXRzW2ldLmxlbmd0aCA9PT0gbnVtRXhhbXBsZXMsXG4gICAgICAgICAgJ051bWJlciBvZiBleGFtcGxlcyBtdXN0IG1hdGNoIGFjcm9zcyBkaWZmZXJlbnQgaW5wdXRzLicpO1xuICAgIH1cblxuICAgIC8vIE1ha2Ugc3VyZSB0aGUgc2hhcGVzIHdpdGhpbiBpbnB1dHMgYWxsIG1hdGNoLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1JbnB1dHM7IGkrKykge1xuICAgICAgY29uc3QgaW5wdXRTaGFwZSA9IHRoaXMuaW5wdXRzW2ldWzBdLnNoYXBlO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmlucHV0c1tpXS5sZW5ndGg7IGorKykge1xuICAgICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGlucHV0U2hhcGUsIHRoaXMuaW5wdXRzW2ldW2pdLnNoYXBlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0Q3VycmVudEV4YW1wbGVJbmRleCgpOiBudW1iZXIge1xuICAgIGNvbnN0IHJldHVybklkeCA9IHRoaXMuaWR4O1xuXG4gICAgdGhpcy5pbnB1dENvdW50ZXIrKztcbiAgICBpZiAodGhpcy5pbnB1dENvdW50ZXIgPj0gdGhpcy5udW1JbnB1dHMpIHtcbiAgICAgIHRoaXMuaWR4Kys7XG4gICAgICB0aGlzLmlucHV0Q291bnRlciA9IDA7XG5cbiAgICAgIGlmICh0aGlzLmlkeCA+PSB0aGlzLmlucHV0c1swXS5sZW5ndGgpIHtcbiAgICAgICAgdGhpcy5pZHggPSAwO1xuICAgICAgICB0aGlzLmVwb2NoKys7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXR1cm5JZHg7XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0TmV4dElucHV0KGlucHV0SWQ6IG51bWJlcik6IE5EQXJyYXkge1xuICAgIGNvbnN0IGN1cnJlbnRFeGFtcGxlSW5kZXggPSB0aGlzLmdldEN1cnJlbnRFeGFtcGxlSW5kZXgoKTtcblxuICAgIHJldHVybiB0aGlzLmlucHV0c1tpbnB1dElkXVt0aGlzLnNodWZmbGVkSW5kaWNlc1tjdXJyZW50RXhhbXBsZUluZGV4XV07XG4gIH1cblxuICBnZXRFcG9jaCgpIHtcbiAgICByZXR1cm4gdGhpcy5lcG9jaDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGlucHV0IHByb3ZpZGVycyB3aGljaCBzaHVmZmxlIHRoZSBpbnB1dHMgYW5kIHN0YXkgaW4gc3luYy5cbiAgICovXG4gIGdldElucHV0UHJvdmlkZXJzKCk6IElucHV0UHJvdmlkZXJbXSB7XG4gICAgY29uc3QgaW5wdXRQcm92aWRlcnM6IElucHV0UHJvdmlkZXJbXSA9IFtdO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bUlucHV0czsgaSsrKSB7XG4gICAgICBpbnB1dFByb3ZpZGVycy5wdXNoKHRoaXMuZ2V0SW5wdXRQcm92aWRlcihpKSk7XG4gICAgfVxuICAgIHJldHVybiBpbnB1dFByb3ZpZGVycztcbiAgfVxuXG4gIGFic3RyYWN0IGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKTogSW5wdXRQcm92aWRlcjtcbn1cblxuLyoqXG4gKiBBbiBpbiBDUFUgbWVtb3J5IFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgdGhhdCBzaHVmZmxlcyBOREFycmF5cyBvbiB0aGVcbiAqIENQVSBhbmQga2VlcHMgdGhlbSBtdXR1YWxseSBpbiBzeW5jLlxuICovXG5leHBvcnQgY2xhc3MgSW5DUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIGV4dGVuZHNcbiAgICBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBnZXRJbnB1dFByb3ZpZGVyKGlucHV0SWQ6IG51bWJlcikge1xuICAgIGNvbnN0IHNodWZmbGVkSW5wdXRQcm92aWRlciA9IHRoaXM7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZ2V0TmV4dENvcHkobWF0aDogTkRBcnJheU1hdGgpOiBOREFycmF5IHtcbiAgICAgICAgcmV0dXJuIE5EQXJyYXkubGlrZShzaHVmZmxlZElucHV0UHJvdmlkZXIuZ2V0TmV4dElucHV0KGlucHV0SWQpKTtcbiAgICAgIH0sXG4gICAgICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSkge1xuICAgICAgICBjb3B5LmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG59XG5cbi8qKlxuICogQW4gaW4gR1BVIG1lbW9yeSBTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIHRoYXQgc2h1ZmZsZXMgTkRBcnJheXMgb24gdGhlXG4gKiBHUFUgYW5kIGtlZXBzIHRoZW0gbXV0dWFsbHkgaW4gc3luYy4gVGhpcyBpcyBtb3JlIHBlcmZvcm1hbnQgdGhhbiB0aGUgQ1BVXG4gKiB2ZXJzaW9uIGFzIHRleHR1cmVzIHdpbGwgc3RheSBpbiBtZW1vcnksIGhvd2V2ZXIgdGhpcyBpcyBtb3JlIEdQVSBtZW1vcnlcbiAqIGludGVuc2l2ZSBhcyBpdCBrZWVwcyB0ZXh0dXJlcyByZXNpZGVudCBpbiBHUFUgbWVtb3J5LlxuICovXG5leHBvcnQgY2xhc3MgSW5HUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIGV4dGVuZHNcbiAgICBJbk1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIge1xuICBnZXRJbnB1dFByb3ZpZGVyKGlucHV0SWQ6IG51bWJlcikge1xuICAgIGNvbnN0IHNodWZmbGVkSW5wdXRQcm92aWRlciA9IHRoaXM7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZ2V0TmV4dENvcHkobWF0aDogTkRBcnJheU1hdGgpOiBOREFycmF5IHtcbiAgICAgICAgcmV0dXJuIG1hdGguY2xvbmUoc2h1ZmZsZWRJbnB1dFByb3ZpZGVyLmdldE5leHRJbnB1dChpbnB1dElkKSk7XG4gICAgICB9LFxuICAgICAgZGlzcG9zZUNvcHkobWF0aDogTkRBcnJheU1hdGgsIGNvcHk6IE5EQXJyYXkpIHtcbiAgICAgICAgY29weS5kaXNwb3NlKCk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbi8qKiBBIG5vZGUncyBhY3RpdmF0aW9uIGZ1bmN0aW9uIGFuZCBpdHMgZGVyaXZhdGl2ZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0PFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgaW5wdXQ6IFQpOiBUO1xuICBkZXI8VCBleHRlbmRzIE5EQXJyYXk+KG1hdGg6IE5EQXJyYXlNYXRoLCBpbnB1dDogVCwgb3V0cHV0OiBUKTogVDtcbn1cblxuZXhwb3J0IGNsYXNzIFRhbkhGdW5jIGltcGxlbWVudHMgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0KG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGgudGFuaCh4KTtcbiAgICB9KTtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSwgeTogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIGNvbnN0IHlTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh5LCB5KTtcbiAgICAgIC8vIDEgLSB5XjIuXG4gICAgICByZXR1cm4gbWF0aC5zY2FsYXJNaW51c0FycmF5KFNjYWxhci5PTkUsIHlTcXVhcmVkKTtcbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmVMVUZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5yZWx1KHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGguc3RlcCh4KTtcbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2lnbW9pZEZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5zaWdtb2lkKHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgLy8geSAqICgxIC0geSkgPSB5IC0geV4yXG4gICAgICBjb25zdCB5U3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoeSwgeSk7XG4gICAgICByZXR1cm4gbWF0aC5zdWIoeSwgeVNxdWFyZWQpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTcXVhcmVGdW5jIGltcGxlbWVudHMgQWN0aXZhdGlvbkZ1bmN0aW9uIHtcbiAgb3V0cHV0KG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgcmV0dXJuIG1hdGguZWxlbWVudFdpc2VNdWwoeCwgeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICAvLyBkeS9keCA9IDIqeC5cbiAgICAgIHJldHVybiBtYXRoLnNjYWxhclRpbWVzQXJyYXkoU2NhbGFyLlRXTywgeCk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgeDFTaGFwZTogbnVtYmVyW10sIHgyU2hhcGU6IG51bWJlcltdLCBheGlzOiBudW1iZXIsXG4gICAgZXJyb3JNZXNzYWdlUHJlZml4ID0gJycpIHtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB4MVNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArICdDb25jYXQzRCB4MSBzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuICB1dGlsLmFzc2VydChcbiAgICAgIHgyU2hhcGUubGVuZ3RoID09PSAzLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgJ0NvbmNhdDNEIHgyIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG5cbiAgdXRpbC5hc3NlcnQoXG4gICAgICBheGlzID49IDAgJiYgYXhpcyA8IDMsICdBeGlzIGZvciBjb25jYXQzRCBtdXN0IGJlIGJldHdlZW4gMCBhbmQgMi4nKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IDM7IGkrKykge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAoaSA9PT0gYXhpcykgfHwgKHgxU2hhcGVbaV0gPT09IHgyU2hhcGVbaV0pLFxuICAgICAgICBlcnJvck1lc3NhZ2VQcmVmaXggK1xuICAgICAgICAgICAgYFNoYXBlICgke3gxU2hhcGV9KSBkb2VzIG5vdCBtYXRjaCAoJHt4MlNoYXBlfSkgYWxvbmcgYCArXG4gICAgICAgICAgICBgbm9uLWNvbmNhdGVuYXRlZCBheGlzLmApO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZShcbiAgICB4MVNoYXBlOiBudW1iZXJbXSwgeDJTaGFwZTogbnVtYmVyW10sXG4gICAgYXhpczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgdXRpbC5hc3NlcnQoeDFTaGFwZS5sZW5ndGggPT09IDMsICdDb25jYXQzRCB4MSBzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuICB1dGlsLmFzc2VydCh4MlNoYXBlLmxlbmd0aCA9PT0gMywgJ0NvbmNhdDNEIHgyc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcblxuICBjb25zdCBvdXRwdXRTaGFwZSA9IHgxU2hhcGUuc2xpY2UoKTtcbiAgb3V0cHV0U2hhcGVbYXhpc10gKz0geDJTaGFwZVtheGlzXTtcbiAgcmV0dXJuIG91dHB1dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICBpbnB1dFNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplOiBudW1iZXIsXG4gICAgZGVwdGg6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHplcm9QYWQ/OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICBpZiAoemVyb1BhZCA9PSBudWxsKSB7XG4gICAgemVyb1BhZCA9IGNvbXB1dGVEZWZhdWx0UGFkKGlucHV0U2hhcGVSb3dDb2xEZXB0aCwgZmllbGRTaXplLCBzdHJpZGUpO1xuICB9XG4gIGNvbnN0IGlucHV0Um93cyA9IGlucHV0U2hhcGVSb3dDb2xEZXB0aFswXTtcbiAgY29uc3QgaW5wdXRDb2xzID0gaW5wdXRTaGFwZVJvd0NvbERlcHRoWzFdO1xuICBjb25zdCBvdXRwdXRSb3dzID0gKGlucHV0Um93cyAtIGZpZWxkU2l6ZSArIDIgKiB6ZXJvUGFkKSAvIHN0cmlkZSArIDE7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgdXRpbC5pc0ludChvdXRwdXRSb3dzKSxcbiAgICAgIGBUaGUgb3V0cHV0ICMgb2Ygcm93cyAoJHtvdXRwdXRSb3dzfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgIGBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcblxuICBjb25zdCBvdXRwdXRDb2xzID0gKGlucHV0Q29scyAtIGZpZWxkU2l6ZSArIDIgKiB6ZXJvUGFkKSAvIHN0cmlkZSArIDE7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgdXRpbC5pc0ludChvdXRwdXRDb2xzKSxcbiAgICAgIGBUaGUgb3V0cHV0ICMgb2YgY29sdW1ucyAoJHtvdXRwdXRDb2xzfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgYCArXG4gICAgICAgICAgYHRoZSBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcblxuICByZXR1cm4gW291dHB1dFJvd3MsIG91dHB1dENvbHMsIGRlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVEZWZhdWx0UGFkKFxuICAgIGlucHV0U2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplOiBudW1iZXIsXG4gICAgc3RyaWRlOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5mbG9vcigoaW5wdXRTaGFwZVswXSAqIChzdHJpZGUgLSAxKSAtIHN0cmlkZSArIGZpZWxkU2l6ZSkgLyAyKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVUZXhTaGFwZUZyb20zRChcbiAgICBzaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtzaGFwZVJvd0NvbERlcHRoWzBdLCBzaGFwZVJvd0NvbERlcHRoWzFdICogc2hhcGVSb3dDb2xEZXB0aFsyXV07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlV2VpZ2h0c1NoYXBlNEQoXG4gICAgaW5wdXREZXB0aDogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIGZTaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbZlNpemUsIGZTaXplLCBpbnB1dERlcHRoLCBvdXRwdXREZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlV2VpZ2h0c1RleFNoYXBlKFxuICAgIGlucHV0RGVwdGg6IG51bWJlciwgb3V0cHV0RGVwdGg6IG51bWJlcixcbiAgICBmaWVsZFNpemU6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2ZpZWxkU2l6ZSAqIGZpZWxkU2l6ZSAqIGlucHV0RGVwdGgsIG91dHB1dERlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVCaWFzZXNUZXhTaGFwZShvdXRwdXREZXB0aDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbMSwgb3V0cHV0RGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZURpbGF0ZWRSQyhcbiAgICByYzogW251bWJlciwgbnVtYmVyXSwgb3JpZ1N0cmlkZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGNvbnN0IHJvd3NEaWxhdGVkID0gKHJjWzBdIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgY29uc3QgY29sc0RpbGF0ZWQgPSAocmNbMV0gLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICByZXR1cm4gW3Jvd3NEaWxhdGVkLCBjb2xzRGlsYXRlZF07XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVNoYXBlcyhcbiAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBzcmNBcmVhID0gc291cmNlU2l6ZVswXSAqIHNvdXJjZVNpemVbMV07XG4gIGNvbnN0IGRzdEFyZWEgPSBkZXN0U2l6ZVswXSAqIGRlc3RTaXplWzFdO1xuICBpZiAoc3JjQXJlYSAhPT0gZHN0QXJlYSkge1xuICAgIGNvbnN0IHNyY1N0ciA9ICdbJyArIHNvdXJjZVNpemVbMF0gKyAnLCAnICsgc291cmNlU2l6ZVsxXSArICddJztcbiAgICBjb25zdCBkc3RTdHIgPSAnWycgKyBkZXN0U2l6ZVswXSArICcsICcgKyBkZXN0U2l6ZVsxXSArICddJztcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdjb3B5MkQgc2hhcGVzIGhhdmUgZGlmZmVyZW50IGFyZWFzOlxcbiAgc291cmNlU2l6ZSAnICsgc3JjU3RyICtcbiAgICAgICAgJywgYXJlYSAnICsgc3JjQXJlYSArICdcXG4gIGRlc3RTaXplICcgKyBkc3RTdHIgKyAnLCBhcmVhICcgKyBkc3RBcmVhKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbi8qKlxuICogQW4gZXJyb3IgZnVuY3Rpb24gYW5kIGl0cyBkZXJpdmF0aXZlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVsZW1lbnRXaXNlQ29zdEZ1bmN0aW9uIHtcbiAgY29zdDxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIHgxOiBULCB4MjogVCk6IFQ7XG4gIGRlcjxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIHgxOiBULCB4MjogVCk6IFQ7XG4gIGRpc3Bvc2UoKTogdm9pZDtcbn1cblxuZXhwb3J0IGNsYXNzIFNxdWFyZUNvc3RGdW5jIGltcGxlbWVudHMgRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24ge1xuICBwcml2YXRlIGhhbGZPbmUgPSBTY2FsYXIubmV3KDAuNSk7XG5cbiAgY29zdChtYXRoOiBOREFycmF5TWF0aCwgeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogTkRBcnJheSB7XG4gICAgY29uc3QgZGlmZiA9IG1hdGguc3ViKHgxLCB4Mik7XG4gICAgY29uc3QgZGlmZlNxdWFyZWQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKGRpZmYsIGRpZmYpO1xuICAgIGNvbnN0IHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0aGlzLmhhbGZPbmUsIGRpZmZTcXVhcmVkKTtcblxuICAgIGRpZmYuZGlzcG9zZSgpO1xuICAgIGRpZmZTcXVhcmVkLmRpc3Bvc2UoKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IE5EQXJyYXkge1xuICAgIHJldHVybiBtYXRoLnN1Yih4MSwgeDIpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmhhbGZPbmUuZGlzcG9zZSgpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb3B5MmRfdXRpbCBmcm9tICcuL2NvcHkyZF91dGlsJztcblxuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbmV4cG9ydCB0eXBlIFNjb3BlUmVzdWx0ID0gTkRBcnJheVtdfE5EQXJyYXl8dm9pZDtcblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIE5EQXJyYXlNYXRoIHtcbiAgcHJpdmF0ZSBuZGFycmF5U2NvcGVzOiBOREFycmF5W11bXSA9IFtdO1xuICBwcml2YXRlIGFjdGl2ZVNjb3BlOiBOREFycmF5W107XG5cbiAgcHJpdmF0ZSBuZGFycmF5c1RvS2VlcDogTkRBcnJheVtdW10gPSBbXTtcbiAgcHJpdmF0ZSBhY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwOiBOREFycmF5W10gPSBbXTtcblxuICAvKipcbiAgICogQHBhcmFtIHNhZmVNb2RlIEluIHNhZmUgbW9kZSwgeW91IG11c3QgdXNlIG1hdGggb3BlcmF0aW9ucyBpbnNpZGVcbiAgICogYSBtYXRoLnNjb3BlKCkgd2hpY2ggd2lsbCBhdXRvbWF0aWNhbGx5IGNsZWFuIHVwIGludGVybWVkaWF0ZSBOREFycmF5cy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgc2FmZU1vZGU6IGJvb2xlYW4pIHt9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBtYXRoIHNjb3BlLiBQdXQgY2hhaW5lZCBtYXRoIG9wZXJhdGlvbnMgaW5zaWRlIGEgc2NvcGVcbiAgICogZnVuY3Rpb24gY2xvc3VyZSBzbyB0aGF0IHRoZSBsaWJyYXJ5IGF1dG9tYXRpY2FsbHkgY2xlYW5zIHVwIE5EQXJyYXlzXG4gICAqIGZyb20gaW50ZXJtZWRpYXRlIG1hdGggb3BlcmF0aW9ucy4gWW91IG11c3QgY3JlYXRlIGEgc2NvcGUgaW4gc2FmZSBtb2RlXG4gICAqIHRvIGNhbGwgbWF0aCBvcGVyYXRpb25zLiBJZiBhIHJlc3VsdCBpcyByZXR1cm5lZCBmcm9tIHRoZSBzY29wZSwgaXQgd2lsbFxuICAgKiBhbHNvIGJlIHRyYWNrZWQsIHdoaWNoIG1lYW5zIHRoZXJlIG11c3QgYmUgeWV0IGFub3RoZXIgd3JhcHBpbmcgc2NvcGUuXG4gICAqIEBwYXJhbSBzY29wZUZuIFRoZSBmdW5jdGlvbiB0byBleGVjdXRlIHdpdGggY2hhaW5lZCBtYXRoIG9wZXJhdGlvbnMuXG4gICAqL1xuICBzY29wZTxUIGV4dGVuZHMgU2NvcGVSZXN1bHQ+KFxuICAgICAgc2NvcGVGbjpcbiAgICAgICAgICAoa2VlcDogPFQxIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVDEpID0+IFQxLFxuICAgICAgICAgICB0cmFjazogPFQyIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVDIpID0+IFQyKSA9PiBUKSB7XG4gICAgdGhpcy5zdGFydFNjb3BlKCk7XG5cbiAgICBjb25zdCBrZWVwRm4gPSA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUID0+IHRoaXMua2VlcChuZGFycmF5KTtcbiAgICBjb25zdCB0cmFja0ZuID0gPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCA9PiB0aGlzLnRyYWNrKG5kYXJyYXkpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHNjb3BlRm4oa2VlcEZuLCB0cmFja0ZuKTtcblxuICAgIHRoaXMuZW5kU2NvcGUocmVzdWx0KTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgYSBzY29wZS4gVXNlIHRoaXMgd2l0aCBlbmRTY29wZSgpIHRvIGFjaGlldmUgdGhlIHNhbWUgZnVuY3Rpb25hbGl0eVxuICAgKiBhcyBzY29wZSgpIHdpdGhvdXQgdGhlIG5lZWQgZm9yIGEgZnVuY3Rpb24gY2xvc3VyZS5cbiAgICovXG4gIHN0YXJ0U2NvcGUoKSB7XG4gICAgY29uc3QgbmV3U2NvcGU6IE5EQXJyYXlbXSA9IFtdO1xuICAgIHRoaXMubmRhcnJheVNjb3Blcy5wdXNoKG5ld1Njb3BlKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlID0gbmV3U2NvcGU7XG5cbiAgICBjb25zdCBuZXdOREFycmF5c1RvS2VlcDogTkRBcnJheVtdID0gW107XG4gICAgdGhpcy5uZGFycmF5c1RvS2VlcC5wdXNoKG5ld05EQXJyYXlzVG9LZWVwKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXAgPSBuZXdOREFycmF5c1RvS2VlcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmQgYSBzY29wZS4gVXNlIHRoaXMgd2l0aCBzdGFydFNjb3BlKCkgdG8gYWNoaWV2ZSB0aGUgc2FtZSBmdW5jdGlvbmFsaXR5XG4gICAqIGFzIHNjb3BlKCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgYSBmdW5jdGlvbiBjbG9zdXJlLlxuICAgKi9cbiAgZW5kU2NvcGUocmVzdWx0OiBTY29wZVJlc3VsdCkge1xuICAgIC8vIERpc3Bvc2UgdGhlIGN1cnJlbnQgc2NvcGUuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmFjdGl2ZVNjb3BlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBuZGFycmF5ID0gdGhpcy5hY3RpdmVTY29wZVtpXTtcblxuICAgICAgaWYgKHRoaXMuaXNOREFycmF5RGF0YUluTGlzdChuZGFycmF5LCB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXApIHx8XG4gICAgICAgICAgKHJlc3VsdCAhPSBudWxsICYmIHJlc3VsdCBpbnN0YW5jZW9mIE5EQXJyYXkgJiZcbiAgICAgICAgICAgbmRhcnJheS5nZXREYXRhKCkgPT09IChyZXN1bHQgYXMgTkRBcnJheSkuZ2V0RGF0YSgpKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIG5kYXJyYXkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIC8vIFBvcCB0aGUgY3VycmVudCBzY29wZS5cbiAgICB0aGlzLm5kYXJyYXlTY29wZXMucG9wKCk7XG4gICAgdGhpcy5hY3RpdmVTY29wZSA9IHRoaXMubmRhcnJheVNjb3Blcy5sZW5ndGggPT09IDAgP1xuICAgICAgICBudWxsISA6XG4gICAgICAgIHRoaXMubmRhcnJheVNjb3Blc1t0aGlzLm5kYXJyYXlTY29wZXMubGVuZ3RoIC0gMV07XG5cbiAgICAvLyBUcmFjayB0aGUgY3VycmVudCByZXN1bHQgaW4gdGhlIHBhcmVudCBzY29wZS5cbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgTkRBcnJheSAmJlxuICAgICAgICAhdGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KHJlc3VsdCwgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwKSkge1xuICAgICAgdGhpcy50cmFjayhyZXN1bHQpO1xuICAgIH0gZWxzZSBpZiAoQXJyYXkuaXNBcnJheShyZXN1bHQpKSB7XG4gICAgICByZXN1bHQuZm9yRWFjaChyID0+IHtcbiAgICAgICAgaWYgKHIgaW5zdGFuY2VvZiBOREFycmF5ICYmXG4gICAgICAgICAgICAhdGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KHIsIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCkpIHtcbiAgICAgICAgICB0aGlzLnRyYWNrKHIpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLm5kYXJyYXlzVG9LZWVwLnBvcCgpO1xuICAgIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCA9IHRoaXMubmRhcnJheXNUb0tlZXAubGVuZ3RoID09PSAwID9cbiAgICAgICAgbnVsbCEgOlxuICAgICAgICB0aGlzLm5kYXJyYXlzVG9LZWVwW3RoaXMubmRhcnJheXNUb0tlZXAubGVuZ3RoIC0gMV07XG4gIH1cblxuICBwcml2YXRlIGlzTkRBcnJheURhdGFJbkxpc3QobmRhcnJheTogTkRBcnJheSwgbmRhcnJheUxpc3Q6IE5EQXJyYXlbXSkge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmRhcnJheUxpc3QubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChuZGFycmF5TGlzdFtpXS5nZXREYXRhKCkgPT09IG5kYXJyYXkuZ2V0RGF0YSgpKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogS2VlcHMgYW4gTkRBcnJheSBpbiB0aGUgY3VycmVudCBzY29wZSBmcm9tIGJlaW5nIGRpc3Bvc2VkIGF1dG9tYXRpY2FsbHkuXG4gICAqIEBwYXJhbSByZXN1bHQgVGhlIE5EQXJyYXkgdG8ga2VlcCBmcm9tIGJlaW5nIGRpc3Bvc2VkLlxuICAgKi9cbiAga2VlcDxUIGV4dGVuZHMgTkRBcnJheT4ocmVzdWx0OiBUKTogVCB7XG4gICAgaWYgKHRoaXMuYWN0aXZlU2NvcGUgPT0gbnVsbCkge1xuICAgICAgaWYgKHRoaXMuc2FmZU1vZGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgJ1lvdSBhcmUgdXNpbmcgbWF0aCBpbiBzYWZlIG1vZGUuIEVuY2xvc2UgYWxsICcgK1xuICAgICAgICAgICAgJ21hdGgubWV0aG9kKCkgY2FsbHMgaW5zaWRlIGEgc2NvcGU6ICcgK1xuICAgICAgICAgICAgJ21hdGguc2NvcGUoKCkgPT4ge21hdGgubWV0aG9kKCk7Li4ufSkgdG8gYXZvaWQgbWVtb3J5ICcgK1xuICAgICAgICAgICAgJ2xlYWtzLicpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwLnB1c2gocmVzdWx0KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWNrcyBhbiBOREFycmF5IGluIHRoZSBjdXJyZW50IHNjb3BlIHRvIGJlIGF1dG9tYXRpY2FsbHkgY2xlYW5lZCB1cCB3aGVuXG4gICAqIHRoZSBjdXJyZW50IHNjb3BlIGVuZHMsIGFuZCByZXR1cm5zIHRoZSB2YWx1ZS5cbiAgICogQHBhcmFtIHJlc3VsdCBUaGUgTkRBcnJheSB0byB0cmFjayBpbiB0aGUgY3VycmVudCBzY29wZS5cbiAgICovXG4gIHRyYWNrPFQgZXh0ZW5kcyBOREFycmF5PihyZXN1bHQ6IFQpOiBUIHtcbiAgICBpZiAodGhpcy5hY3RpdmVTY29wZSA9PSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5zYWZlTW9kZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnWW91IGFyZSB1c2luZyBtYXRoIGluIHNhZmUgbW9kZS4gRW5jbG9zZSBhbGwgJyArXG4gICAgICAgICAgICAnbWF0aC5tZXRob2QoKSBjYWxscyBpbnNpZGUgYSBzY29wZTogJyArXG4gICAgICAgICAgICAnbWF0aC5zY29wZSgoKSA9PiB7bWF0aC5tZXRob2QoKTsuLi59KSB0byBhdm9pZCBtZW1vcnkgJyArXG4gICAgICAgICAgICAnbGVha3MuJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmFjdGl2ZVNjb3BlLnB1c2gocmVzdWx0KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBkb3QgcHJvZHVjdCBvZiB0d28gbWF0cmljZXMsIEEgKiBCLiBUaGVzZSBtdXN0IGJlIG1hdHJpY2VzLFxuICAgKiB1c2UgbWF0cml4VGltZXNWZWN0b3IgYW5kIHZlY3RvclRpbWVzTWF0cml4LCBkb3RQcm9kdWN0LCBhbmQgb3V0ZXJQcm9kdWN0XG4gICAqIGluIG90aGVyIGNhc2VzLlxuICAgKiBAcGFyYW0gYSBGaXJzdCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYiBTZWNvbmQgbWF0cml4IGluIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIGFPcmllbnRhdGlvbiBUaGUgTWF0cml4T3JpZW50YXRpb24gb2YgQS4gSWYgdXNpbmcgVFJBTlNQT1NFRCwgd2lsbFxuICAgKiBjb21wdXRlIEFeVCAqIEIuXG4gICAqIEBwYXJhbSBiT3JpZW50YXRpb24gVGhlIE1hdHJpeE9yaWVudGF0aW9uIG9mIEIuIElmIHVzaW5nIFRSQU5TUE9TRUQsIHdpbGxcbiAgICogY29tcHV0ZSBBICogQl5ULlxuICAgKi9cbiAgbWF0TXVsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBBcnJheTJEIHtcbiAgICBjb25zdCBpbm5lclNoYXBlQSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG4gICAgY29uc3QgaW5uZXJTaGFwZUIgPVxuICAgICAgICAoYk9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGIuc2hhcGVbMF0gOiBiLnNoYXBlWzFdO1xuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gMiAmJiBiLnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiBtYXRNdWw6IGlucHV0cyBtdXN0IGJlIHJhbmsgMiwgZ290IHJhbmtzICR7YS5yYW5rfWAgK1xuICAgICAgICAgICAgYGFuZCAke2IucmFua30uYCk7XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgaW5uZXJTaGFwZUEgPT09IGlubmVyU2hhcGVCLFxuICAgICAgICBgRXJyb3IgaW4gbWF0TXVsOiBpbm5lciBzaGFwZXMgKCR7aW5uZXJTaGFwZUF9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7aW5uZXJTaGFwZUJ9KSBvZiBOREFycmF5cyB3aXRoIHNoYXBlcyAke2Euc2hhcGV9IGFuZCBgICtcbiAgICAgICAgICAgIGAke2Iuc2hhcGV9IGFuZCBvcmllbnRhdGlvbnMgJHtNYXRyaXhPcmllbnRhdGlvblthT3JpZW50YXRpb25dfWAgK1xuICAgICAgICAgICAgYCBhbmQgJHtNYXRyaXhPcmllbnRhdGlvbltiT3JpZW50YXRpb25dfSBtdXN0IG1hdGNoLmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXRNdWxJbnRlcm5hbChhLCBiLCBhT3JpZW50YXRpb24sIGJPcmllbnRhdGlvbikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXRNdWxJbnRlcm5hbChcbiAgICAgIGE6IEFycmF5MkQsIGI6IEFycmF5MkQsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sXG4gICAgICBiT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogQXJyYXkyRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIGEgdmVjdG9yIGFuZCBhIG1hdHJpeCwgdiAqIEIuXG4gICAqIEBwYXJhbSB2IFRoZSB2ZWN0b3IgaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gbWF0cml4IFRoZSBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgdmVjdG9yVGltZXNNYXRyaXgodjogQXJyYXkxRCwgbWF0cml4OiBBcnJheTJEKTogQXJyYXkxRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBmaXJzdCBpbnB1dCBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7di5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgbWF0cml4LnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2Vjb25kIGlucHV0IG11c3QgYmUgcmFuayAyLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHttYXRyaXgucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYuc2l6ZSA9PT0gbWF0cml4LnNoYXBlWzBdLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IHNpemUgb2YgZmlyc3QgcmFuayAxIGlucHV0ICgke3Yuc2l6ZX0pIGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggaW5uZXIgZGltZW5zaW9uIG9mIHNlY29uZCByYW5rIDIgaW5wdXQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bCh2LmFzMkQoMSwgdi5zaXplKSwgbWF0cml4KS5hczFEKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIGEgbWF0cml4IGFuZCB2ZWN0b3IsIEEgKiB2LlxuICAgKiBAcGFyYW0gbWF0cml4IFRoZSBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdiBUaGUgdmVjdG9yIGluIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIG1hdHJpeFRpbWVzVmVjdG9yKG1hdHJpeDogQXJyYXkyRCwgdjogQXJyYXkxRCk6IEFycmF5MUQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB2LnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2Vjb25kIGlucHV0IG11c3QgcmFuayAxLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHt2LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtYXRyaXgucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBmaXJzdCBpbnB1dCBtdXN0IGJlIGEgcmFuayAyLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHttYXRyaXgucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYuc2l6ZSA9PT0gbWF0cml4LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IHNpemUgb2YgZmlyc3QgcmFuayAxIGlucHV0ICR7di5zaXplfSBgICtcbiAgICAgICAgICAgIGBtdXN0IG1hdGNoIGlubmVyIGRpbWVuc2lvbiBvZiBzZWNvbmQgcmFuayAyIGlucHV0LCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHNoYXBlICR7bWF0cml4LnNoYXBlfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bChtYXRyaXgsIHYuYXMyRCh2LnNpemUsIDEpKS5hczFEKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byB2ZWN0b3JzLCB2MSAqIHYyLlxuICAgKiBAcGFyYW0gdjEgVGhlIGZpcnN0IHZlY3RvciBpbiB0aGUgZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdjIgVGhlIHNlY29uZCB2ZWN0b3IgaW4gdGhlIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIGRvdFByb2R1Y3QodjE6IEFycmF5MUQsIHYyOiBBcnJheTFEKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEucmFuayA9PT0gMSAmJiB2Mi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gZG90UHJvZHVjdDogaW5wdXRzIG11c3QgYmUgcmFuayAxLCBidXQgZ290IHJhbmtzIGAgK1xuICAgICAgICAgICAgYCR7djEucmFua30gYW5kICR7djIucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYxLnNpemUgPT09IHYyLnNpemUsXG4gICAgICAgIGBFcnJvciBpbiBkb3RQcm9kdWN0OiBzaXplIG9mIGlucHV0cyAoJHt2MS5zaXplfSkgYW5kIChgICtcbiAgICAgICAgICAgIGAke3YyLnNpemV9KSBtdXN0IG1hdGNoLmApO1xuICAgIHJldHVybiB0aGlzLm1hdE11bCh2MS5hczJEKDEsIHYxLnNpemUpLCB2Mi5hczJEKHYyLnNpemUsIDEpKS5hc1NjYWxhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBvdXRlciBwcm9kdWN0IG9mIHR3byB2ZWN0b3JzLCB2MSBhbmQgdjIuXG4gICAqIEBwYXJhbSB2MSBUaGUgZmlyc3QgdmVjdG9yIGluIHRoZSBvdXRlciBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHYyIFRoZSBzZWNvbmQgdmVjdG9yIGluIHRoZSBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqL1xuICBvdXRlclByb2R1Y3QodjE6IEFycmF5MUQsIHYyOiBBcnJheTFEKTogQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYxLnJhbmsgPT09IDEgJiYgdjIucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIG91dGVyUHJvZHVjdDogaW5wdXRzIG11c3QgYmUgcmFuayAxLCBidXQgZ290IHJhbmtzIGAgK1xuICAgICAgICAgICAgYCR7djEucmFua30gYW5kICR7djIucmFua30uYCk7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRNdWwodjEuYXMyRCh2MS5zaXplLCAxKSwgdjIuYXMyRCgxLCB2Mi5zaXplKSk7XG4gIH1cblxuICAvLy8vLy8vLy8vLy8vLy9cbiAgLy8gU2hhcGUgb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vL1xuXG4gIC8qKlxuICAgKiBDbG9uZXMgYW4gTkRBcnJheSBvZiBhbnkgc2hhcGUuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBOREFycmF5IHRvIGNsb25lLlxuICAgKi9cbiAgY2xvbmU8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNsb25lSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogUmVzaGFwZXMgYW4gTkRBcnJheSB0byBhIG5ldyBzaGFwZS4gVGhlIHNpemUgb2YgdGhlIGlucHV0IE5EQXJyYXkgbXVzdFxuICAgKiBtYXRjaCB0aGUgc2l6ZSBvZiB0aGUgcmVxdWVzdGVkIHNoYXBlLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIG5ld1NoYXBlIFRoZSBuZXcgc2hhcGUgdG8gcmVzaGFwZSB0aGUgTkRBcnJheSB0by4gTXVzdCBiZSB0aGUgc2FtZVxuICAgKiBzaXplIGFzIHRoZSBOREFycmF5LlxuICAgKi9cbiAgcmVzaGFwZTxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBuZGFycmF5LnNpemUgPT09IHV0aWwuc2l6ZUZyb21TaGFwZShuZXdTaGFwZSksXG4gICAgICAgIGBFcnJvciBpbiByZXNoYXBlOiBvbGQgc2l6ZSAke25kYXJyYXkuc2l6ZX0gbXVzdCBtYXRjaCBuZXcgc2l6ZSBgICtcbiAgICAgICAgICAgIGAke3V0aWwuc2l6ZUZyb21TaGFwZShuZXdTaGFwZSl9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMucmVzaGFwZUludGVybmFsPFQxLCBUMj4obmRhcnJheSwgbmV3U2hhcGUpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVzaGFwZUludGVybmFsPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMjtcblxuICAvKipcbiAgICogRXh0cmFjdHMgYSBzbGljZSBmcm9tIGEgbWF0cml4LiBUaGUgb3BlcmF0aW9uIGV4dHJhY2VzIGEgc2xpY2UgZnJvbSBpbnB1dFxuICAgKiB0aGF0IHN0YXJ0cyBhdCBjb29yZGluYXRlcyBgYmVnaW5gIGFuZCBpcyBvZiBzaXplIGBzaXplYC5cbiAgICogQHBhcmFtIGlucHV0IFRoZSBpbnB1dCBtYXRyaXggdG8gc2xpY2UgZnJvbS5cbiAgICogQHBhcmFtIGJlZ2luIFRoZSAyRCBjb29yZGluYXRlcyBpbiB0aGUgaW5wdXQgbWF0cml4IHRvIHN0YXJ0IHRoZSBzbGljZVxuICAgKiBmcm9tLlxuICAgKiBAcGFyYW0gc2l6ZSBUaGUgc2ljZSBvZiB0aGUgMkQgd2luZG93IHRvIHNsaWNlLlxuICAgKi9cbiAgc2xpY2UyRChpbnB1dDogQXJyYXkyRCwgYmVnaW46IFtudW1iZXIsIG51bWJlcl0sIHNpemU6IFtudW1iZXIsIG51bWJlcl0pOlxuICAgICAgQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGJlZ2luWzBdICsgc2l6ZVswXSA8PSBpbnB1dC5zaGFwZVswXSAmJlxuICAgICAgICAgICAgYmVnaW5bMV0gKyBzaXplWzFdIDw9IGlucHV0LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gc2xpY2UyRDogcmVxdWVzdGVkIHN0YXJ0IHBvc2l0aW9uICR7YmVnaW59IGFuZCBzaXplIGAgK1xuICAgICAgICAgICAgYCR7c2l6ZX0gd291bGQgb3ZlcmZsb3cgaW5wdXQgb2Ygc2hhcGUgJHtpbnB1dC5zaGFwZX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zbGljZTJESW50ZXJuYWwoaW5wdXQsIGJlZ2luLCBzaXplKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpbjogW251bWJlciwgbnVtYmVyXSwgc2l6ZTogW251bWJlciwgbnVtYmVyXSk6IEFycmF5MkQ7XG5cbiAgLyoqXG4gICAqIENvcGllcyBhIHdpbmRvdyBmcm9tIHRoZSBgc291cmNlYCBtYXRyaXggc3RhcnRpbmcgYXQgYHNvdXJjZUJlZ2luYCBhbmQgaXNcbiAgICogb2Ygc2l6ZSBgc291cmNlU2l6ZWAgdG8gYSB3aW5kb3cgaW4gdGhlIGBkZXN0YCBtYXRyaXggc3RhcnRpbmcgYXRcbiAgICogYGRlc3RCZWdpbmAgYW5kIGlzIG9mIHNpemUgYGRlc3RTaXplYC9cbiAgICogQHBhcmFtIHNvdXJjZSBUaGUgc291cmNlIG1hdHJpeCB0byBjb3B5IGZyb20uXG4gICAqIEBwYXJhbSBzb3VyY2VCZWdpbiBUaGUgY29vcmRpbmF0ZXMgdG8gc3RhcnQgdGhlIGNvcHkgZnJvbS5cbiAgICogQHBhcmFtIHNvdXJjZVNpemUgVGhlIHNpemUgb2YgdGhlIGNvcHkgd2luZG93LlxuICAgKiBAcGFyYW0gZGVzdCBUaGUgZGVzdGluYXRpb24gbWF0cml4IHRvIGNvcHkgdG8uXG4gICAqIEBwYXJhbSBkZXN0QmVnaW4gVGhlIGNvb3JkaW5hdGVzIGluIGBkZXN0YCB0byBjb3B5IHRvLlxuICAgKiBAcGFyYW0gZGVzdFNpemUgVGhlIHNpemUgb2YgdGhlIGRlc3RpbmF0aW9uIHdpbmRvdy5cbiAgICovXG4gIGNvcHkyRChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELCBkZXN0QmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBzb3VyY2VCZWdpblswXSArIHNvdXJjZVNpemVbMF0gPD0gc291cmNlLnNoYXBlWzBdICYmXG4gICAgICAgICAgICBzb3VyY2VCZWdpblsxXSArIHNvdXJjZVNpemVbMV0gPD0gc291cmNlLnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gY29weTJEOiByZXF1ZXN0ZWQgc291cmNlIHN0YXJ0IHBvc2l0aW9uICR7c291cmNlQmVnaW59IGAgK1xuICAgICAgICAgICAgYGFuZCBzb3VyY2Ugc2l6ZSAke3NvdXJjZVNpemV9IHdvdWxkIG92ZXJmbG93IHNvdXJjZSBOREFycmF5YCArXG4gICAgICAgICAgICBgb2Ygc2hhcGUgJHtzb3VyY2Uuc2hhcGV9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkZXN0QmVnaW5bMF0gKyBkZXN0U2l6ZVswXSA8PSBkZXN0LnNoYXBlWzBdICYmXG4gICAgICAgICAgICBkZXN0QmVnaW5bMV0gKyBkZXN0U2l6ZVsxXSA8PSBkZXN0LnNoYXBlWzFdLFxuICAgICAgICBgRXJyb3IgaW4gY29weTJEOiByZXF1ZXN0ZWQgZGVzdCBzdGFydCBwb3NpdGlvbiAke2Rlc3RCZWdpbn0gYCArXG4gICAgICAgICAgICBgYW5kIHNvdXJjZSBzaXplICR7ZGVzdFNpemV9IHdvdWxkIG92ZXJmbG93IGRlc3QgTkRBcnJheSBvZmAgK1xuICAgICAgICAgICAgYHNoYXBlICR7ZGVzdC5zaGFwZX0uYCk7XG4gICAgY29weTJkX3V0aWwudmFsaWRhdGVTaGFwZXMoc291cmNlU2l6ZSwgZGVzdFNpemUpO1xuXG4gICAgcmV0dXJuIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIHNvdXJjZSwgc291cmNlQmVnaW4sIHNvdXJjZVNpemUsIGRlc3QsIGRlc3RCZWdpbiwgZGVzdFNpemUpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELCBkZXN0QmVnaW46IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBkZXN0U2l6ZTogW251bWJlciwgbnVtYmVyXSk6IHZvaWQ7XG5cbiAgLyoqXG4gICAqIENvbmNhdGVuYXRlcyB0d28gM0QgbmRhcnJheXMgYWxvbmcgYSBnaXZlbiBheGlzLlxuICAgKlxuICAgKiBGb3IgZXhhbXBsZSwgaWY6XG4gICAqIEE6IHNoYXBlKDIsIDEsIDMpID0gfCByMSwgZzEsIGIxIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIgfFxuICAgKlxuICAgKiBCOiBzaGFwZSgyLCAxLCAzKSA9IHwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogQyA9IGNvbmNhdDNEKEEsIEIsIGF4aXMpXG4gICAqXG4gICAqIGlmIGF4aXMgPSAwOlxuICAgKiBDOiBzaGFwZSg0LCAxLCAzKSA9IHwgcjEsIGcxLCBiMSB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIzLCBnMywgYjMgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjQsIGc0LCBiNCB8XG4gICAqXG4gICAqIGlmIGF4aXMgPSAxOlxuICAgKiBDOiBzaGFwZSgyLCAyLCAzKSA9IHwgcjEsIGcxLCBiMSwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyLCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogaWYgYXhpcyA9IDI6XG4gICAqIEMgPSBzaGFwZSgyLCAxLCA2KSA9IHwgcjEsIGcxLCBiMSwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgIHwgcjIsIGcyLCBiMiwgcjQsIGc0LCBiNCB8XG4gICAqXG4gICAqIEBwYXJhbSBuZGFycmF5MSBUaGUgZmlyc3QgYXJyYXkgdG8gY29uY2F0LlxuICAgKiBAcGFyYW0gbmRhcnJheTIgVGhlIHNlY29uZCBhcnJheSB0byBjb25hdC5cbiAgICogQHBhcmFtIGF4aXMgVGhlIGF4aXMgdG8gY29uY2F0ZSBhbG9uZy5cbiAgICovXG4gIGNvbmNhdDNEKG5kYXJyYXkxOiBBcnJheTNELCBuZGFycmF5MjogQXJyYXkzRCwgYXhpczogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uY2F0M2RfdXRpbC5hc3NlcnRDb25jYXQzRFNoYXBlc01hdGNoKFxuICAgICAgICBuZGFycmF5MS5zaGFwZSwgbmRhcnJheTIuc2hhcGUsIGF4aXMsICdFcnJvciBpbiBjb25jYXQzZDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5jb25jYXQzREludGVybmFsKG5kYXJyYXkxLCBuZGFycmF5MiwgYXhpcykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb25jYXQzREludGVybmFsKFxuICAgICAgbmRhcnJheTE6IEFycmF5M0QsIG5kYXJyYXkyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gUmVkdWN0aW9uIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0aGUgbG9nKHN1bShlIF4geCkpIGZvciBlYWNoIHggaW4gdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5IHRvIGNvbXB1dGUgdGhlIGxvZ1N1bUV4cCBvdmVyLlxuICAgKi9cbiAgbG9nU3VtRXhwKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubG9nU3VtRXhwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBsb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc3VtIG9mIGFsbCB0aGUgZW50cmllcyBpbiB0aGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkgdG8gY29tcHV0ZSB0aGUgc3VtIG92ZXIuXG4gICAqL1xuICBzdW0obmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zdW1JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBmbGF0dGVuZWQgaW5kZXggb2YgdGhlIG1pbmltdW0gZWxlbWVudCBpbiB0aGUgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBhcmdNaW4obmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcmdNaW5JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBmbGF0dGVuZWQgaW5kZXggb2YgdGhlIG1heGltdW0gZWxlbWVudCBpbiB0aGUgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBhcmdNYXgobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcmdNYXhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYSAxIGlmIHRoZSBhcmdNYXggb2YgeDEgYW5kIHgyIGFyZSB0aGUgc2FtZSwgb3RoZXJ3aXNlIDAuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01heEVxdWFscyh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goeDEuc2hhcGUsIHgyLnNoYXBlLCAnRXJyb3IgaW4gYXJnTWF4RXF1YWxzOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01heEVxdWFsc0ludGVybmFsKHgxLCB4MikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhcmdNYXhFcXVhbHNJbnRlcm5hbCh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0b3AgSyB2YWx1ZXMgYW5kIGZsYXR0ZW5lZCBpbmRpY2VzLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIGsgSG93IG1hbnkgdG9wIHZhbHVlcyB0byBjb21wdXRlLlxuICAgKi9cbiAgdG9wSyhuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOiB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGsgPD0gbmRhcnJheS5zaXplLFxuICAgICAgICBgRXJyb3IgaW4gdG9wSzogayB2YWx1ZSAoJHtrfSkgbXVzdCBiZSBsZXNzIHRoYW4gc2l6ZSBvZiBpbnB1dCBgICtcbiAgICAgICAgICAgIGBuZGFycmF5LCBnb3Qgc2hhcGUgJHtuZGFycmF5LnNoYXBlfS5gKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnRvcEtJbnRlcm5hbChuZGFycmF5LCBrKTtcbiAgICB0aGlzLnRyYWNrKHJlc3VsdC52YWx1ZXMpO1xuICAgIHRoaXMudHJhY2socmVzdWx0LmluZGljZXMpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH07XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBtaW5pbXVtIHZhbHVlIGZyb20gdGhlIGlucHV0LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIG1pbihuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1pbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG1heGltdW0gdmFsdWUgZnJvbSB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbWF4KG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4SW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc29mdG1heCBub3JtYWxpemVkIHZlY3RvciBmcm9tIHRoZSBpbnB1dCB2ZWN0b3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB2ZWN0b3IuXG4gICAqL1xuICBzb2Z0bWF4KHg6IEFycmF5MUQpOiBBcnJheTFEIHtcbiAgICByZXR1cm4gdGhpcy5zY29wZSgoKSA9PiB7XG4gICAgICAvLyBEbyBpdCBpbiBsb2cgc3BhY2UgZm9yIG51bWVyaWNhbCBzdGFiaWxpdHkuXG4gICAgICAvLyBleHAoWCAtIGxvZ1N1bUV4cChYKSlcbiAgICAgIGNvbnN0IGxzZSA9IHRoaXMubG9nU3VtRXhwKHgpO1xuICAgICAgY29uc3QgbG9nUmVzdWx0ID0gdGhpcy5hcnJheU1pbnVzU2NhbGFyKHgsIGxzZSk7XG4gICAgICByZXR1cm4gdGhpcy5leHAobG9nUmVzdWx0KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gRWxlbWVudC13aXNlIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIFN3aXRjaGVzIGRpbWVuc2lvbnMgb2YgdGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBhIFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmV3RGltIFRoZSBuZXcgaW5kaWNlcyB0aGF0IGRlZmluZSB3aGljaCBzaGFwZXMgdmFsdWVzIHRvIHN3aXRjaC5cbiAgICovXG4gIHN3aXRjaERpbTxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBhLnJhbmsgPT09IG5ld0RpbS5sZW5ndGgsXG4gICAgICAgIGBFcnJvciBpbiBzd2l0Y2hEaW06IGxlbmd0aCBvZiBpbnB1dCBzaGFwZSAke2Euc2hhcGV9IGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggc2l6ZSBvZiBuZXdEaW0gYXJyYXkgJHtuZXdEaW19LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc3dpdGNoRGltSW50ZXJuYWwoYSwgbmV3RGltKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN3aXRjaERpbUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGE6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBwbHVzIE5EQXJyYXksIGMgKyBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gYyArIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gYyArIEEuXG4gICAqL1xuICBzY2FsYXJQbHVzQXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsYXJQbHVzQXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyUGx1c0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJQbHVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjOiBTY2FsYXIsIGE6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBtaW51cyBOREFycmF5LCBjIC0gQS5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciBjIGluIGMgLSBBLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSBBIGluIGMgLSBBLlxuICAgKi9cbiAgc2NhbGFyTWludXNBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxhck1pbnVzQXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyTWludXNBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIG1pbnVzIE5EQXJyYXksIEEgLSBjLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSBBIGluIEEgLSBjLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gQSAtIGMuXG4gICAqL1xuICBhcnJheU1pbnVzU2NhbGFyPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlNaW51c1NjYWxhcjogc2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJyYXlNaW51c1NjYWxhckludGVybmFsKGEsIGMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGE6IFQsIGM6IFNjYWxhcik6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIC0xICogQSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBhIFRoZSBpbnB1dCBhcnJheS5cbiAgICovXG4gIG5lZzxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubmVnSW50ZXJuYWwoYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBuZWdJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIEFkZHMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSwgQSArIEIuIElucHV0cyBtdXN0IGJlIHRoZSBzYW1lIHNoYXBlLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSB0byBhZGQgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gYWRkIGVsZW1lbnQtd2lzZS5cbiAgICovXG4gIGFkZDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSwgJ0Vycm9yIGluIGFkZDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hZGRJbnRlcm5hbChhLCBiKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVDtcblxuICAvKipcbiAgICogU3VidHJhY3RzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UsIEEgLSBCLiBJbnB1dHMgbXVzdCBiZSB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gc3VidHJhY3QgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gc3VidHJhY3QgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgc3ViPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gc3ViOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN1YkludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBNdWx0aXBsaWVzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UgKGhhZGFtYXJkIHByb2R1Y3QpLCBBICogQi4gSW5wdXRzIG11c3RcbiAgICogYmUgdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIG11bHRpcGx5IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGIgVGhlIHNlY29uZCBOREFycmF5IHRvIG11bHRpcGx5IGVsZW1lbnQtd2lzZS5cbiAgICovXG4gIGVsZW1lbnRXaXNlTXVsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gZWxlbWVudFdpc2VNdWw6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZWxlbWVudFdpc2VNdWxJbnRlcm5hbChhLCBiKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBEaXZpZGVzIHR3byBOREFycmF5cyBlbGVtZW50LXdpc2UgKGhhZGFtYXJkIHByb2R1Y3QpLCBBIC8gQi4gSW5wdXRzIG11c3QgYmVcbiAgICogdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIGRpdmlkZSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBkaXZpZGUgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgZGl2aWRlPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gZGl2aWRlOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmRpdmlkZUludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZGl2aWRlSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciBkaXZpZGVkIGJ5IGFuIE5EQXJyYXksIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIGMgL1xuICAgKiBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIHZhbHVlIGluIGMgLyBBLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSB2YWx1ZSBpbiBjIC8gQS5cbiAgICovXG4gIHNjYWxhckRpdmlkZWRCeUFycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGFyRGl2aWRlZEJ5QXJyYXk6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IE5EQXJyYXkgb2YgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJEaXZpZGVkQnlBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjOiBTY2FsYXIsIGE6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhbiBOREFycmF5IGRpdmlkZWQgYnkgYSBzY2FsYXIsIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIEEgL1xuICAgKiBjLlxuICAgKiBAcGFyYW0gYSBUaGUgTkRBcnJheSB2YWx1ZSBpbiBBIC8gYy5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciB2YWx1ZSBpbiBBIC8gYy5cbiAgICovXG4gIGFycmF5RGl2aWRlZEJ5U2NhbGFyPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlEaXZpZGVkQnlTY2FsYXI6IHNlY29uZCBhcmd1bWVudCBtdXN0IGJlIHJhbmsgMCwgYCArXG4gICAgICAgICAgICBgYnV0IGdvdCBOREFycmF5IG9mIHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbChhLCBjKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFycmF5RGl2aWRlZEJ5U2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgYzogU2NhbGFyKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgZXhwb25lbnRpYWwgb2YgdGhlIGlucHV0IE5EQXJyYXkgZWxlbWVudC13aXNlLiB5ID0gZSBeIHhcbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBleHA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmV4cEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBuYXR1cmFsIGxvZ2FyaXRobSBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuIHkgPSBsbih4KVxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGxvZzxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubG9nSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBsb2dJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHJlY3RpZmllZCBsaW5lYXIgZWxlbWVudC13aXNlLCBtYXgoeCwgMCkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgcmVsdTxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMucmVsdUludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVsdUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc2lnbW9pZCBlbGVtZW50LXdpc2UsIHkgPSAxIC8gKDEgKyBleHAoLXgpKS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzaWdtb2lkPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zaWdtb2lkSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzaWdtb2lkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBoeXBlcmJvbGljIHRhbmdlbnQgb2YgdGhlIGlucHV0IE5EQXJyYXkgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHRhbmg8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnRhbmhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHRhbmhJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHNpbiBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UsIHkgPSBzaW4oeCkuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgc2luPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zaW5JbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc3RlcCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UsIHkgPSAxIGlmIHggPiAwIHwgMCBpZiB4IDw9XG4gICAqIDBcbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzdGVwPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zdGVwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzdGVwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxlZCBhcnJheSBhZGQgb3BlcmF0aW9uLCBjMSAqIEEgKyBjMiAqIEIuXG4gICAqIEBwYXJhbSBjMSBUaGUgZmlyc3Qgc2NhbGFyIGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSBpbiB0aGUgc2NhbGVkIGFycmF5IGFkZCBjb21wdXRhdGlvbi5cbiAgICogQHBhcmFtIGMyIFRoZSBzZWNvbmQgc2NhbGFyIGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gY2IgVGhlIHNlY29uZCBOREFycmF5IGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKi9cbiAgc2NhbGVkQXJyYXlBZGQ8VCBleHRlbmRzIE5EQXJyYXk+KGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYzEuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxlZEFycmF5QWRkOiBmaXJzdCBhcmd1bWVudCBtdXN0IHJhbmsgMCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGAgcmFuayAke2MxLnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjMi5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6IHRoaXJkIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYE5EQXJyYXkgb2YgcmFuayAke2MyLnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSwgJ0Vycm9yIGluIHNjYWxlZEFycmF5QWRkOiAnKTtcblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGVkQXJyYXlBZGRJbnRlcm5hbChjMSwgYSwgYzIsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjMTogU2NhbGFyLCBhOiBULCBjMjogU2NhbGFyLCBiOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBzY2FsYXIgdGltZXMgYXJyYXkgb3BlcmF0aW9uIGJyb2FkY2FzdGVkIG92ZXIgdGhlIE5EQXJyYXksIGMgKlxuICAgKiBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGluIHRoZSBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBBIHRoZSBOREFycmF5IGluIHRoZSBvcGVyYXRpb24gdGhhdCB3aWxsIGJlIGJyb2FkY2FzdGVkIG92ZXIuXG4gICAqL1xuICBzY2FsYXJUaW1lc0FycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYXJyYXlEaXZpZGVkQnlTY2FsYXI6IGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBidXQgYCArXG4gICAgICAgICAgICBgZ290IHJhbmsgJHtjLnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc2NhbGFyVGltZXNBcnJheUludGVybmFsKGMsIGEpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGFuIGVsZW1lbnQtd2lzZSBicm9hZGNhc3RlZCBtdWx0aXBsaWNhdGlvbiBvZiB0d28gbWF0cmljZXMgQSBhbmRcbiAgICogQi4gV2lsbCByZXR1cm4gYSBuZXcgbWF0cml4IHRoYXQgaXMgdGhlIG1heCBvZiBBIGFuZCBCLCB3aGVyZSB0aGUgc21hbGxlclxuICAgKiBtYXRyaXggd2lsbCBicm9hZGNhc3Qgb3ZlciB0aGUgbGFyZ2VyIG1hdHJpeC5cbiAgICogQHBhcmFtIGMgVGhlIHNjYWxhciBpbiB0aGUgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gQSB0aGUgTkRBcnJheSBpbiB0aGUgb3BlcmF0aW9uIHRoYXQgd2lsbCBiZSBicm9hZGNhc3RlZCBvdmVyLlxuICAgKi9cbiAgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3QoYTogQXJyYXkyRCwgYjogQXJyYXkyRCk6IEFycmF5MkQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBhLnJhbmsgPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiBlbGVtZW50V2lzZU11bEJyb2FkY2FzdDogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSBgICtcbiAgICAgICAgICAgIGByYW5rIDIsIGJ1dCBnb3QgcmFuayAke2EucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGIucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIGVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0OiBzZWNvbmQgYXJndW1lbnQgbXVzdCBiZSBgICtcbiAgICAgICAgICAgIGByYW5rIDIsIGJ1dCBnb3QgcmFuayAke2IucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5lbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTpcbiAgICAgIEFycmF5MkQ7XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vIENvbnZvbHV0aW9uIG9wcyAvL1xuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSAyRCBjb252b2x1dGlvbiBvdmVyIHRoZSBpbnB1dCB4LlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbcm93cywgY29scywgZGVwdGgxXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBiaWFzZXMgT3B0aW9uYWwgYmlhc2VzIE5EQXJyYXksIG11c3QgYmUgcmFuayAxIG9mIHNoYXBlIFtkZXB0aDJdLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0gemVyb1BhZCBUaGUgemVybyBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWRcbiAgICogZXF1YWxseSBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBjb252MmQoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzLnJhbmsgPT09IDQsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3dlaWdodHMucmFua30uYCk7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBiaWFzZXMucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gY29udjJkOiBiaWFzZXMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgICAgYCR7Ymlhc2VzLnJhbmt9LmApO1xuICAgIH1cblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzJdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkOiBkZXB0aCBvZiBpbnB1dCAoJHt4LnNoYXBlWzJdfSkgbXVzdCBtYXRjaCAgYCArXG4gICAgICAgICAgICBgaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgJHt3ZWlnaHRzLnNoYXBlWzJdfS5gKTtcblxuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5jb252MmRJbnRlcm5hbCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBiYWNrcHJvcCBvZiBhIDJEIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbeHJvd3MsIHhjb2xzLCBkZXB0aDFdLlxuICAgKiBAcGFyYW0gZHkgVGhlIGR5IGltYWdlLCBtdXN0IGJlIHJhbmsgMywgb2Ygc2hhcGUgW3lyb3dzLCB5Y29scywgZGVwdGgyXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgb3JpZ2luYWwgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgdGhlIG9yaWdpbmFsIGNvbnZvbHV0aW9uLlxuICAgKi9cbiAgY29udjJkQmFja1Byb3AoXG4gICAgICB4OiBBcnJheTNELCBkeTogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IHtkeDogQXJyYXkzRCwgZHc6IEFycmF5NEQsIGRiOiBBcnJheTFEfSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7eC5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRCYWNrUHJvcDogZHkgbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHtkeS5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiB3ZWlnaHRzIG11c3QgYmUgcmFuayA0LCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7d2VpZ2h0cy5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbMl0sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRCYWNrUHJvcDogZGVwdGggb2YgeCAke3guc2hhcGVbMl19KSBtdXN0IGAgK1xuICAgICAgICAgICAgYG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICgke3dlaWdodHMuc2hhcGVbMl19LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkeS5zaGFwZVsyXSA9PT0gd2VpZ2h0cy5zaGFwZVszXSxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkZXB0aCBvZiBkeSAoJHtkeS5zaGFwZVsyXX0pIG11c3QgYCArXG4gICAgICAgICAgICBgbWF0Y2ggb3V0cHV0IGRlcHRoIGZvciB3ZWlnaHRzICgke3dlaWdodHMuc2hhcGVbM119KS5gKTtcblxuICAgIGNvbnN0IGJhY2twcm9wUmVzdWx0ID1cbiAgICAgICAgdGhpcy5jb252MmRCYWNrUHJvcEludGVybmFsKHgsIGR5LCB3ZWlnaHRzLCBzdHJpZGUsIHBhZCk7XG5cbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmRiKTtcbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmR3KTtcbiAgICB0aGlzLnRyYWNrKGJhY2twcm9wUmVzdWx0LmR4KTtcblxuICAgIHJldHVybiBiYWNrcHJvcFJlc3VsdDtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9O1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgdHJhbnNwb3NlZCAyRCBjb252b2x1dGlvbiBvZiBhbiBpbWFnZSwgYWxzbyBrbm93biBhcyBhXG4gICAqIGRlY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFt4cm93cywgeGNvbHMsIGRlcHRoMV0uXG4gICAqIEBwYXJhbSB3ZWlnaHRzIFRoZSB3ZWlnaHRzIE5EQXJyYXksIG11c3QgYmUgcmFuayA0LCBvZiBzaGFwZSBbZiwgZiwgZGVwdGgxLFxuICAgKiBkZXB0aDJdLlxuICAgKiBAcGFyYW0gYmlhc2VzIE9wdGlvbmFsIGJpYXNlcyBOREFycmF5LCBtdXN0IGJlIHJhbmsgMSBvZiBzaGFwZSBbZGVwdGgyXS5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgY29udjJkVHJhbnNwb3NlKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkVHJhbnNwb3NlOiB4IG11c3QgYmUgcmFuayAzLCBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzLnJhbmsgPT09IDQsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke3dlaWdodHMucmFua31gKTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGJpYXNlcy5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IGJpYXNlcyBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCAnICtcbiAgICAgICAgICAgICAgJ3JhbmsgJHtiaWFzZXMucmFua30uYCk7XG4gICAgfVxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzNdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkVHJhbnNwb3NlOiBkZXB0aCBvZiBpbnB1dCAoJHt4LnNoYXBlWzJdfSkgbXVzdCBgICtcbiAgICAgICAgICAgIGBtYXRjaCBpbnB1dCBkZXB0aCBmb3Igd2VpZ2h0cyAke3dlaWdodHMuc2hhcGVbM119LmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2soXG4gICAgICAgIHRoaXMuY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoeCwgd2VpZ2h0cywgYmlhc2VzLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIG1heCBwb29saW5nIG9mIGFuIGltYWdlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLlxuICAgKiBAcGFyYW0gZlNpemUgVGhlIGZpZWxkIHNpemUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBtYXhQb29sKHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgICdFcnJvciBpbiBtYXhQb29sOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayAnICsgeC5yYW5rICsgJy4nKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1heFBvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGJhY2twcm9wIG9mIGEgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBkeSBUaGUgZHkgZXJyb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1heFBvb2xCYWNrcHJvcChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtYXhQb29sQmFja3Byb3A6IGR5IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke2R5LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtYXhQb29sQmFja3Byb3A6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7eC5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoZHksIHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhQb29sQmFja3Byb3BJbnRlcm5hbChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIG1pbiBwb29saW5nIG9mIGFuIGltYWdlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgaW1hZ2UsIG11c3QgYmUgcmFuayAzLlxuICAgKiBAcGFyYW0gZlNpemUgVGhlIGZpZWxkIHNpemUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIG1heCBwb29sLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBtaW5Qb29sKHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBtaW5Qb29sOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayAke3gucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5taW5Qb29sSW50ZXJuYWwoeCwgZlNpemUsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1pblBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSAyRCBhdmVyYWdlIHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIGF2Z1Bvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGF2Z1Bvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmF2Z1Bvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXZnUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKlxuICAgKiBCaWxpbmVhciByZXNpemUgYSAzRCBhcnJheSBwZXIgZWFjaCBjaGFubmVsIHRvIGEgbmV3IDJEIHNoYXBlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgQXJyYXkzRC5cbiAgICogQHBhcmFtIG5ld1NoYXBlMkQgVGhlIG5ldyBzaGFwZSB0byByZXNpemUgdGhlIEFycmF5M0QgdG8uIEVhY2ggY2hhbm5lbCBpc1xuICAgKiByZXNpemVkIGluZGl2aWR1YWxseS5cbiAgICogQHBhcmFtIGFsaWduQ29ybmVycyBBbiBvcHRpb25hbCBib29sLiBEZWZhdWx0cyB0byBGYWxzZS4gSWYgdHJ1ZSwgcmVzY2FsZVxuICAgKiBpbnB1dCBieSAobmV3X2hlaWdodCAtIDEpIC8gKGhlaWdodCAtIDEpLCB3aGljaCBleGFjdGx5IGFsaWducyB0aGUgNFxuICAgKiBjb3JuZXJzIG9mIGltYWdlcyBhbmQgcmVzaXplZCBpbWFnZXMuIElmIGZhbHNlLCByZXNjYWxlIGJ5IG5ld19oZWlnaHQgL1xuICAgKiBoZWlnaHQuIFRyZWF0IHNpbWlsYXJseSB0aGUgd2lkdGggZGltZW5zaW9uLlxuICAgKi9cbiAgcmVzaXplQmlsaW5lYXIzRChcbiAgICAgIHg6IEFycmF5M0QsIG5ld1NoYXBlMkQ6IFtudW1iZXIsIG51bWJlcl0sIGFsaWduQ29ybmVycyA9IGZhbHNlKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIHJlc2l6ZUJpbGluZWFyM0Q6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgbmV3U2hhcGUyRC5sZW5ndGggPT09IDIsXG4gICAgICAgIGBFcnJvciBpbiByZXNpemVCaWxpbmVhcjNEOiBuZXcgc2hhcGUgbXVzdCAyRCwgYnV0IGdvdCBzaGFwZSBgICtcbiAgICAgICAgICAgIGAke25ld1NoYXBlMkR9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKFxuICAgICAgICB0aGlzLnJlc2l6ZUJpbGluZWFyM0RJbnRlcm5hbCh4LCBuZXdTaGFwZTJELCBhbGlnbkNvcm5lcnMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQmF0Y2ggbm9ybWFsaXphdGlvbiAzRC4gTWVhbiwgdmFyaWFuY2UsIHNjYWxlLCBhbmQgb2Zmc2V0IGNhbiBiZSBvZiB0d29cbiAgICogc2hhcGVzOiAxKSBUaGUgc2FtZSBzaGFwZSBhcyB0aGUgaW5wdXQ6IGFuIEFycmF5M0QuIDIpIEluIHRoZSBjb21tb24gY2FzZSxcbiAgICogdGhlIGRlcHRoIGRpbWVuc2lvbiBpcyB0aGUgbGFzdCBkaW1lbnNpb24gb2YgeCwgc28gdGhlIHZhbHVlcyB3b3VsZCBiZSBhblxuICAgKiBBcnJheTFEIG9mIHNoYXBlIFtkZXB0aF0uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbWVhbiBBIG1lYW4gTkRBcnJheS5cbiAgICogQHBhcmFtIHZhcmlhbmNlIEEgdmFyaWFuY2UgTkRBcnJheS5cbiAgICogQHBhcmFtIHZhcmlhbmNlRXBzaWxvbiBBIHNtYWxsIGZsb2F0IG51bWJlciB0byBhdm9pZCBkaXZpZGluZyBieSAwLlxuICAgKiBAcGFyYW0gc2NhbGUgQSBzY2FsZSBOREFycmF5LlxuICAgKiBAcGFyYW0gb2Zmc2V0IEFuIG9mZnNldCBOREFycmF5LlxuICAgKi9cbiAgYmF0Y2hOb3JtYWxpemF0aW9uM0QoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb24gPSAuMDAxLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtZWFuLnJhbmsgPT09IDMgfHwgbWVhbi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IG1lYW4gbXVzdCBiZSByYW5rIDMgb3IgcmFuayAxIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke21lYW4ucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHZhcmlhbmNlLnJhbmsgPT09IDMgfHwgdmFyaWFuY2UucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIGJhdGNoTm9ybWFsaXphdGlvbjNEOiB2YXJpYW5jZSBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYCArXG4gICAgICAgICAgICBgYnV0IGdvdCByYW5rICR7dmFyaWFuY2UucmFua30uYCk7XG4gICAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHNjYWxlLnJhbmsgPT09IDMgfHwgc2NhbGUucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IHNjYWxlIG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgICAgYGJ1dCBnb3QgcmFuayAke3NjYWxlIS5yYW5rfS5gKTtcbiAgICB9XG4gICAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBvZmZzZXQucmFuayA9PT0gMyB8fCBvZmZzZXQucmFuayA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IG9mZnNldCBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYCArXG4gICAgICAgICAgICAgIGBidXQgZ290IHJhbmsgJHtvZmZzZXQhLnJhbmt9LmApO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYmF0Y2hOb3JtYWxpemF0aW9uM0RJbnRlcm5hbChcbiAgICAgICAgeCwgbWVhbiwgdmFyaWFuY2UsIHZhcmlhbmNlRXBzaWxvbiwgc2NhbGUsIG9mZnNldCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBiYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbWVhbjogQXJyYXkzRHxBcnJheTFELCB2YXJpYW5jZTogQXJyYXkzRHxBcnJheTFELFxuICAgICAgdmFyaWFuY2VFcHNpbG9uOiBudW1iZXIsIHNjYWxlPzogQXJyYXkzRHxBcnJheTFELFxuICAgICAgb2Zmc2V0PzogQXJyYXkzRHxBcnJheTFEKTogQXJyYXkzRDtcbn1cblxuZXhwb3J0IGVudW0gTWF0cml4T3JpZW50YXRpb24ge1xuICBSRUdVTEFSLFxuICBUUkFOU1BPU0VEXG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9tYXRoL2NvbnZfdXRpbCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQgKiBhcyBjb3B5MkRfdXRpbCBmcm9tICcuL2NvcHkyZF91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5cbmV4cG9ydCBjbGFzcyBOREFycmF5TWF0aENQVSBleHRlbmRzIE5EQXJyYXlNYXRoIHtcbiAgY29uc3RydWN0b3Ioc2FmZU1vZGUgPSBmYWxzZSkge1xuICAgIHN1cGVyKHNhZmVNb2RlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3ZhbHVlczogbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LmdldFZhbHVlcygpKX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2hhcGVJbnRlcm5hbDxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIHJldHVybiB0aGlzLmNsb25lSW50ZXJuYWwobmRhcnJheSkucmVzaGFwZTxUMj4obmV3U2hhcGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICBjb25zdCByZXN1bHQgPSBBcnJheTJELnplcm9zKHNpemVSb3dDb2wpO1xuICAgIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIGlucHV0LCBiZWdpblJvd0NvbCwgc2l6ZVJvd0NvbCwgcmVzdWx0LCBbMCwgMF0sIHNpemVSb3dDb2wpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29weTJESW50ZXJuYWwoXG4gICAgICBzb3VyY2U6IEFycmF5MkQsIHNvdXJjZUJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogQXJyYXkyRCxcbiAgICAgIGRlc3RCZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZCB7XG4gICAgY29weTJEX3V0aWwudmFsaWRhdGVTaGFwZXMoc291cmNlU2l6ZVJvd0NvbCwgZGVzdFNpemVSb3dDb2wpO1xuICAgIGNvbnN0IHNyY1ZhbHVlcyA9IHNvdXJjZS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBkc3RWYWx1ZXMgPSBkZXN0LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG4gPSBzb3VyY2VTaXplUm93Q29sWzBdICogc291cmNlU2l6ZVJvd0NvbFsxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG47ICsraSkge1xuICAgICAgY29uc3Qgc3JjUm93ID0gc291cmNlQmVnaW5Sb3dDb2xbMF0gKyBNYXRoLmZsb29yKGkgLyBzb3VyY2VTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IHNyY0NvbCA9IHNvdXJjZUJlZ2luUm93Q29sWzFdICsgKGkgJSBzb3VyY2VTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IHNyY09mZiA9IHNyY1JvdyAqIHNvdXJjZS5zaGFwZVsxXSArIHNyY0NvbDtcbiAgICAgIGNvbnN0IGRzdFJvdyA9IGRlc3RCZWdpblJvd0NvbFswXSArIE1hdGguZmxvb3IoaSAvIGRlc3RTaXplUm93Q29sWzFdKTtcbiAgICAgIGNvbnN0IGRzdENvbCA9IGRlc3RCZWdpblJvd0NvbFsxXSArIChpICUgZGVzdFNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3QgZHN0T2ZmID0gZHN0Um93ICogZGVzdC5zaGFwZVsxXSArIGRzdENvbDtcbiAgICAgIGRzdFZhbHVlc1tkc3RPZmZdID0gc3JjVmFsdWVzW3NyY09mZl07XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNvbmNhdDNESW50ZXJuYWwoeDE6IEFycmF5M0QsIHgyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9XG4gICAgICAgIGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlLCBheGlzKTtcblxuICAgIGNvbnN0IHZhbHVlcyA9IE5EQXJyYXkuemVyb3M8QXJyYXkzRD4ob3V0cHV0U2hhcGUpO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvdXRwdXRTaGFwZVswXTsgaSsrKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IG91dHB1dFNoYXBlWzFdOyBqKyspIHtcbiAgICAgICAgZm9yIChsZXQgayA9IDA7IGsgPCBvdXRwdXRTaGFwZVsyXTsgaysrKSB7XG4gICAgICAgICAgLy8gU2hhZGVyIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCBpbmRleDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID0gW2ksIGosIGtdO1xuICAgICAgICAgIGxldCB2YWx1ZTogbnVtYmVyO1xuICAgICAgICAgIGlmIChpbmRleFtheGlzXSA8IHgxLnNoYXBlW2F4aXNdKSB7XG4gICAgICAgICAgICB2YWx1ZSA9IHgxLmdldChpLCBqLCBrKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaW5kZXhbYXhpc10gLT0geDEuc2hhcGVbYXhpc107XG4gICAgICAgICAgICBjb25zdCBbaTIsIGoyLCBrMl0gPSBpbmRleDtcbiAgICAgICAgICAgIHZhbHVlID0geDIuZ2V0KGkyLCBqMiwgazIpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHZhbHVlcy5zZXQodmFsdWUsIGksIGosIGspO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJQbHVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWwgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcmVzdWx0VmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBjVmFsICsgYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpIHtcbiAgICBjb25zdCBjVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGMxVmFsID0gYzEuZ2V0KCk7XG4gICAgY29uc3QgYzJWYWwgPSBjMi5nZXQoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNWYWx1ZXNbaV0gPSBjMVZhbCAqIGFWYWx1ZXNbaV0gKyBjMlZhbCAqIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogY1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWwgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gY1ZhbCAqIGFWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCBuZWdBID0gdGhpcy5uZWdJbnRlcm5hbChhKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnNjYWxhclBsdXNBcnJheUludGVybmFsKGMsIG5lZ0EpO1xuXG4gICAgbmVnQS5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGFycmF5TWludXNTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTogVCB7XG4gICAgY29uc3QgbmVnQyA9IHRoaXMubmVnSW50ZXJuYWwoYyk7XG4gICAgY29uc3QgcmVzdWx0ID0gdGhpcy5zY2FsYXJQbHVzQXJyYXlJbnRlcm5hbChuZWdDLCBhKTtcblxuICAgIG5lZ0MuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBuZWdJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbChTY2FsYXIuTkVHX09ORSwgYSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsPFQ+KFNjYWxhci5PTkUsIGEsIFNjYWxhci5PTkUsIGIpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1YkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUPihTY2FsYXIuT05FLCBhLCBTY2FsYXIuTkVHX09ORSwgYik7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF0TXVsSW50ZXJuYWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEFycmF5MkQge1xuICAgIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG5cbiAgICBjb25zdCBsZWZ0RGltID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzBdIDogYS5zaGFwZVsxXTtcbiAgICBjb25zdCByaWdodERpbSA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVsxXSA6IGIuc2hhcGVbMF07XG5cbiAgICBjb25zdCBub3JtYWxHZXR0ZXIgPSAobWF0cml4OiBBcnJheTJELCBpOiBudW1iZXIsIGo6IG51bWJlcikgPT5cbiAgICAgICAgbWF0cml4LmdldChpLCBqKTtcbiAgICBjb25zdCB0cmFuc3Bvc2VkR2V0dGVyID0gKG1hdHJpeDogQXJyYXkyRCwgaTogbnVtYmVyLCBqOiBudW1iZXIpID0+XG4gICAgICAgIG1hdHJpeC5nZXQoaiwgaSk7XG5cbiAgICBjb25zdCBhR2V0dGVyID0gKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgP1xuICAgICAgICBub3JtYWxHZXR0ZXIgOlxuICAgICAgICB0cmFuc3Bvc2VkR2V0dGVyO1xuICAgIGNvbnN0IGJHZXR0ZXIgPSAoYk9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/XG4gICAgICAgIG5vcm1hbEdldHRlciA6XG4gICAgICAgIHRyYW5zcG9zZWRHZXR0ZXI7XG4gICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShsZWZ0RGltICogcmlnaHREaW0pO1xuICAgIGxldCBpbmRleCA9IDA7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxlZnREaW07ICsraSkge1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCByaWdodERpbTsgKytqKSB7XG4gICAgICAgIGxldCBzdW0gPSAwO1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHNoYXJlZERpbTsgKytrKSB7XG4gICAgICAgICAgLy8gVE9ETzogb3B0aW1pemUgQ1BVIG1hdG11bC5cbiAgICAgICAgICBzdW0gKz0gYUdldHRlcihhLCBpLCBrKSAqIGJHZXR0ZXIoYiwgaywgaik7XG4gICAgICAgIH1cbiAgICAgICAgdmFsdWVzW2luZGV4KytdID0gc3VtO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gQXJyYXkyRC5uZXcoW2xlZnREaW0sIHJpZ2h0RGltXSwgdmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAqIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgY29uc3QgbWF4Um93ID0gTWF0aC5tYXgoYS5zaGFwZVswXSwgYi5zaGFwZVswXSk7XG4gICAgY29uc3QgbWF4Q29sID0gTWF0aC5tYXgoYS5zaGFwZVsxXSwgYi5zaGFwZVsxXSk7XG5cbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG1heFJvdyAqIG1heENvbCk7XG4gICAgbGV0IGluZGV4ID0gMDtcbiAgICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCBtYXhSb3c7IHJvdysrKSB7XG4gICAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCBtYXhDb2w7IGNvbCsrKSB7XG4gICAgICAgIHZhbHVlc1tpbmRleCsrXSA9IGEuZ2V0KHJvdyAlIGEuc2hhcGVbMF0sIGNvbCAlIGEuc2hhcGVbMV0pICpcbiAgICAgICAgICAgIGIuZ2V0KHJvdyAlIGIuc2hhcGVbMF0sIGNvbCAlIGIuc2hhcGVbMV0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gQXJyYXkyRC5uZXcoW21heFJvdywgbWF4Q29sXSwgdmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBkaXZpZGVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBiVmFsdWVzID0gYi5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IGFWYWx1ZXNbaV0gLyBiVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6XG4gICAgICBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbHVlID0gYy5nZXQoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGFWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IGNWYWx1ZSAvIGFWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBjVmFsdWUgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAvIGNWYWx1ZTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzdW1JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBsZXQgc3VtID0gMDtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBzdW0gKz0gdmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhzdW0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBtaW4gPSBOdW1iZXIuTUFYX1ZBTFVFO1xuICAgIGxldCBtaW5JbmRleCA9IC0xO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIDwgbWluKSB7XG4gICAgICAgIG1pbiA9IHZhbHVlO1xuICAgICAgICBtaW5JbmRleCA9IGk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1pbkluZGV4KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBsZXQgbWF4ID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIGxldCBtYXhJbmRleCA9IC0xO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlID4gbWF4KSB7XG4gICAgICAgIG1heCA9IHZhbHVlO1xuICAgICAgICBtYXhJbmRleCA9IGk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1heEluZGV4KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhFcXVhbHNJbnRlcm5hbCh4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IGFyZ01heDEgPSB0aGlzLmFyZ01heEludGVybmFsKHgxKS5nZXQoKTtcbiAgICBjb25zdCBhcmdNYXgyID0gdGhpcy5hcmdNYXhJbnRlcm5hbCh4MikuZ2V0KCk7XG4gICAgaWYgKGlzTmFOKGFyZ01heDEpIHx8IGlzTmFOKGFyZ01heDIpKSB7XG4gICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldygrKGFyZ01heDEgPT09IGFyZ01heDIpKTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0b3BLSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSwgazogbnVtYmVyKTpcbiAgICAgIHt2YWx1ZXM6IEFycmF5MUQsIGluZGljZXM6IEFycmF5MUR9IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHZhbHVlc0FuZEluZGljZXM6IEFycmF5PHt2YWx1ZTogbnVtYmVyLCBpbmRleDogbnVtYmVyfT4gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFsdWVzQW5kSW5kaWNlcy5wdXNoKHt2YWx1ZTogdmFsdWVzW2ldLCBpbmRleDogaX0pO1xuICAgIH1cbiAgICB2YWx1ZXNBbmRJbmRpY2VzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIHJldHVybiBiLnZhbHVlIC0gYS52YWx1ZTtcbiAgICB9KTtcbiAgICBjb25zdCB0b3BrVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShrKTtcbiAgICBjb25zdCB0b3BrSW5kaWNlcyA9IG5ldyBGbG9hdDMyQXJyYXkoayk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBrOyBpKyspIHtcbiAgICAgIHRvcGtWYWx1ZXNbaV0gPSB2YWx1ZXNBbmRJbmRpY2VzW2ldLnZhbHVlO1xuICAgICAgdG9wa0luZGljZXNbaV0gPSB2YWx1ZXNBbmRJbmRpY2VzW2ldLmluZGV4O1xuICAgIH1cbiAgICByZXR1cm4ge3ZhbHVlczogQXJyYXkxRC5uZXcodG9wa1ZhbHVlcyksIGluZGljZXM6IEFycmF5MUQubmV3KHRvcGtJbmRpY2VzKX07XG4gIH1cblxuICBwcm90ZWN0ZWQgbWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBsZXQgbWluID0gdmFsdWVzWzBdO1xuICAgIGZvciAobGV0IGkgPSAxOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIFNjYWxhci5uZXcoTmFOKTtcbiAgICAgIH1cbiAgICAgIGlmICh2YWx1ZSA8IG1pbikge1xuICAgICAgICBtaW4gPSB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWluKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGxldCBtYXggPSB2YWx1ZXNbMF07XG4gICAgZm9yIChsZXQgaSA9IDE7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlID4gbWF4KSB7XG4gICAgICAgIG1heCA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhtYXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGV4cEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBNYXRoLmV4cCh2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IE1hdGgubG9nKHZhbHVlKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBsb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB4TWF4ID0gdGhpcy5tYXgobmRhcnJheSk7XG4gICAgY29uc3QgYSA9IHRoaXMuYXJyYXlNaW51c1NjYWxhcihuZGFycmF5LCB4TWF4KTtcbiAgICBjb25zdCBiID0gdGhpcy5leHAoYSk7XG4gICAgY29uc3QgYyA9IHRoaXMuc3VtKGIpO1xuICAgIGNvbnN0IGQgPSB0aGlzLmxvZyhjKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLmFkZCh4TWF4LCBkKTtcblxuICAgIHhNYXguZGlzcG9zZSgpO1xuICAgIGEuZGlzcG9zZSgpO1xuICAgIGIuZGlzcG9zZSgpO1xuICAgIGMuZGlzcG9zZSgpO1xuICAgIGQuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gTWF0aC5tYXgoMCwgdmFsdWVzW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaWdtb2lkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gMSAvICgxICsgTWF0aC5leHAoLXZhbHVlc1tpXSkpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRhbmhJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSB1dGlsLnRhbmgodmFsdWVzW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaW5JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBNYXRoLnNpbih2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN0ZXBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBjb25zdCB2YWx1ZSA9IHZhbHVlc1tpXTtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IHZhbHVlID4gMCA/IDEgOiAodmFsdWUgPCAwID8gMCA6IHZhbHVlKTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBpbnB1dERlcHRoXSA9IHguc2hhcGU7XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCB4UkNvcm5lcik7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIGZpZWxkU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IHkuc2hhcGVbMV07ICsreUMpIHtcbiAgICAgICAgICBjb25zdCB4Q0Nvcm5lciA9IHlDICogc3RyaWRlIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDTWluID0gTWF0aC5tYXgoMCwgeENDb3JuZXIpO1xuICAgICAgICAgIGNvbnN0IHhDTWF4ID0gTWF0aC5taW4oeENvbHMsIGZpZWxkU2l6ZSArIHhDQ29ybmVyKTtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBpbnB1dERlcHRoOyArK2QxKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGl4ZWwgPSB4LmdldCh4UiwgeEMsIGQxKTtcbiAgICAgICAgICAgICAgICBjb25zdCB3ZWlnaHQgPSB3ZWlnaHRzLmdldCh3Uiwgd0MsIGQxLCBkMik7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIHdlaWdodDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBiaWFzID0gKGJpYXNlcyAhPSBudWxsKSA/IGJpYXNlcy5nZXQoZDIpIDogMDtcbiAgICAgICAgICB5LnNldChkb3RQcm9kICsgYmlhcywgeVIsIHlDLCBkMik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgZHcgPSB0aGlzLmNvbnYyZERlcldlaWdodHMoeCwgZHksIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgZGIgPSB0aGlzLmNvbnYyZERlckJpYXMoZHkpO1xuICAgIGNvbnN0IGR4ID0gdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbChkeSwgd2VpZ2h0cywgbnVsbCwgc3RyaWRlLCBwYWQpO1xuICAgIHJldHVybiB7ZHgsIGRiLCBkd307XG4gIH1cblxuICAvKipcbiAgICogaW1hZ2UgaXMgb2Ygc2hhcGUgW3IsIGMsIGQxXS5cbiAgICogd2VpZ2h0cyBpcyBvZiBzaGFwZSBbRiwgRiwgZDEsIGQyXS5cbiAgICovXG4gIHByb3RlY3RlZCBjb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgICBvcmlnUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCB4RGVwdGhdID0geC5zaGFwZTtcblxuICAgIC8vIERpbGF0ZSB0aGUgaW5wdXQuXG4gICAgY29uc3QgeFJvd3NEaWxhdGVkID0gKHhSb3dzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgICBjb25zdCB4Q29sc0RpbGF0ZWQgPSAoeENvbHMgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuXG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93c0RpbGF0ZWQsIHhDb2xzRGlsYXRlZCwgb3JpZ091dHB1dERlcHRoXSwgZlNpemUsIG9yaWdJbnB1dERlcHRoLCAxLFxuICAgICAgICBwYWQpO1xuICAgIGNvbnN0IHkgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3JpZ0lucHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKHhSQ29ybmVyIC8gb3JpZ1N0cmlkZSkpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCAoZlNpemUgKyB4UkNvcm5lcikgLyBvcmlnU3RyaWRlKTtcblxuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCBNYXRoLmNlaWwoeENDb3JuZXIgLyBvcmlnU3RyaWRlKSk7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgKGZTaXplICsgeENDb3JuZXIpIC8gb3JpZ1N0cmlkZSk7XG5cbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAqIG9yaWdTdHJpZGUgLSB4UkNvcm5lcjtcblxuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDICogb3JpZ1N0cmlkZSAtIHhDQ29ybmVyO1xuXG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBvcmlnT3V0cHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9XG4gICAgICAgICAgICAgICAgICAgIHdlaWdodHMuZ2V0KGZTaXplIC0gMSAtIHdSLCBmU2l6ZSAtIDEgLSB3QywgZDIsIGQxKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJpYXMgPSBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXQoZDIpIDogMDtcbiAgICAgICAgICB5LnNldChkb3RQcm9kICsgYmlhcywgeVIsIHlDLCBkMik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICAvKipcbiAgICogaW1hZ2UgaXMgb2Ygc2hhcGUgW3IsIGMsIGQxXS5cbiAgICogd2VpZ2h0cyBpcyBvZiBzaGFwZSBbRiwgRiwgZDEsIGQyXS5cbiAgICovXG4gIHByb3RlY3RlZCBjb252MmRUcmFuc3Bvc2VTaGFkZXJMaWtlKFxuICAgICAgeDogQXJyYXkzRCwgb3JpZ1dlaWdodHM6IEFycmF5NEQsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGZTaXplID0gb3JpZ1dlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IG9yaWdXZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG9yaWdPdXRwdXREZXB0aCA9IG9yaWdXZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIHhEZXB0aF0gPSB4LnNoYXBlO1xuXG4gICAgLy8gRGlsYXRlIHRoZSBpbnB1dC5cbiAgICBjb25zdCB4Um93c0RpbGF0ZWQgPSAoeFJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IHhDb2xzRGlsYXRlZCA9ICh4Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzRGlsYXRlZCwgeENvbHNEaWxhdGVkLCBvcmlnT3V0cHV0RGVwdGhdLCBmU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIDEsXG4gICAgICAgIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuXG4gICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG9yaWdJbnB1dERlcHRoOyArK2QyKSB7XG4gICAgICBmb3IgKGxldCB5UiA9IDA7IHlSIDwgeS5zaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIC8vIFNoYWRlciBjb2RlIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgLSBwYWQ7XG4gICAgICAgICAgbGV0IGRvdFByb2QgPSAwO1xuICAgICAgICAgIGZvciAobGV0IHdSID0gMDsgd1IgPCBmU2l6ZTsgKyt3Uikge1xuICAgICAgICAgICAgY29uc3QgeFIgPSAoeFJDb3JuZXIgKyB3UikgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgaWYgKHhSIDwgMCB8fCB4UiA+PSB4Um93cyB8fCBNYXRoLmZsb29yKHhSKSAhPT0geFIpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGxldCB3QyA9IDA7IHdDIDwgZlNpemU7ICsrd0MpIHtcbiAgICAgICAgICAgICAgY29uc3QgeEMgPSAoeENDb3JuZXIgKyB3QykgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgICBpZiAoeEMgPCAwIHx8IHhDID49IHhDb2xzIHx8IE1hdGguZmxvb3IoeEMpICE9PSB4Qykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBvcmlnT3V0cHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9XG4gICAgICAgICAgICAgICAgICAgIG9yaWdXZWlnaHRzLmdldChmU2l6ZSAtIDEgLSB3UiwgZlNpemUgLSAxIC0gd0MsIGQyLCBkMSk7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIHdlaWdodDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICB5LnNldChkb3RQcm9kLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIGNvbnYyZERlcldlaWdodHMoXG4gICAgICB4OiBBcnJheTNELCBkWTogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTREIHtcbiAgICBjb25zdCBpbnB1dERlcHRoID0geC5zaGFwZVsyXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IGRZLnNoYXBlWzJdO1xuICAgIGNvbnN0IHdlaWdodHNTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1NoYXBlNEQoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICBjb25zdCBkVyA9IEFycmF5NEQuemVyb3Mod2VpZ2h0c1NoYXBlKTtcblxuICAgIGNvbnN0IHlOdW1Sb3dzID0gZFkuc2hhcGVbMF07XG4gICAgY29uc3QgeU51bUNvbHMgPSBkWS5zaGFwZVsxXTtcbiAgICBjb25zdCB4TnVtUm93cyA9IHguc2hhcGVbMF07XG4gICAgY29uc3QgeE51bUNvbHMgPSB4LnNoYXBlWzFdO1xuXG4gICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICBjb25zdCB5Uk1pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgoemVyb1BhZCAtIHdSKSAvIHN0cmlkZSkpO1xuICAgICAgY29uc3QgeVJNYXggPSBNYXRoLm1pbih5TnVtUm93cywgKHhOdW1Sb3dzICsgemVyb1BhZCAtIHdSKSAvIHN0cmlkZSk7XG5cbiAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICBjb25zdCB5Q01pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCgoemVyb1BhZCAtIHdDKSAvIHN0cmlkZSkpO1xuICAgICAgICBjb25zdCB5Q01heCA9IE1hdGgubWluKHlOdW1Db2xzLCAoeE51bUNvbHMgKyB6ZXJvUGFkIC0gd0MpIC8gc3RyaWRlKTtcblxuICAgICAgICBmb3IgKGxldCBkMSA9IDA7IGQxIDwgaW5wdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgICAgICAgLy8gTmVlZCB0byBjb252b2x2ZS5cbiAgICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICAgIGZvciAobGV0IHlSID0geVJNaW47IHlSIDwgeVJNYXg7ICsreVIpIHtcbiAgICAgICAgICAgICAgY29uc3QgeFIgPSB3UiArIHlSICogc3RyaWRlIC0gemVyb1BhZDtcbiAgICAgICAgICAgICAgZm9yIChsZXQgeUMgPSB5Q01pbjsgeUMgPCB5Q01heDsgKyt5Qykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHhDID0gd0MgKyB5QyAqIHN0cmlkZSAtIHplcm9QYWQ7XG4gICAgICAgICAgICAgICAgZG90UHJvZCArPSB4LmdldCh4UiwgeEMsIGQxKSAqIGRZLmdldCh5UiwgeUMsIGQyKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZFcuc2V0KGRvdFByb2QsIHdSLCB3QywgZDEsIGQyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGRXO1xuICB9XG5cbiAgY29udjJkRGVyQmlhcyhkWTogQXJyYXkzRCk6IEFycmF5MUQge1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgbnVtUm93cyA9IGRZLnNoYXBlWzBdO1xuICAgIGNvbnN0IG51bUNvbHMgPSBkWS5zaGFwZVsxXTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KG91dHB1dERlcHRoKTtcbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3V0cHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGxldCBzdW0gPSAwO1xuICAgICAgZm9yIChsZXQgciA9IDA7IHIgPCBudW1Sb3dzOyArK3IpIHtcbiAgICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBudW1Db2xzOyArK2MpIHtcbiAgICAgICAgICBzdW0gKz0gZFkuZ2V0KHIsIGMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdmFsdWVzW2QyXSA9IHN1bTtcbiAgICB9XG4gICAgcmV0dXJuIEFycmF5MUQubmV3KHZhbHVlcyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KHQ6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUIHtcbiAgICBjb25zdCBuZXdTaGFwZTogbnVtYmVyW10gPSBuZXcgQXJyYXkodC5yYW5rKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld1NoYXBlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBuZXdTaGFwZVtpXSA9IHQuc2hhcGVbbmV3RGltW2ldXTtcbiAgICB9XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh0LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IHQuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgcmVzdWx0ID0gTkRBcnJheS5tYWtlPFQ+KG5ld1NoYXBlLCB7dmFsdWVzOiByZXN1bHRWYWx1ZXN9KTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHQuc2l6ZTsgKytpKSB7XG4gICAgICBjb25zdCBsb2MgPSB0LmluZGV4VG9Mb2MoaSk7XG5cbiAgICAgIC8vIFBlcm11dGUgbG9jYXRpb24uXG4gICAgICBjb25zdCBuZXdMb2M6IG51bWJlcltdID0gbmV3IEFycmF5KGxvYy5sZW5ndGgpO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZXdMb2MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgbmV3TG9jW2ldID0gbG9jW25ld0RpbVtpXV07XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG5ld0luZGV4ID0gcmVzdWx0LmxvY1RvSW5kZXgobmV3TG9jKTtcbiAgICAgIHJlc3VsdFZhbHVlc1tuZXdJbmRleF0gPSB2YWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcml2YXRlIHBvb2woXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIsXG4gICAgICBwb29sVHlwZTogJ21heCd8J21pbid8J2F2ZycpIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBkZXB0aF0gPSB4LnNoYXBlO1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbeFJvd3MsIHhDb2xzLCBkZXB0aF0sIGZTaXplLCBkZXB0aCwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IHkgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgeFJDb3JuZXIpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCBmU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IHkuc2hhcGVbMV07ICsreUMpIHtcbiAgICAgICAgICBjb25zdCB4Q0Nvcm5lciA9IHlDICogc3RyaWRlIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IHhDTWluID0gTWF0aC5tYXgoMCwgeENDb3JuZXIpO1xuICAgICAgICAgIGNvbnN0IHhDTWF4ID0gTWF0aC5taW4oeENvbHMsIGZTaXplICsgeENDb3JuZXIpO1xuXG5cbiAgICAgICAgICBsZXQgbWluTWF4VmFsdWUgPVxuICAgICAgICAgICAgICAocG9vbFR5cGUgPT09ICdtYXgnID8gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZIDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlci5QT1NJVElWRV9JTkZJTklUWSk7XG4gICAgICAgICAgbGV0IGF2Z1ZhbHVlID0gMDtcblxuICAgICAgICAgIGZvciAobGV0IHhSID0geFJNaW47IHhSIDwgeFJNYXg7ICsreFIpIHtcbiAgICAgICAgICAgIGNvbnN0IHdSID0geFIgLSB4UkNvcm5lcjtcbiAgICAgICAgICAgIGZvciAobGV0IHhDID0geENNaW47IHhDIDwgeENNYXg7ICsreEMpIHtcbiAgICAgICAgICAgICAgY29uc3Qgd0MgPSB4QyAtIHhDQ29ybmVyO1xuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZCk7XG4gICAgICAgICAgICAgIGlmIChpc05hTihwaXhlbCkpIHtcbiAgICAgICAgICAgICAgICBtaW5NYXhWYWx1ZSA9IE5hTjtcbiAgICAgICAgICAgICAgICBhdmdWYWx1ZSA9IE5hTjtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoKHBvb2xUeXBlID09PSAnbWF4JyAmJiBwaXhlbCA+IG1pbk1heFZhbHVlKSB8fFxuICAgICAgICAgICAgICAgICAgKHBvb2xUeXBlID09PSAnbWluJyAmJiBwaXhlbCA8IG1pbk1heFZhbHVlKSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gcGl4ZWw7XG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAocG9vbFR5cGUgPT09ICdhdmcnKSB7XG4gICAgICAgICAgICAgICAgYXZnVmFsdWUgKz0gcGl4ZWwgLyAoZlNpemUgKiBmU2l6ZSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpc05hTihtaW5NYXhWYWx1ZSkpIHtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHkuc2V0KHBvb2xUeXBlID09PSAnYXZnJyA/IGF2Z1ZhbHVlIDogbWluTWF4VmFsdWUsIHlSLCB5QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIHRoaXMucG9vbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdtYXgnKTtcbiAgfVxuXG4gIG1heFBvb2xQb3NpdGlvbnMoeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKSB7XG4gICAgY29uc3QgW3hSb3dzLCB4Q29scywgZGVwdGhdID0geC5zaGFwZTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRCh4LnNoYXBlLCBmU2l6ZSwgZGVwdGgsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCBtYXhQb3NpdGlvbnMgPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCBvdXRwdXRTaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSICogc3RyaWRlIC0gcGFkO1xuICAgICAgICBjb25zdCB4Uk1pbiA9IE1hdGgubWF4KDAsIHhSQ29ybmVyKTtcbiAgICAgICAgY29uc3QgeFJNYXggPSBNYXRoLm1pbih4Um93cywgZlNpemUgKyB4UkNvcm5lcik7XG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCBvdXRwdXRTaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZlNpemUgKyB4Q0Nvcm5lcik7XG4gICAgICAgICAgbGV0IG1heFZhbHVlID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgICAgICAgIGxldCBtYXhQb3NpdGlvbiA9IC0xO1xuICAgICAgICAgIGZvciAobGV0IHhSID0geFJNaW47IHhSIDwgeFJNYXg7ICsreFIpIHtcbiAgICAgICAgICAgIGNvbnN0IHdSID0geFIgLSB4UkNvcm5lcjtcbiAgICAgICAgICAgIGZvciAobGV0IHhDID0geENNaW47IHhDIDwgeENNYXg7ICsreEMpIHtcbiAgICAgICAgICAgICAgY29uc3Qgd0MgPSB4QyAtIHhDQ29ybmVyO1xuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZCk7XG4gICAgICAgICAgICAgIGlmIChwaXhlbCA+IG1heFZhbHVlKSB7XG4gICAgICAgICAgICAgICAgbWF4VmFsdWUgPSBwaXhlbDtcbiAgICAgICAgICAgICAgICBtYXhQb3NpdGlvbiA9IHdSICogZlNpemUgKyB3QztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBtYXhQb3NpdGlvbnMuc2V0KG1heFBvc2l0aW9uLCB5UiwgeUMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBtYXhQb3NpdGlvbnM7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoXG4gICAgICBkeTogQXJyYXkzRCwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9zaXRpb25zID0gdGhpcy5tYXhQb29sUG9zaXRpb25zKHgsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IFtkeVJvd3MsIGR5Q29scywgZGVwdGhdID0gZHkuc2hhcGU7XG5cbiAgICAvLyBEaWxhdGUgdGhlIGlucHV0LlxuICAgIGNvbnN0IGR5Um93c0RpbGF0ZWQgPSAoZHlSb3dzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcbiAgICBjb25zdCBkeENvbHNEaWxhdGVkID0gKGR5Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW2R5Um93c0RpbGF0ZWQsIGR4Q29sc0RpbGF0ZWQsIGRlcHRoXSwgZlNpemUsIGRlcHRoLCAxLCBwYWQpO1xuICAgIGNvbnN0IGR4ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBkID0gMDsgZCA8IGRlcHRoOyArK2QpIHtcbiAgICAgIGZvciAobGV0IGR4UiA9IDA7IGR4UiA8IGR4LnNoYXBlWzBdOyArK2R4Uikge1xuICAgICAgICBmb3IgKGxldCBkeEMgPSAwOyBkeEMgPCBkeC5zaGFwZVsxXTsgKytkeEMpIHtcbiAgICAgICAgICAvLyBTaGFkZXIgY29kZSBiZWdpbnMuXG4gICAgICAgICAgY29uc3QgZHlSQ29ybmVyID0gZHhSIC0gcGFkO1xuICAgICAgICAgIGNvbnN0IGR5Q0Nvcm5lciA9IGR4QyAtIHBhZDtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICAgICAgICBjb25zdCBkeVIgPSAoZHlSQ29ybmVyICsgd1IpIC8gb3JpZ1N0cmlkZTtcbiAgICAgICAgICAgIGlmIChkeVIgPCAwIHx8IGR5UiA+PSBkeVJvd3MgfHwgTWF0aC5mbG9vcihkeVIpICE9PSBkeVIpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBmb3IgKGxldCB3QyA9IDA7IHdDIDwgZlNpemU7ICsrd0MpIHtcbiAgICAgICAgICAgICAgY29uc3QgZHlDID0gKGR5Q0Nvcm5lciArIHdDKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICAgIGlmIChkeUMgPCAwIHx8IGR5QyA+PSBkeUNvbHMgfHwgTWF0aC5mbG9vcihkeUMpICE9PSBkeUMpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBtYXhQb3MgPSBmU2l6ZSAqIGZTaXplIC0gMSAtIG1heFBvc2l0aW9ucy5nZXQoZHlSLCBkeUMsIGQpO1xuICAgICAgICAgICAgICBjb25zdCBjdXJQb3MgPSB3UiAqIGZTaXplICsgd0M7XG5cbiAgICAgICAgICAgICAgY29uc3QgbWFzayA9IG1heFBvcyA9PT0gY3VyUG9zID8gMSA6IDA7XG4gICAgICAgICAgICAgIGlmIChtYXNrID09PSAwKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IGR5LmdldChkeVIsIGR5QywgZCk7XG4gICAgICAgICAgICAgIGRvdFByb2QgKz0gcGl4ZWwgKiBtYXNrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBkeC5zZXQoZG90UHJvZCwgZHhSLCBkeEMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkeDtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5Qb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5wb29sKHgsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21pbicpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGF2Z1Bvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnBvb2woeCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnYXZnJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGFsaWduQ29ybmVyczogYm9vbGVhbik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG91dHB1dCA9IEFycmF5M0QuemVyb3MoW25ld1NoYXBlMkRbMF0sIG5ld1NoYXBlMkRbMV0sIHguc2hhcGVbMl1dKTtcblxuICAgIGNvbnN0IGVmZmVjdGl2ZUlucHV0U2l6ZSA9XG4gICAgICAgIGFsaWduQ29ybmVycyA/IFt4LnNoYXBlWzBdIC0gMSwgeC5zaGFwZVsxXSAtIDEsIHguc2hhcGVbMl1dIDogeC5zaGFwZTtcbiAgICBjb25zdCBlZmZlY3RpdmVPdXRwdXRTaXplID0gYWxpZ25Db3JuZXJzID9cbiAgICAgICAgW291dHB1dC5zaGFwZVswXSAtIDEsIG91dHB1dC5zaGFwZVsxXSAtIDEsIG91dHB1dC5zaGFwZVsyXV0gOlxuICAgICAgICBvdXRwdXQuc2hhcGU7XG4gICAgZm9yIChsZXQgciA9IDA7IHIgPCBvdXRwdXQuc2hhcGVbMF07IHIrKykge1xuICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCBvdXRwdXQuc2hhcGVbMV07IGMrKykge1xuICAgICAgICBmb3IgKGxldCBkID0gMDsgZCA8IG91dHB1dC5zaGFwZVsyXTsgZCsrKSB7XG4gICAgICAgICAgLy8gQmVnaW4gc2hhZGVyLlxuXG4gICAgICAgICAgLy8gQ29tcHV0ZSB0aGUgZnJhY3Rpb25hbCBpbmRleCBvZiB0aGUgc291cmNlLlxuICAgICAgICAgIGNvbnN0IHNvdXJjZUZyYWNSb3cgPVxuICAgICAgICAgICAgICAoZWZmZWN0aXZlSW5wdXRTaXplWzBdKSAqIHIgLyAoZWZmZWN0aXZlT3V0cHV0U2l6ZVswXSk7XG4gICAgICAgICAgY29uc3Qgc291cmNlRnJhY0NvbCA9XG4gICAgICAgICAgICAgIChlZmZlY3RpdmVJbnB1dFNpemVbMV0pICogYyAvIChlZmZlY3RpdmVPdXRwdXRTaXplWzFdKTtcblxuICAgICAgICAgIGNvbnN0IHNvdXJjZVJvd0Zsb29yID0gTWF0aC5mbG9vcihzb3VyY2VGcmFjUm93KTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VSb3dDZWlsID1cbiAgICAgICAgICAgICAgTWF0aC5taW4oeC5zaGFwZVswXSAtIDEsIE1hdGguY2VpbChzb3VyY2VGcmFjUm93KSk7XG4gICAgICAgICAgY29uc3Qgc291cmNlQ29sRmxvb3IgPSBNYXRoLmZsb29yKHNvdXJjZUZyYWNDb2wpO1xuICAgICAgICAgIGNvbnN0IHNvdXJjZUNvbENlaWwgPVxuICAgICAgICAgICAgICBNYXRoLm1pbih4LnNoYXBlWzFdIC0gMSwgTWF0aC5jZWlsKHNvdXJjZUZyYWNDb2wpKTtcblxuICAgICAgICAgIGNvbnN0IHRvcExlZnQgPSB4LmdldChzb3VyY2VSb3dGbG9vciwgc291cmNlQ29sRmxvb3IsIGQpO1xuICAgICAgICAgIGNvbnN0IGJvdHRvbUxlZnQgPSB4LmdldChzb3VyY2VSb3dDZWlsLCBzb3VyY2VDb2xGbG9vciwgZCk7XG4gICAgICAgICAgY29uc3QgdG9wUmlnaHQgPSB4LmdldChzb3VyY2VSb3dGbG9vciwgc291cmNlQ29sQ2VpbCwgZCk7XG4gICAgICAgICAgY29uc3QgYm90dG9tUmlnaHQgPSB4LmdldChzb3VyY2VSb3dDZWlsLCBzb3VyY2VDb2xDZWlsLCBkKTtcblxuICAgICAgICAgIGNvbnN0IHJvd0ZyYWMgPSBzb3VyY2VGcmFjUm93IC0gc291cmNlUm93Rmxvb3I7XG4gICAgICAgICAgY29uc3QgY29sRnJhYyA9IHNvdXJjZUZyYWNDb2wgLSBzb3VyY2VDb2xGbG9vcjtcblxuICAgICAgICAgIGNvbnN0IHRvcCA9IHRvcExlZnQgKyAodG9wUmlnaHQgLSB0b3BMZWZ0KSAqIGNvbEZyYWM7XG4gICAgICAgICAgY29uc3QgYm90dG9tID0gYm90dG9tTGVmdCArIChib3R0b21SaWdodCAtIGJvdHRvbUxlZnQpICogY29sRnJhYztcbiAgICAgICAgICBjb25zdCBuZXdWYWx1ZSA9IHRvcCArIChib3R0b20gLSB0b3ApICogcm93RnJhYztcblxuICAgICAgICAgIG91dHB1dC5zZXQobmV3VmFsdWUsIHIsIGMsIGQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dHB1dDtcbiAgfVxuXG4gIHByb3RlY3RlZCBiYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbWVhbjogQXJyYXkzRHxBcnJheTFELCB2YXJpYW5jZTogQXJyYXkzRHxBcnJheTFELFxuICAgICAgdmFyaWFuY2VFcHNpbG9uID0gLjAwMSwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEIHtcbiAgICBjb25zdCB4VmFsdWVzID0geC5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBtZWFuVmFsdWVzID0gbWVhbi5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCB2YXJpYW5jZVZhbHVlcyA9IHZhcmlhbmNlLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHNjYWxlVmFsdWVzID0gc2NhbGUgPyBzY2FsZS5nZXRWYWx1ZXMoKSA6IG5ldyBGbG9hdDMyQXJyYXkoWzFdKTtcbiAgICBjb25zdCBvZmZzZXRWYWx1ZXMgPSBvZmZzZXQgPyBvZmZzZXQuZ2V0VmFsdWVzKCkgOiBuZXcgRmxvYXQzMkFycmF5KFswXSk7XG4gICAgY29uc3Qgb3V0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh4VmFsdWVzLmxlbmd0aCk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHhWYWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIG91dFZhbHVlc1tpXSA9IG9mZnNldFZhbHVlc1tpICUgb2Zmc2V0VmFsdWVzLmxlbmd0aF0gK1xuICAgICAgICAgICh4VmFsdWVzW2ldIC0gbWVhblZhbHVlc1tpICUgbWVhblZhbHVlcy5sZW5ndGhdKSAqXG4gICAgICAgICAgICAgIHNjYWxlVmFsdWVzW2kgJSBzY2FsZVZhbHVlcy5sZW5ndGhdIC9cbiAgICAgICAgICAgICAgTWF0aC5zcXJ0KFxuICAgICAgICAgICAgICAgICAgdmFyaWFuY2VWYWx1ZXNbaSAlIHZhcmlhbmNlVmFsdWVzLmxlbmd0aF0gKyB2YXJpYW5jZUVwc2lsb24pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KHguc2hhcGUsIHt2YWx1ZXM6IG91dFZhbHVlc30pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL2NvbnZfdXRpbCc7XG5pbXBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoJztcbmltcG9ydCAqIGFzIG5kYXJyYXkgZnJvbSAnLi9uZGFycmF5JztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgQXJyYXkzRCwgQXJyYXk0RCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL25kYXJyYXknO1xuaW1wb3J0ICogYXMgYWRkc2NhbGVkbWF0X2dwdSBmcm9tICcuL3dlYmdsL2FkZHNjYWxlZG1hdF9ncHUnO1xuaW1wb3J0ICogYXMgYWRkc3VibXVsZGl2X2dwdSBmcm9tICcuL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUnO1xuaW1wb3J0IHtPcGVyYW5kVHlwZX0gZnJvbSAnLi93ZWJnbC9hZGRzdWJtdWxkaXZfZ3B1JztcbmltcG9ydCAqIGFzIGFyZ21heGVxdWFsc19ncHUgZnJvbSAnLi93ZWJnbC9hcmdtYXhlcXVhbHNfZ3B1JztcbmltcG9ydCAqIGFzIGFyZ21pbm1heF9ncHUgZnJvbSAnLi93ZWJnbC9hcmdtaW5tYXhfZ3B1JztcbmltcG9ydCAqIGFzIGF2Z19wb29sX2dwdSBmcm9tICcuL3dlYmdsL2F2Z19wb29sX2dwdSc7XG5pbXBvcnQgKiBhcyBiYXRjaG5vcm1fZ3B1IGZyb20gJy4vd2ViZ2wvYmF0Y2hub3JtX2dwdSc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF9ncHUgZnJvbSAnLi93ZWJnbC9jb25jYXQzZF9ncHUnO1xuaW1wb3J0ICogYXMgY29udl9iYWNrcHJvcF9ncHUgZnJvbSAnLi93ZWJnbC9jb252X2JhY2twcm9wX2dwdSc7XG5pbXBvcnQgKiBhcyBjb252X2dwdSBmcm9tICcuL3dlYmdsL2NvbnZfZ3B1JztcbmltcG9ydCAqIGFzIGNvcHlfZ3B1IGZyb20gJy4vd2ViZ2wvY29weV9ncHUnO1xuaW1wb3J0ICogYXMgZXhwX2dwdSBmcm9tICcuL3dlYmdsL2V4cF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBncGdwdV91dGlsIGZyb20gJy4vd2ViZ2wvZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyBsb2dfZ3B1IGZyb20gJy4vd2ViZ2wvbG9nX2dwdSc7XG5pbXBvcnQgKiBhcyBsb2dzdW1leHBfZ3B1IGZyb20gJy4vd2ViZ2wvbG9nc3VtZXhwX2dwdSc7XG5pbXBvcnQgKiBhcyBtYXhfcG9vbF9iYWNrcHJvcF9ncHUgZnJvbSAnLi93ZWJnbC9tYXhfcG9vbF9iYWNrcHJvcF9ncHUnO1xuaW1wb3J0ICogYXMgbWF4X3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvbWF4X3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIG1pbl9wb29sX2dwdSBmcm9tICcuL3dlYmdsL21pbl9wb29sX2dwdSc7XG5pbXBvcnQgKiBhcyBtaW5tYXhfZ3B1IGZyb20gJy4vd2ViZ2wvbWlubWF4X2dwdSc7XG5pbXBvcnQgKiBhcyBtdWxtYXRfZ3B1IGZyb20gJy4vd2ViZ2wvbXVsbWF0X2dwdSc7XG5pbXBvcnQgKiBhcyBuZWdfZ3B1IGZyb20gJy4vd2ViZ2wvbmVnX2dwdSc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3dlYmdsL3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIHJlZHVjZXN1bV9ncHUgZnJvbSAnLi93ZWJnbC9yZWR1Y2VzdW1fZ3B1JztcbmltcG9ydCAqIGFzIHJlbHVfZ3B1IGZyb20gJy4vd2ViZ2wvcmVsdV9ncHUnO1xuaW1wb3J0ICogYXMgcmVzaGFwZV9ncHUgZnJvbSAnLi93ZWJnbC9yZXNoYXBlX2dwdSc7XG5pbXBvcnQgKiBhcyByZXNpemVfYmlsaW5lYXJfZ3B1IGZyb20gJy4vd2ViZ2wvcmVzaXplX2JpbGluZWFyX2dwdSc7XG5pbXBvcnQgKiBhcyBzaGFkZXJfY29tcGlsZXIgZnJvbSAnLi93ZWJnbC9zaGFkZXJfY29tcGlsZXInO1xuaW1wb3J0ICogYXMgc2lnbW9pZF9ncHUgZnJvbSAnLi93ZWJnbC9zaWdtb2lkX2dwdSc7XG5pbXBvcnQgKiBhcyBzdGVwX2dwdSBmcm9tICcuL3dlYmdsL3N0ZXBfZ3B1JztcbmltcG9ydCB7VGV4dHVyZU1hbmFnZXJ9IGZyb20gJy4vd2ViZ2wvdGV4dHVyZV9tYW5hZ2VyJztcbmltcG9ydCAqIGFzIHRyaWdfZ3B1IGZyb20gJy4vd2ViZ2wvdHJpZ19ncHUnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsL3dlYmdsX3V0aWwnO1xuXG5jb25zdCBBUkdNQVhfUFJPRyA9ICdhcmdtYXgnO1xuY29uc3QgQVJHTUFYX0VRVUFMU19QUk9HID0gJ2FyZ21heGVxdWFscyc7XG5jb25zdCBBUkdNSU5fUFJPRyA9ICdhcmdtaW4nO1xuXG5jb25zdCBCQVRDSE5PUk1fUFJPRyA9ICdiYXRjaG5vcm0nO1xuXG5jb25zdCBDT1BZX1BST0cgPSAnY29weSc7XG5jb25zdCBDT05DQVRfUFJPRyA9ICdjb25jYXQnO1xuXG4vLyBNYXRyaXggYWxnZWJyYS5cbmNvbnN0IEFERF9TQ0FMRURfTUFUX1BST0cgPSAnYWRkc2NhbGVkbWF0JztcbmNvbnN0IE1BVE1VTF9QUk9HID0gJ21hdG11bCc7XG5cbi8vIEVsZW1lbnQtd2lzZSBvcHMuXG5jb25zdCBSRUxVX1BST0cgPSAncmVsdSc7XG5jb25zdCBUQU5IX1BST0cgPSAndGFuaCc7XG5jb25zdCBTSU5fUFJPRyA9ICdzaW4nO1xuY29uc3QgU0lHTU9JRF9QUk9HID0gJ3NpZ21vaWQnO1xuY29uc3QgTUFYX1BST0cgPSAnbWF4JztcbmNvbnN0IE1JTl9QUk9HID0gJ21pbic7XG5jb25zdCBORUdfUFJPRyA9ICduZWcnO1xuY29uc3QgRVhQX1BST0cgPSAnZXhwJztcbmNvbnN0IExPR19QUk9HID0gJ2xvZyc7XG5jb25zdCBTVU1fUFJPRyA9ICdzdW0nO1xuY29uc3QgU1RFUF9QUk9HID0gJ3N0ZXAnO1xuY29uc3QgTE9HU1VNRVhQX1BST0cgPSAnbG9nc3VtZXhwJztcbmNvbnN0IFJFU0hBUEVfUFJPRyA9ICdyZXNoYXBlJztcbmNvbnN0IEFERF9TVU1fTVVMX0RJVl9QUk9HID0gJ2FkZHN1bW11bGRpdic7XG5cbi8vIENvbnZvbHV0aW9uLlxuY29uc3QgQ09OVjJEX1BST0cgPSAnY29udic7XG5jb25zdCBDT05WMkRfVFJBTlNQT1NFX1BST0cgPSAnY29udl90cmFuc3Bvc2UnO1xuY29uc3QgQ09OVjJEX0RFUldfUFJPRyA9ICdjb252X2RlcncnO1xuY29uc3QgQ09OVjJEX0RFUkJfUFJPRyA9ICdjb252X2RlcmInO1xuY29uc3QgTUFYX1BPT0xfUFJPRyA9ICdtYXhwb29sJztcbmNvbnN0IE1BWF9QT09MX1BPU0lUSU9OU19QUk9HID0gJ21heHBvb2xfcG9zbic7XG5jb25zdCBNQVhfUE9PTF9CQUNLUFJPUF9QUk9HID0gJ21heHBvb2xfYmFja3Byb3AnO1xuY29uc3QgTUlOX1BPT0xfUFJPRyA9ICdtaW5wb29sJztcbmNvbnN0IEFWR19QT09MX1BST0cgPSAnYXZncG9vbCc7XG5cbmNvbnN0IFJFU0laRV9CSUxJTkVBUl9QUk9HID0gJ3Jlc2l6ZWJpbGluJztcblxuZnVuY3Rpb24gbWFrZUNvcHlQcm9ncmFtTmFtZShcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBkZXN0U2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXBlTmFtZSA9IGAke3NvdXJjZVNoYXBlUm93Q29sWzBdfV8ke3NvdXJjZVNoYXBlUm93Q29sWzFdfWA7XG4gIGNvbnN0IHNyY1NpemVOYW1lID0gYCR7c291cmNlU2l6ZVJvd0NvbFswXX1fJHtzb3VyY2VTaXplUm93Q29sWzFdfWA7XG4gIGNvbnN0IGRzdFNpemVOYW1lID0gYCR7ZGVzdFNpemVSb3dDb2xbMF19XyR7ZGVzdFNpemVSb3dDb2xbMV19YDtcbiAgcmV0dXJuIGAke0NPUFlfUFJPR31fJHtzaGFwZU5hbWV9XyR7c3JjU2l6ZU5hbWV9XyR7ZHN0U2l6ZU5hbWV9YDtcbn1cblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlNYXRoR1BVIGV4dGVuZHMgTkRBcnJheU1hdGgge1xuICBwcml2YXRlIGdwZ3B1OiBHUEdQVUNvbnRleHQ7XG4gIHByaXZhdGUgdGV4dHVyZU1hbmFnZXI6IFRleHR1cmVNYW5hZ2VyO1xuICBwcml2YXRlIHByb2dyYW1DYWNoZToge1trZXk6IHN0cmluZ106IFdlYkdMUHJvZ3JhbX0gPSB7fTtcbiAgcHJpdmF0ZSBncGdwdUNyZWF0ZWRMb2NhbGx5OiBib29sZWFuO1xuXG4gIGNvbnN0cnVjdG9yKGdwZ3B1PzogR1BHUFVDb250ZXh0LCBzYWZlTW9kZSA9IHRydWUpIHtcbiAgICBzdXBlcihzYWZlTW9kZSk7XG4gICAgaWYgKGdwZ3B1ID09IG51bGwpIHtcbiAgICAgIGNvbnN0IGdsID0gZ3BncHVfdXRpbC5jcmVhdGVXZWJHTENvbnRleHQoKTtcbiAgICAgIHRoaXMuZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KGdsKTtcbiAgICAgIHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSA9IHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZ3BncHUgPSBncGdwdTtcbiAgICAgIHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSA9IGZhbHNlO1xuICAgIH1cblxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIgPSBuZXcgVGV4dHVyZU1hbmFnZXIodGhpcy5ncGdwdSk7XG5cbiAgICBuZGFycmF5LmluaXRpYWxpemVHUFUodGhpcy5ncGdwdSwgdGhpcy50ZXh0dXJlTWFuYWdlcik7XG4gIH1cblxuICBnZXRHUEdQVUNvbnRleHQoKTogR1BHUFVDb250ZXh0IHtcbiAgICByZXR1cm4gdGhpcy5ncGdwdTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjbG9uZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIG1ha2VDb3B5UHJvZ3JhbU5hbWUodGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDLCB0ZXh0dXJlU2hhcGVSQyksXG4gICAgICAgICgpID0+IGNvcHlfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDLCB0ZXh0dXJlU2hhcGVSQykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgY29weV9ncHUuY29weShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDLCBbMCwgMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDLCByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQywgWzAsIDBdLCB0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNoYXBlSW50ZXJuYWw8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgbmRhcnJheTogVDEsIG5ld1NoYXBlOiBudW1iZXJbXSk6IFQyIHtcbiAgICBsZXQgbmV3VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgICBzd2l0Y2ggKG5ld1NoYXBlLmxlbmd0aCkge1xuICAgICAgY2FzZSAwOlxuICAgICAgICBuZXdUZXhTaGFwZSA9IFsxLCAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCBuZXdTaGFwZVsxXV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICBuZXdUZXhTaGFwZSA9IFtuZXdTaGFwZVswXSwgbmV3U2hhcGVbMV0gKiBuZXdTaGFwZVsyXV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhyb3cgRXJyb3IoXG4gICAgICAgICAgICBgUmVzaGFwZXMgaW50byAke25ld1NoYXBlLmxlbmd0aH0tZGltIG5kYXJyYXkgaXMgbm90IHlldCBgICtcbiAgICAgICAgICAgIGBzdXBwb3J0ZWQgb24gR1BVYCk7XG4gICAgfVxuXG4gICAgY29uc3QgYWN0dWFsVGV4U2hhcGUgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKG5ld1RleFNoYXBlKTtcbiAgICBsZXQgY2xvbmVkQXJyYXk6IFQxO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxUZXhTaGFwZSwgbmV3VGV4U2hhcGUpKSB7XG4gICAgICBjbG9uZWRBcnJheSA9IHRoaXMucmVzaGFwZVRleHR1cmUobmRhcnJheSwgbmV3VGV4U2hhcGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjbG9uZWRBcnJheSA9IHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KTtcbiAgICB9XG4gICAgcmV0dXJuIGNsb25lZEFycmF5LnJlc2hhcGU8VDI+KG5ld1NoYXBlKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzbGljZTJESW50ZXJuYWwoXG4gICAgICBpbnB1dDogQXJyYXkyRCwgYmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRCB7XG4gICAgY29uc3QgcmVzdWx0ID0gTkRBcnJheS5tYWtlPEFycmF5MkQ+KHNpemVSb3dDb2wsIHtcbiAgICAgIHRleHR1cmU6IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoc2l6ZVJvd0NvbCksXG4gICAgICB0ZXh0dXJlU2hhcGVSQzogc2l6ZVJvd0NvbFxuICAgIH0pO1xuICAgIHRoaXMuY29weTJESW50ZXJuYWwoXG4gICAgICAgIGlucHV0LCBiZWdpblJvd0NvbCwgc2l6ZVJvd0NvbCwgcmVzdWx0LCBbMCwgMF0sIHNpemVSb3dDb2wpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29weTJESW50ZXJuYWwoXG4gICAgICBzb3VyY2U6IEFycmF5MkQsIHNvdXJjZUJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogQXJyYXkyRCxcbiAgICAgIGRlc3RCZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZCB7XG4gICAgY29uc3Qgc291cmNlU2hhcGVSQyA9IHNvdXJjZS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGRlc3RTaGFwZVJDID0gZGVzdC5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBtYWtlQ29weVByb2dyYW1OYW1lKHNvdXJjZVNoYXBlUkMsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3RTaXplUm93Q29sKSxcbiAgICAgICAgKCkgPT4gY29weV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICBzb3VyY2VTaGFwZVJDLCBzb3VyY2VTaXplUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCkpO1xuXG4gICAgY29weV9ncHUuY29weShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgc291cmNlLmdldFRleHR1cmUoKSwgc291cmNlU2hhcGVSQyxcbiAgICAgICAgc291cmNlQmVnaW5Sb3dDb2wsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3QuZ2V0VGV4dHVyZSgpLCBkZXN0U2hhcGVSQyxcbiAgICAgICAgZGVzdEJlZ2luUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29uY2F0M0RJbnRlcm5hbCh4MTogQXJyYXkzRCwgeDI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHgxVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeDEuc2hhcGUpO1xuICAgIGNvbnN0IHgyVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeDIuc2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYMVRleFNoYXBlID0geDEuZ2V0VGV4dHVyZVNoYXBlUkMoeDFUZXhTaGFwZVJDKTtcbiAgICBsZXQgY2xlYW51cFgxID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFgxVGV4U2hhcGUsIHgxVGV4U2hhcGVSQykpIHtcbiAgICAgIHgxID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4MSwgeDFUZXhTaGFwZVJDKTtcbiAgICAgIGNsZWFudXBYMSA9IHRydWU7XG4gICAgfVxuICAgIGNvbnN0IGFjdHVhbFgyVGV4U2hhcGUgPSB4Mi5nZXRUZXh0dXJlU2hhcGVSQyh4MlRleFNoYXBlUkMpO1xuICAgIGxldCBjbGVhbnVwWDIgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDJUZXhTaGFwZSwgeDJUZXhTaGFwZVJDKSkge1xuICAgICAgeDIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgyLCB4MlRleFNoYXBlUkMpO1xuICAgICAgY2xlYW51cFgyID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRTaGFwZVJDRCA9XG4gICAgICAgIGNvbmNhdDNkX3V0aWwuY29tcHV0ZUNvbmNhdDNET3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlLCBheGlzKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtDT05DQVRfUFJPR31fJHt4MS5zaGFwZX1fJHt4Mi5zaGFwZX1fJHtheGlzfWAsXG4gICAgICAgICgpID0+IGNvbmNhdDNkX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHgxLnNoYXBlLCB4Mi5zaGFwZSwgcmVzdWx0U2hhcGVSQ0QsIGF4aXMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZVJDRCk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb25jYXQzZF9ncHUuY29uY2F0M0QoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHgxLmdldFRleHR1cmUoKSwgeDIuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWDEpIHtcbiAgICAgIHgxLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBpZiAoY2xlYW51cFgyKSB7XG4gICAgICB4Mi5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGVSQ0QsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGMsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy0nLCBPcGVyYW5kVHlwZS5TQ0FMQVIpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyTWludXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJy0nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGVkQXJyYXlBZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBjMTogU2NhbGFyLCBhOiBULCBjMjogU2NhbGFyLCBiOiBUKSB7XG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKCF0aGlzLmRvR1BVU2hhcGVzTWF0Y2goYSwgYikpIHtcbiAgICAgIGIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGIsIGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgICBjbGVhbnVwQiA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIEFERF9TQ0FMRURfTUFUX1BST0csICgpID0+IGFkZHNjYWxlZG1hdF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBhZGRzY2FsZWRtYXRfZ3B1LmFkZFNjYWxlZE1hdHJpY2VzKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYi5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgYzEuZ2V0VGV4dHVyZSgpLCBjMi5nZXRUZXh0dXJlKCksIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgLy8gQnJpbmcgdGhlIHJlc3VsdCBiYWNrIHRvIHRoZSBvcmlnaW5hbCBzaGFwZS5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclRpbWVzQXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBjLCBhLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5TQ0FMQVIsICcqJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIE5FR19QUk9HLCAoKSA9PiBuZWdfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgbmVnX2dwdS5uZWcoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByaXZhdGUgcmVzaGFwZVRleHR1cmU8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIG5ld1RleHR1cmVTaGFwZTogW1xuICAgIG51bWJlciwgbnVtYmVyXG4gIF0pOiBUIHtcbiAgICBjb25zdCBhVGV4U2hhcGUgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgUkVTSEFQRV9QUk9HLCAoKSA9PiByZXNoYXBlX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKG5ld1RleHR1cmVTaGFwZSk7XG4gICAgcmVzaGFwZV9ncHUucmVzaGFwZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIGFUZXhTaGFwZVswXSwgYVRleFNoYXBlWzFdLFxuICAgICAgICByZXN1bHRUZXh0dXJlLCBuZXdUZXh0dXJlU2hhcGVbMF0sIG5ld1RleHR1cmVTaGFwZVsxXSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBhLnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IG5ld1RleHR1cmVTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1hdE11bEludGVybmFsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbixcbiAgICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBBcnJheTJEIHtcbiAgICBjb25zdCBzaGFyZWREaW0gPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMV0gOiBhLnNoYXBlWzBdO1xuICAgIGNvbnN0IG91dGVyU2hhcGVBID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzBdIDogYS5zaGFwZVsxXTtcbiAgICBjb25zdCBvdXRlclNoYXBlQiA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVsxXSA6IGIuc2hhcGVbMF07XG4gICAgY29uc3Qgb3V0U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbb3V0ZXJTaGFwZUEsIG91dGVyU2hhcGVCXTtcbiAgICBjb25zdCBvdXRUZXhTaGFwZSA9XG4gICAgICAgIHdlYmdsX3V0aWwuZ2V0VGV4dHVyZVNoYXBlRnJvbUxvZ2ljYWxTaGFwZSh0aGlzLmdwZ3B1LmdsLCBvdXRTaGFwZSk7XG4gICAgY29uc3Qgb3V0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUob3V0VGV4U2hhcGUpO1xuICAgIGNvbnN0IG91dCA9IG5ldyBBcnJheTJEKFxuICAgICAgICBvdXRTaGFwZSwge3RleHR1cmU6IG91dFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiBvdXRUZXhTaGFwZX0pO1xuXG4gICAgY29uc3Qga2V5ID0gc2hhZGVyX2NvbXBpbGVyLm1ha2VTaGFkZXJLZXkoW2EsIGJdLCBvdXQpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNQVRNVUxfUFJPR31fJHtrZXl9XyR7YU9yaWVudGF0aW9ufV8ke2JPcmllbnRhdGlvbn1gLFxuICAgICAgICAoKSA9PiBtdWxtYXRfZ3B1LmdldEZyYWdtZW50U2hhZGVyKFxuICAgICAgICAgICAgYSwgYiwgb3V0LCBhT3JpZW50YXRpb24sIGJPcmllbnRhdGlvbikpO1xuXG4gICAgbXVsbWF0X2dwdS5tdWx0aXBseU1hdHJpeChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIGIuZ2V0VGV4dHVyZSgpLCBvdXRUZXh0dXJlLFxuICAgICAgICBvdXRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gb3V0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJyonLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxCcm9hZGNhc3RJbnRlcm5hbChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdOb3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb246IG51bWJlciwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEIHtcbiAgICBjb25zdCB4VGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBsZXQgY2xlYW51cE1lYW4gPSBmYWxzZTtcbiAgICBjb25zdCBwcmVmZXJyZWRNZWFuVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICBtZWFuLnJhbmsgPT09IDEgPyBbMSwgbWVhbi5zaXplXSA6IHhUZXhTaGFwZTtcbiAgICBsZXQgbWVhblRleFNoYXBlID0gbWVhbi5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRNZWFuVGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChtZWFuVGV4U2hhcGUsIHByZWZlcnJlZE1lYW5UZXhTaGFwZSkpIHtcbiAgICAgIG1lYW4gPSB0aGlzLnJlc2hhcGVUZXh0dXJlKG1lYW4sIHByZWZlcnJlZE1lYW5UZXhTaGFwZSk7XG4gICAgICBtZWFuVGV4U2hhcGUgPSBwcmVmZXJyZWRNZWFuVGV4U2hhcGU7XG4gICAgICBjbGVhbnVwTWVhbiA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBWYXJpYW5jZSA9IGZhbHNlO1xuICAgIGNvbnN0IHByZWZlcnJlZFZhcmlhbmNlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICB2YXJpYW5jZS5yYW5rID09PSAxID8gWzEsIHZhcmlhbmNlLnNpemVdIDogeFRleFNoYXBlO1xuICAgIGxldCB2YXJpYW5jZVRleFNoYXBlID0gdmFyaWFuY2UuZ2V0VGV4dHVyZVNoYXBlUkMocHJlZmVycmVkTWVhblRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwodmFyaWFuY2VUZXhTaGFwZSwgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZSkpIHtcbiAgICAgIHZhcmlhbmNlID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh2YXJpYW5jZSwgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZSk7XG4gICAgICB2YXJpYW5jZVRleFNoYXBlID0gcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZTtcbiAgICAgIGNsZWFudXBWYXJpYW5jZSA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IHNjYWxlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl18bnVsbCA9IG51bGw7XG4gICAgbGV0IGNsZWFudXBTY2FsZSA9IGZhbHNlO1xuICAgIGlmIChzY2FsZSAhPSBudWxsKSB7XG4gICAgICBjb25zdCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgICBzY2FsZS5yYW5rID09PSAxID8gWzEsIHNjYWxlLnNpemVdIDogeFRleFNoYXBlO1xuXG4gICAgICBzY2FsZVRleFNoYXBlID0gc2NhbGUuZ2V0VGV4dHVyZVNoYXBlUkMocHJlZmVycmVkU2NhbGVUZXhTaGFwZSk7XG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoc2NhbGVUZXhTaGFwZSwgcHJlZmVycmVkU2NhbGVUZXhTaGFwZSkpIHtcbiAgICAgICAgc2NhbGUgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHNjYWxlLCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlKTtcbiAgICAgICAgc2NhbGVUZXhTaGFwZSA9IHByZWZlcnJlZFNjYWxlVGV4U2hhcGU7XG4gICAgICAgIGNsZWFudXBTY2FsZSA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbGV0IG9mZnNldFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdfG51bGwgPSBudWxsO1xuICAgIGxldCBjbGVhbnVwT2Zmc2V0ID0gZmFsc2U7XG4gICAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgICBjb25zdCBwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgICAgb2Zmc2V0LnJhbmsgPT09IDEgPyBbMSwgb2Zmc2V0LnNpemVdIDogeFRleFNoYXBlO1xuXG4gICAgICBvZmZzZXRUZXhTaGFwZSA9IG9mZnNldC5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZSk7XG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwob2Zmc2V0VGV4U2hhcGUsIHByZWZlcnJlZE9mZnNldFRleFNoYXBlKSkge1xuICAgICAgICBvZmZzZXQgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKG9mZnNldCwgcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGUpO1xuICAgICAgICBvZmZzZXRUZXhTaGFwZSA9IHByZWZlcnJlZE9mZnNldFRleFNoYXBlO1xuICAgICAgICBjbGVhbnVwT2Zmc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtCQVRDSE5PUk1fUFJPR31fJHt4VGV4U2hhcGV9XyR7bWVhblRleFNoYXBlfV8ke3ZhcmlhbmNlVGV4U2hhcGV9X2AgK1xuICAgICAgICAgICAgYCR7c2NhbGVUZXhTaGFwZSF9XyR7b2Zmc2V0VGV4U2hhcGUhfV8ke3ZhcmlhbmNlRXBzaWxvbn1gLFxuICAgICAgICAoKSA9PiBiYXRjaG5vcm1fZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgeFRleFNoYXBlLCBtZWFuVGV4U2hhcGUsIHZhcmlhbmNlVGV4U2hhcGUsIG9mZnNldFRleFNoYXBlLFxuICAgICAgICAgICAgc2NhbGVUZXhTaGFwZSwgdmFyaWFuY2VFcHNpbG9uKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBiYXRjaG5vcm1fZ3B1LmJhdGNoTm9ybWFsaXphdGlvbihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHhUZXhTaGFwZSwgbWVhbi5nZXRUZXh0dXJlKCksXG4gICAgICAgIG1lYW5UZXhTaGFwZSwgdmFyaWFuY2UuZ2V0VGV4dHVyZSgpLCB2YXJpYW5jZVRleFNoYXBlLFxuICAgICAgICBvZmZzZXQgIT0gbnVsbCA/IG9mZnNldC5nZXRUZXh0dXJlKCkgOiBudWxsLFxuICAgICAgICBvZmZzZXQgIT0gbnVsbCA/IG9mZnNldFRleFNoYXBlIDogbnVsbCxcbiAgICAgICAgc2NhbGUgIT0gbnVsbCA/IHNjYWxlLmdldFRleHR1cmUoKSA6IG51bGwsXG4gICAgICAgIHNjYWxlICE9IG51bGwgPyBzY2FsZVRleFNoYXBlIDogbnVsbCwgcmVzdWx0VGV4dHVyZSwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBNZWFuKSB7XG4gICAgICBtZWFuLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBWYXJpYW5jZSkge1xuICAgICAgdmFyaWFuY2UuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFNjYWxlKSB7XG4gICAgICBzY2FsZSEuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cE9mZnNldCkge1xuICAgICAgb2Zmc2V0IS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgeC5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN3aXRjaERpbUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBuZXdEaW06IG51bWJlcltdKTogVCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdOb3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke1NVTV9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiByZWR1Y2VzdW1fZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICByZWR1Y2VzdW1fZ3B1LnJlZHVjZVN1bShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNSU5fUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWlubWF4X2dwdS5nZXRBcmdNaW5GcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWlubWF4X2dwdS5hcmdNaW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7QVJHTUFYX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IGFyZ21pbm1heF9ncHUuZ2V0QXJnTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIGFyZ21pbm1heF9ncHUuYXJnTWluTWF4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2gsIGRvIGEgcGh5c2ljYWwgcmVzaGFwZSBzbyB0aGV5IGRvLlxuICAgIGNvbnN0IGFjdHVhbFgxVGV4U2hhcGUgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGFjdHVhbFgyVGV4U2hhcGUgPSB4Mi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGxldCBjbGVhbnVwWDIgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDFUZXhTaGFwZSwgYWN0dWFsWDJUZXhTaGFwZSkpIHtcbiAgICAgIHgyID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4MiwgYWN0dWFsWDFUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWDIgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0geDEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNQVhfRVFVQUxTX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IGFyZ21heGVxdWFsc19ncHUuZ2V0QXJnTWF4RXF1YWxzRnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICBudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWF4ZXF1YWxzX2dwdS5hcmdNYXhFcXVhbHMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHgxLmdldFRleHR1cmUoKSwgeDIuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLFxuICAgICAgICBudW1Db2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIGlmIChjbGVhbnVwWDIpIHtcbiAgICAgIHgyLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH0ge1xuICAgIHRocm93IG5ldyBFcnJvcigndG9wSyBHUFUgbm90IHlldCBpbXBsZW1lbnRlZCEnKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNSU5fUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbWlubWF4X2dwdS5nZXRNaW5GcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgbWlubWF4X2dwdS5taW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7TUFYX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IG1pbm1heF9ncHUuZ2V0TWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIG1pbm1heF9ncHUubWluTWF4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRpdmlkZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcvJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6XG4gICAgICBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgICAgICAgICBjLCBhLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5TQ0FMQVIsICcvJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFycmF5RGl2aWRlZEJ5U2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6XG4gICAgICBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgICAgICAgICBhLCBjLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcvJywgT3BlcmFuZFR5cGUuU0NBTEFSKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICcrJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1YkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuYWRkU3ViTXVsRGl2KFxuICAgICAgICBhLCBiLCBhLnNoYXBlLCBPcGVyYW5kVHlwZS5NQVRSSVgsICctJywgT3BlcmFuZFR5cGUuTUFUUklYKSBhcyBUO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtMT0dTVU1FWFBfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbG9nc3VtZXhwX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHQgPVxuICAgICAgICBuZXcgU2NhbGFyKHt0ZXh0dXJlOiB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSl9KTtcblxuICAgIHJlZHVjZXN1bV9ncHUucmVkdWNlU3VtKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0LmdldFRleHR1cmUoKSk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGV4cEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIEVYUF9QUk9HLCAoKSA9PiBleHBfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgZXhwX2dwdS5leHAoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgTE9HX1BST0csICgpID0+IGxvZ19ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG4gICAgbG9nX2dwdS5sb2coXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVsdUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFJFTFVfUFJPRywgKCkgPT4gcmVsdV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICByZWx1X2dwdS5yZWx1KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBTSUdNT0lEX1BST0csICgpID0+IHNpZ21vaWRfZ3B1LmdldFNpZ21vaWRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHNpZ21vaWRfZ3B1LnNpZ21vaWQoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFRBTkhfUFJPRywgKCkgPT4gdHJpZ19ncHUuZ2V0VGFuaEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgdHJpZ19ncHUudGFuaChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzaW5JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBTSU5fUFJPRywgKCkgPT4gdHJpZ19ncHUuZ2V0U2luRnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICB0cmlnX2dwdS5zaW4oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RlcEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNURVBfUFJPRywgKCkgPT4gc3RlcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBzdGVwX2dwdS5zdGVwKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBpbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgcHJvZ0tleSA9IFtcbiAgICAgIENPTlYyRF9QUk9HLCB4LnNoYXBlLCBvdXRwdXREZXB0aCwgZmllbGRTaXplLCBzdHJpZGUsIGJpYXNlcyAhPSBudWxsXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0ocHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIG91dHB1dERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSwgemVyb1BhZCwgYmlhc2VzICE9IG51bGwpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCB3VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgICBjb25zdCBiaWFzVGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICBjb25zdCBhY3R1YWxCVGV4U2hhcGUgPSBiaWFzZXMuZ2V0VGV4dHVyZVNoYXBlUkMoYmlhc1RleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxCVGV4U2hhcGUsIGJpYXNUZXhTaGFwZSkpIHtcbiAgICAgICAgYmlhc2VzID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiaWFzZXMsIGJpYXNUZXhTaGFwZSk7XG4gICAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9ncHUuY29udm9sdmUoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCB3ZWlnaHRzLmdldFRleHR1cmUoKSxcbiAgICAgICAgYmlhc2VzICE9IG51bGwgPyBiaWFzZXMuZ2V0VGV4dHVyZSgpIDogbnVsbCwgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFcpIHtcbiAgICAgIHdlaWdodHMuZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cEIgJiYgYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGJpYXNlcy5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZEJhY2tQcm9wSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBkeTogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IHtkeDogQXJyYXkzRCwgZHc6IEFycmF5NEQsIGRiOiBBcnJheTFEfSB7XG4gICAgY29uc3QgZlNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgY29uc3QgeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChkeS5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoeFRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWFRleFNoYXBlLCB4VGV4U2hhcGUpKSB7XG4gICAgICB4ID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4LCB4VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFggPSB0cnVlO1xuICAgIH1cblxuICAgIGxldCBjbGVhbnVwVyA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFdUZXhTaGFwZSA9IHdlaWdodHMuZ2V0VGV4dHVyZVNoYXBlUkMod1RleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsV1RleFNoYXBlLCB3VGV4U2hhcGUpKSB7XG4gICAgICB3ZWlnaHRzID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh3ZWlnaHRzLCB3VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFcgPSB0cnVlO1xuICAgIH1cblxuICAgIGxldCBjbGVhbnVwWSA9IGZhbHNlO1xuICAgIGNvbnN0IGFjdHVhbFlUZXhTaGFwZSA9IGR5LmdldFRleHR1cmVTaGFwZVJDKHlUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFlUZXhTaGFwZSwgeVRleFNoYXBlKSkge1xuICAgICAgZHkgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGR5LCB5VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFkgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGR3ID0gdGhpcy5jb252MmREZXJXZWlnaHRzKHgsIGR5LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IGRiID0gdGhpcy5jb252MmREZXJCaWFzKGR5KTtcbiAgICBjb25zdCBkeCA9IHRoaXMuY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoXG4gICAgICAgIGR5LCB3ZWlnaHRzLCBudWxsIC8qKiBiaWFzZXMgKi8sIHN0cmlkZSwgcGFkKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZHkuZGlzcG9zZSgpO1xuICAgIH1cbiAgICByZXR1cm4ge2R4LCBkYiwgZHd9O1xuICB9XG5cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvcmlnT3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IGZpZWxkU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG5cbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX1RSQU5TUE9TRV9QUk9HLCB4LnNoYXBlLCBmaWVsZFNpemUsIG9yaWdJbnB1dERlcHRoLCBvcmlnU3RyaWRlLFxuICAgICAgb3JpZ1BhZCwgYmlhc2VzICE9IG51bGxcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZpZWxkU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIG9yaWdTdHJpZGUsIG9yaWdQYWQsXG4gICAgICAgICAgYmlhc2VzICE9IG51bGwpO1xuICAgIH0pO1xuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCB3VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShcbiAgICAgICAgb3JpZ0lucHV0RGVwdGgsIG9yaWdPdXRwdXREZXB0aCwgZmllbGRTaXplKTtcbiAgICBjb25zdCBiaWFzVGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG9yaWdJbnB1dERlcHRoKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG4gICAgaWYgKGJpYXNlcyAhPSBudWxsKSB7XG4gICAgICBjb25zdCBhY3R1YWxCaWFzVGV4U2hhcGUgPSBiaWFzZXMuZ2V0VGV4dHVyZVNoYXBlUkMoYmlhc1RleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxCaWFzVGV4U2hhcGUsIGJpYXNUZXhTaGFwZSkpIHtcbiAgICAgICAgYmlhc2VzID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiaWFzZXMsIGJpYXNUZXhTaGFwZSk7XG4gICAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBGaWd1cmUgb3V0IHRoZSBvdXRwdXQgc2hhcGUgYnkgZGlsYXRpbmcgdGhlIGlucHV0LlxuICAgIGNvbnN0IGRpbGF0ZWRSQyA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlRGlsYXRlZFJDKFt4LnNoYXBlWzBdLCB4LnNoYXBlWzFdXSwgb3JpZ1N0cmlkZSk7XG4gICAgY29uc3QgcGFkID0gZmllbGRTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgcmVzdWx0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFtkaWxhdGVkUkNbMF0sIGRpbGF0ZWRSQ1sxXSwgb3JpZ091dHB1dERlcHRoXSwgZmllbGRTaXplLFxuICAgICAgICBvcmlnSW5wdXREZXB0aCwgMSwgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuY29udlRyYW5zcG9zZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHdlaWdodHMuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXRUZXh0dXJlKCkgOiBudWxsLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwQikge1xuICAgICAgYmlhc2VzIS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTNEPihcbiAgICAgICAgcmVzdWx0U2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgY29udjJkRGVyV2VpZ2h0cyhcbiAgICAgIHg6IEFycmF5M0QsIGRZOiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5NEQge1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB4LnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgcHJvZ0tleSA9IFtcbiAgICAgIENPTlYyRF9ERVJXX1BST0csIHguc2hhcGUsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0ocHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGNvbnZfYmFja3Byb3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyRGVyV2VpZ2h0c1NvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHlTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZlNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpO1xuICAgIGNvbnN0IHlUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeVNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZFkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkWSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZFksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuZGVyV2VpZ2h0cyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIGRZLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4LFxuICAgICAgICByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFkpIHtcbiAgICAgIGRZLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBjb25zdCB3ZWlnaHRzU2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNTaGFwZTREKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxBcnJheTREPihcbiAgICAgICAgd2VpZ2h0c1NoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIGNvbnYyZERlckJpYXMoZFk6IEFycmF5M0QpOiBBcnJheTFEIHtcbiAgICBjb25zdCBvdXRwdXREZXB0aCA9IGRZLnNoYXBlWzJdO1xuICAgIGNvbnN0IHByb2dLZXkgPSBbQ09OVjJEX0RFUkJfUFJPRywgZFkuc2hhcGVdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJEZXJCaWFzU291cmNlKGRZLnNoYXBlKTtcbiAgICB9KTtcbiAgICBjb25zdCB5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGRZLnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZFkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkWSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZFksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoKTtcbiAgICBjb25zdCByZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbnZfYmFja3Byb3BfZ3B1LmRlckJpYXMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGRZLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFkpIHtcbiAgICAgIGRZLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5MUQ+KFxuICAgICAgICBbb3V0cHV0RGVwdGhdLCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIHByaXZhdGUgcG9vbChcbiAgICAgIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbFhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoeFRleFNoYXBlKTtcbiAgICBsZXQgY2xlYW51cFggPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWFRleFNoYXBlLCB4VGV4U2hhcGUpKSB7XG4gICAgICB4ID0gdGhpcy5yZXNoYXBlVGV4dHVyZSh4LCB4VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cFggPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKHguc2hhcGUsIGZTaXplLCB4LnNoYXBlWzJdLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHJlc3VsdFNoYXBlKTtcbiAgICBjb25zdCBwb29sUmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBwb29sX2dwdS5wb29sQ29tbW9uKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgcG9vbFJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHBvb2xSZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heFBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG1heFBvb2xQcm9nS2V5ID1cbiAgICAgICAgW01BWF9QT09MX1BST0csIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZF0uam9pbignXycpO1xuICAgIGNvbnN0IG1heFBvb2xQcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShtYXhQb29sUHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIG1heF9wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlck1heFBvb2xTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLnBvb2wobWF4UG9vbFByb2dyYW0sIHgsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWluUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWluUG9vbFByb2dLZXkgPVxuICAgICAgICBbTUlOX1BPT0xfUFJPRywgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWluUG9vbFByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1pblBvb2xQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWluX3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWluUG9vbFNvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMucG9vbChtaW5Qb29sUHJvZ3JhbSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhdmdQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBhdmdQb29sUHJvZ0tleSA9XG4gICAgICAgIFtBVkdfUE9PTF9QUk9HLCB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWRdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBhdmdQb29sUHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oYXZnUG9vbFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBhdmdfcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJBdmdQb29sU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5wb29sKGF2Z1Bvb2xQcm9ncmFtLCB4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heFBvb2xCYWNrcHJvcEludGVybmFsKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG1heFBvb2xQb3NpdGlvbnNQcm9nS2V5ID0gW1xuICAgICAgTUFYX1BPT0xfUE9TSVRJT05TX1BST0csIHguc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkXG4gICAgXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Byb2dyYW0gPVxuICAgICAgICB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xQb3NpdGlvbnNQcm9nS2V5LCAoKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIG1heF9wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlck1heFBvb2xQb3NpdGlvbnNTb3VyY2UoXG4gICAgICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICAgICAgfSk7XG5cbiAgICBjb25zdCBtYXhQb29sUmVzdWx0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIHguc2hhcGUsIGZTaXplLCB4LnNoYXBlWzJdLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICBjb25zdCBtYXhQb29sUmVzdWx0VGV4U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKG1heFBvb2xSZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCA9XG4gICAgICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUobWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbWF4X3Bvb2xfZ3B1Lm1heFBvb2xDb21tb24oXG4gICAgICAgIHRoaXMuZ3BncHUsIG1heFBvb2xQb3NpdGlvbnNQcm9ncmFtLCB4LmdldFRleHR1cmUoKSxcbiAgICAgICAgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCwgbWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbnN0IG1heFBvb2xCYWNrcHJvcFByb2dLZXkgPSBbXG4gICAgICBNQVhfUE9PTF9CQUNLUFJPUF9QUk9HLCBkeS5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShtYXhQb29sQmFja3Byb3BQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWF4X3Bvb2xfYmFja3Byb3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbEJhY2twcm9wKFxuICAgICAgICAgIGR5LnNoYXBlLCBmU2l6ZSwgb3JpZ1N0cmlkZSwgb3JpZ1BhZCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBkeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChkeS5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbER5VGV4U2hhcGUgPSBkeS5nZXRUZXh0dXJlU2hhcGVSQyhkeVRleFNoYXBlKTtcbiAgICBsZXQgY2xlYW51cER5ID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbER5VGV4U2hhcGUsIGR5VGV4U2hhcGUpKSB7XG4gICAgICBkeSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZHksIGR5VGV4U2hhcGUpO1xuICAgICAgY2xlYW51cER5ID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCBkaWxhdGVkRHlSQyA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlRGlsYXRlZFJDKFtkeS5zaGFwZVswXSwgZHkuc2hhcGVbMV1dLCBvcmlnU3RyaWRlKTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IHJlc3VsdFNoYXBlUkNEID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbZGlsYXRlZER5UkNbMF0sIGRpbGF0ZWREeVJDWzFdLCBkeS5zaGFwZVsyXV0sIGZTaXplLCBkeS5zaGFwZVsyXSwgMSxcbiAgICAgICAgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGVSQ0QpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgbWF4X3Bvb2xfYmFja3Byb3BfZ3B1Lm1heFBvb2xCYWNrcHJvcChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgZHkuZ2V0VGV4dHVyZSgpLCBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4LFxuICAgICAgICByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwRHkpIHtcbiAgICAgIGR5LmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIucmVsZWFzZVRleHR1cmUoXG4gICAgICAgIG1heFBvb2xQb3NpdGlvbnNSZXN1bHRUZXgsIG1heFBvb2xSZXN1bHRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaXplQmlsaW5lYXIzREludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGFsaWduQ29ybmVyczogYm9vbGVhbik6IEFycmF5M0Qge1xuICAgIGNvbnN0IHByb2dyYW1LZXkgPVxuICAgICAgICBbUkVTSVpFX0JJTElORUFSX1BST0csIHguc2hhcGUsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVyc10uam9pbignXycpO1xuXG4gICAgY29uc3QgbmV3U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIFtuZXdTaGFwZTJEWzBdLCBuZXdTaGFwZTJEWzFdLCB4LnNoYXBlWzJdXTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QobmV3U2hhcGVSQ0QpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIHByb2dyYW1LZXksXG4gICAgICAgICgpID0+IHJlc2l6ZV9iaWxpbmVhcl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB4LnNoYXBlLCBuZXdTaGFwZTJELCBhbGlnbkNvcm5lcnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJlc2l6ZV9iaWxpbmVhcl9ncHUucmVzaXplQmlsaW5lYXIoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICBuZXdTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRBbmRTYXZlUHJvZ3JhbShwcm9ncmFtS2V5OiBzdHJpbmcsIGdldFNoYWRlclNvdXJjZTogKCkgPT4gc3RyaW5nKTpcbiAgICAgIFdlYkdMUHJvZ3JhbSB7XG4gICAgaWYgKCEocHJvZ3JhbUtleSBpbiB0aGlzLnByb2dyYW1DYWNoZSkpIHtcbiAgICAgIHRoaXMucHJvZ3JhbUNhY2hlW3Byb2dyYW1LZXldID1cbiAgICAgICAgICB0aGlzLmdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0U2hhZGVyU291cmNlKCkpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5wcm9ncmFtQ2FjaGVbcHJvZ3JhbUtleV07XG4gIH1cblxuICBwcml2YXRlIGFkZFN1Yk11bERpdihcbiAgICAgIGE6IE5EQXJyYXksIGI6IE5EQXJyYXksIHJlc3VsdFNoYXBlOiBudW1iZXJbXSxcbiAgICAgIG9wZXJhbmRBOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhbmRUeXBlLFxuICAgICAgb3BUeXBlOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhdGlvbixcbiAgICAgIG9wZXJhbmRCOiBhZGRzdWJtdWxkaXZfZ3B1Lk9wZXJhbmRUeXBlKTogTkRBcnJheSB7XG4gICAgbGV0IGNsZWFudXBCID0gZmFsc2U7XG5cbiAgICBjb25zdCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuICAgIGxldCBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuXG4gICAgbGV0IGxvZ2ljYWxCVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgICBpZiAob3BlcmFuZEEgPT09IE9wZXJhbmRUeXBlLk1BVFJJWCAmJiBvcGVyYW5kQiA9PT0gT3BlcmFuZFR5cGUuTUFUUklYKSB7XG4gICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUpO1xuXG4gICAgICBpZiAoYS5pbkdQVSgpKSB7XG4gICAgICAgIC8vIFByZWZlciBCIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEEuXG4gICAgICAgIGIuZ2V0VGV4dHVyZVNoYXBlUkMoYS5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIH0gZWxzZSBpZiAoYi5pbkdQVSgpKSB7XG4gICAgICAgIC8vIFByZWZlciBBIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEIuXG4gICAgICAgIGEuZ2V0VGV4dHVyZVNoYXBlUkMoYi5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYVRleFNoYXBlID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgY29uc3QgYlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IGJUZXhTaGFwZTtcblxuICAgICAgaWYgKGEucmFuayA9PT0gMSkge1xuICAgICAgICAvLyBXaGVuIGRlYWxpbmcgd2l0aCB2ZWN0b3JzLCB3ZSBjYW4gc2FtcGxlIGluIHRyYW5zcG9zZWQgd2F5IHdpdGhvdXRcbiAgICAgICAgLy8gdGhlIG5lZWQgdG8gZG8gcGh5c2ljYWwgcmVzaGFwZS5cbiAgICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGJUZXhTaGFwZSwgYVRleFNoYXBlKSkge1xuICAgICAgICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlRSQU5TUE9TRUQ7XG4gICAgICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IFtiVGV4U2hhcGVbMV0sIGJUZXhTaGFwZVswXV07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFUZXhTaGFwZSwgbG9naWNhbEJUZXhTaGFwZSkpIHtcbiAgICAgICAgYiA9IHRoaXMucmVzaGFwZVRleHR1cmUoYiwgYVRleFNoYXBlKTtcbiAgICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUjtcbiAgICAgICAgbG9naWNhbEJUZXhTaGFwZSA9IGIuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBsb2dpY2FsQlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIH1cblxuICAgIGNvbnN0IGFUZXhTaGFwZSA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBiVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG5cbiAgICBjb25zdCBwcm9ncmFtS2V5ID0gW1xuICAgICAgQUREX1NVTV9NVUxfRElWX1BST0csIG9wZXJhbmRBLCBhT3JpZW50YXRpb24sIG9wVHlwZSwgb3BlcmFuZEIsXG4gICAgICBiT3JpZW50YXRpb25cbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgcHJvZ3JhbUtleSxcbiAgICAgICAgKCkgPT4gYWRkc3VibXVsZGl2X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIG9wZXJhbmRBLCBhT3JpZW50YXRpb24sIG9wVHlwZSwgb3BlcmFuZEIsIGJPcmllbnRhdGlvbikpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID0gW1xuICAgICAgTWF0aC5tYXgoYVRleFNoYXBlWzBdLCBsb2dpY2FsQlRleFNoYXBlWzBdKSxcbiAgICAgIE1hdGgubWF4KGFUZXhTaGFwZVsxXSwgbG9naWNhbEJUZXhTaGFwZVsxXSlcbiAgICBdO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9XG4gICAgICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4dHVyZVNoYXBlKTtcblxuICAgIGFkZHN1Ym11bGRpdl9ncHUuYWRkU3ViTXVsRGl2KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYVRleFNoYXBlLCBiLmdldFRleHR1cmUoKSxcbiAgICAgICAgYlRleFNoYXBlLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXh0dXJlU2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlKFxuICAgICAgICByZXN1bHRTaGFwZSxcbiAgICAgICAge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXh0dXJlU2hhcGV9KTtcbiAgfVxuXG4gIHByaXZhdGUgZG9HUFVTaGFwZXNNYXRjaChhOiBOREFycmF5LCBiOiBOREFycmF5KTogYm9vbGVhbiB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlKTtcbiAgICBpZiAoYS5pbkdQVSgpKSB7XG4gICAgICAvLyBQcmVmZXIgQiB0byBoYXZlIHRoZSBzaGFwZSBvZiBBLlxuICAgICAgYi5nZXRUZXh0dXJlU2hhcGVSQyhhLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgIH0gZWxzZSBpZiAoYi5pbkdQVSgpKSB7XG4gICAgICAvLyBQcmVmZXIgQSB0byBoYXZlIHRoZSBzaGFwZSBvZiBCLlxuICAgICAgYS5nZXRUZXh0dXJlU2hhcGVSQyhiLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgIH1cbiAgICByZXR1cm4gdXRpbC5hcnJheXNFcXVhbChhLmdldFRleHR1cmVTaGFwZVJDKCksIGIuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gIH1cblxuICBnZXRUZXh0dXJlTWFuYWdlcigpOiBUZXh0dXJlTWFuYWdlciB7XG4gICAgcmV0dXJuIHRoaXMudGV4dHVyZU1hbmFnZXI7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGZvciAoY29uc3QgcHJvZ3JhbUtleSBpbiB0aGlzLnByb2dyYW1DYWNoZSkge1xuICAgICAgaWYgKHRoaXMucHJvZ3JhbUNhY2hlLmhhc093blByb3BlcnR5KHByb2dyYW1LZXkpKSB7XG4gICAgICAgIHRoaXMuZ3BncHUuZGVsZXRlUHJvZ3JhbSh0aGlzLnByb2dyYW1DYWNoZVtwcm9ncmFtS2V5XSk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMudGV4dHVyZU1hbmFnZXIuZGlzcG9zZSgpO1xuXG4gICAgaWYgKHRoaXMuZ3BncHVDcmVhdGVkTG9jYWxseSkge1xuICAgICAgdGhpcy5ncGdwdS5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtUZXh0dXJlTWFuYWdlcn0gZnJvbSAnLi93ZWJnbC90ZXh0dXJlX21hbmFnZXInO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsL3dlYmdsX3V0aWwnO1xuXG4vLyBUaGVzZSBnbG9iYWwgdmFyaWFibGVzIG5lZWQgdG8gYmUgaW5pdGlhbGl6ZWQgdG8gbnVsbCBzbyB0aGF0IGNsb3N1cmUga25vd3Ncbi8vIG5vdCB0byBzZWFsIHRoZW0uXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGxldCBHUEdQVTogR1BHUFVDb250ZXh0ID0gbnVsbCE7XG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGxldCBURVhUVVJFX01BTkFHRVI6IFRleHR1cmVNYW5hZ2VyID0gbnVsbCE7XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgaW50ZXJmYWNlIE5EQXJyYXlEYXRhIHtcbiAgdmFsdWVzPzogRmxvYXQzMkFycmF5O1xuICB0ZXh0dXJlPzogV2ViR0xUZXh0dXJlO1xuICAvKiogW3Jvd3MsIGNvbHVtbnNdIHNoYXBlIG9mIHRoZSB0ZXh0dXJlLiAqL1xuICB0ZXh0dXJlU2hhcGVSQz86IFtudW1iZXIsIG51bWJlcl07XG59XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgZnVuY3Rpb24gaW5pdGlhbGl6ZUdQVShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCB0ZXh0dXJlTWFuYWdlcjogVGV4dHVyZU1hbmFnZXIpIHtcbiAgR1BHUFUgPSBncGdwdTtcbiAgVEVYVFVSRV9NQU5BR0VSID0gdGV4dHVyZU1hbmFnZXI7XG59XG5cbmZ1bmN0aW9uIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpIHtcbiAgaWYgKEdQR1BVID09IG51bGwgfHwgVEVYVFVSRV9NQU5BR0VSID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0dQVSBub3QgaW50aWFsaXplZC4nKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheSB7XG4gIC8qKiBUaGUgc2hhcGUgb2YgdGhlIG5kYXJyYXkuICovXG4gIHNoYXBlOiBudW1iZXJbXTtcbiAgLyoqIE51bWJlciBvZiBlbGVtZW50cyBpbiB0aGUgbmRhcnJheS4gKi9cbiAgc2l6ZTogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBOdW1iZXIgb2YgZWxlbWVudHMgdG8gc2tpcCBpbiBlYWNoIGRpbWVuc2lvbiB3aGVuIGluZGV4aW5nLiBTZWVcbiAgICogaHR0cHM6Ly9kb2NzLnNjaXB5Lm9yZy9kb2MvbnVtcHkvcmVmZXJlbmNlL2dlbmVyYXRlZC9udW1weS5uZGFycmF5LnN0cmlkZXMuaHRtbFxuICAgKi9cbiAgcHJvdGVjdGVkIHN0cmlkZXM6IG51bWJlcltdO1xuXG4gIHByaXZhdGUgZGF0YTogTkRBcnJheURhdGE7XG5cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKHNoYXBlOiBudW1iZXJbXSwgZGF0YTogTkRBcnJheURhdGEpIHtcbiAgICAvLyBTYW5pdHkgY2hlY2tzLlxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkYXRhLnZhbHVlcyAhPSBudWxsIHx8IGRhdGEudGV4dHVyZSAhPSBudWxsLFxuICAgICAgICAnRWl0aGVyIGB2YWx1ZXNgIG9yIGB0ZXh0dXJlYCBtdXN0IGJlIGRlZmluZWQnKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBkYXRhLnRleHR1cmUgPT0gbnVsbCB8fCAoZGF0YS50ZXh0dXJlU2hhcGVSQyAhPSBudWxsKSxcbiAgICAgICAgJ2B0ZXh0dXJlU2hhcGVgIG11c3QgYmUgZGVmaW5lZCB3aGVuIGB0ZXh0dXJlYCBpcyBkZWZpbmVkJyk7XG5cbiAgICB0aGlzLnNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpO1xuXG4gICAgaWYgKGRhdGEudmFsdWVzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMuc2l6ZSA9PT0gZGF0YS52YWx1ZXMubGVuZ3RoLFxuICAgICAgICAgICdDb25zdHJ1Y3RpbmcgbmRhcnJheSBvZiBzaGFwZSAoJyArIHRoaXMuc2l6ZSArICcpIHNob3VsZCBtYXRjaCB0aGUnICtcbiAgICAgICAgICAgICAgJyBsZW5ndGggb2YgdmFsdWVzICgnICsgZGF0YS52YWx1ZXMubGVuZ3RoICsgJyknKTtcbiAgICB9XG5cbiAgICB0aGlzLnNoYXBlID0gc2hhcGU7XG4gICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICBjb25zdCBkaW0gPSB0aGlzLnNoYXBlLmxlbmd0aDtcblxuICAgIGlmIChkaW0gPCAyKSB7XG4gICAgICB0aGlzLnN0cmlkZXMgPSBbXTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTGFzdCBkaW1lbnNpb24gaGFzIGltcGxpY2l0IHN0cmlkZSBvZiAxLCB0aHVzIGhhdmluZyBELTEgKGluc3RlYWQgb2YgRClcbiAgICAgIC8vIHN0cmlkZXMuXG4gICAgICB0aGlzLnN0cmlkZXMgPSBuZXcgQXJyYXkoZGltIC0gMSk7XG4gICAgICB0aGlzLnN0cmlkZXNbZGltIC0gMl0gPSB0aGlzLnNoYXBlW2RpbSAtIDFdO1xuICAgICAgZm9yIChsZXQgaSA9IGRpbSAtIDM7IGkgPj0gMDsgLS1pKSB7XG4gICAgICAgIHRoaXMuc3RyaWRlc1tpXSA9IHRoaXMuc3RyaWRlc1tpICsgMV0gKiB0aGlzLnNoYXBlW2kgKyAxXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgb2YgemVyb3Mgd2l0aCB0aGUgc3BlY2lmaWVkIHNoYXBlLiAqL1xuICBzdGF0aWMgemVyb3M8VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSk6IFQge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodXRpbC5zaXplRnJvbVNoYXBlKHNoYXBlKSk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihzaGFwZSwge3ZhbHVlc30pO1xuICB9XG5cbiAgLyoqIENyZWF0ZXMgYSBuZGFycmF5IG9mIHplcm9zIHdpdGggdGhlIHNhbWUgc2hhcGUgYXMgdGhlIHNwZWNpZmllZCBuZGFycmF5LlxuICAgKi9cbiAgc3RhdGljIHplcm9zTGlrZTxUIGV4dGVuZHMgTkRBcnJheT4oYW5vdGhlcjogVCk6IFQge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zKGFub3RoZXIuc2hhcGUpIGFzIFQ7XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgd2l0aCB0aGUgc2FtZSB2YWx1ZXMvc2hhcGUgYXMgdGhlIHNwZWNpZmllZCBuZGFycmF5LiAqL1xuICBzdGF0aWMgbGlrZTxUIGV4dGVuZHMgTkRBcnJheT4oYW5vdGhlcjogVCk6IFQge1xuICAgIGNvbnN0IHZhbHVlcyA9IGFub3RoZXIuZ2V0VmFsdWVzKCk7XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhbm90aGVyLnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYWtlcyBhIG5ldyBuZGFycmF5IHdpdGggdGhlIHByb3ZpZGVkIHNoYXBlIGFuZCB2YWx1ZXMuIFZhbHVlcyBzaG91bGQgYmUgaW5cbiAgICogYSBmbGF0IGFycmF5LlxuICAgKi9cbiAgc3RhdGljIG1ha2U8VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSwgZGF0YTogTkRBcnJheURhdGEpOiBUIHtcbiAgICBzd2l0Y2ggKHNoYXBlLmxlbmd0aCkge1xuICAgICAgY2FzZSAwOlxuICAgICAgICByZXR1cm4gbmV3IFNjYWxhcihkYXRhKSBhcyBUO1xuICAgICAgY2FzZSAxOlxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXkxRChkYXRhKSBhcyBhbnk7XG4gICAgICBjYXNlIDI6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTJEKHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgMzpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IEFycmF5M0Qoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhKSBhcyBhbnk7XG4gICAgICBjYXNlIDQ6XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXk0RChcbiAgICAgICAgICAgICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgICAgICAgICAgICAgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBOREFycmF5KHNoYXBlLCBkYXRhKSBhcyBhbnk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFJlc2hhcGVzIHRoZSBjdXJyZW50IG5kYXJyYXkgaW50byB0aGUgcHJvdmlkZWQgc2hhcGUuICovXG4gIHJlc2hhcGU8VCBleHRlbmRzIE5EQXJyYXk+KG5ld1NoYXBlOiBudW1iZXJbXSk6IFQge1xuICAgIGlmICh1dGlsLmFycmF5c0VxdWFsKHRoaXMuc2hhcGUsIG5ld1NoYXBlKSkge1xuICAgICAgLy8gTm8tb3AuXG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICByZXR1cm4gdGhpcyBhcyBhbnk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuc2l6ZSA9PT0gdXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKSxcbiAgICAgICAgJ25ldyBzaGFwZSBhbmQgb2xkIHNoYXBlIG11c3QgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2YgZWxlbWVudHMuJyk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5ld1NoYXBlLCB0aGlzLmRhdGEpO1xuICB9XG5cbiAgYXNTY2FsYXIoKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydCh0aGlzLnNpemUgPT09IDEsICdUaGUgYXJyYXkgbXVzdCBoYXZlIG9ubHkgMSBlbGVtZW50LicpO1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8U2NhbGFyPihbXSk7XG4gIH1cblxuICBhczFEKCk6IEFycmF5MUQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkxRD4oW3RoaXMuc2l6ZV0pO1xuICB9XG5cbiAgYXMyRChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEFycmF5MkQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkyRD4oW3Jvd3MsIGNvbHVtbnNdKTtcbiAgfVxuXG4gIGFzM0Qocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGRlcHRoOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5yZXNoYXBlPEFycmF5M0Q+KFtyb3dzLCBjb2x1bW5zLCBkZXB0aF0pO1xuICB9XG5cbiAgYXM0RChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgZGVwdGg6IG51bWJlciwgZGVwdGgyOiBudW1iZXIpOiBBcnJheTREIHtcbiAgICByZXR1cm4gdGhpcy5yZXNoYXBlPEFycmF5NEQ+KFtyb3dzLCBjb2x1bW5zLCBkZXB0aCwgZGVwdGgyXSk7XG4gIH1cblxuICBnZXQgcmFuaygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnNoYXBlLmxlbmd0aDtcbiAgfVxuXG4gIGdldCguLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIGxldCBpbmRleCA9IGxvY3NbbG9jcy5sZW5ndGggLSAxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBpbmRleCArPSB0aGlzLnN0cmlkZXNbaV0gKiBsb2NzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVtpbmRleF07XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgLi4ubG9jczogbnVtYmVyW10pIHtcbiAgICB0aGlzLnNldCh0aGlzLmdldCguLi5sb2NzKSArIHZhbHVlLCAuLi5sb2NzKTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCAuLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIGxldCBpbmRleCA9IGxvY3NbbG9jcy5sZW5ndGggLSAxXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBpbmRleCArPSB0aGlzLnN0cmlkZXNbaV0gKiBsb2NzW2ldO1xuICAgIH1cbiAgICB0aGlzLmdldFZhbHVlcygpW2luZGV4XSA9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBudW1iZXJbXSk6IG51bWJlciB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHJldHVybiBpbmRleDtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IG51bWJlcltdIHtcbiAgICBjb25zdCBsb2NzOiBudW1iZXJbXSA9IG5ldyBBcnJheSh0aGlzLnNoYXBlLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsb2NzLmxlbmd0aCAtIDE7ICsraSkge1xuICAgICAgbG9jc1tpXSA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZXNbaV0pO1xuICAgICAgaW5kZXggLT0gbG9jc1tpXSAqIHRoaXMuc3RyaWRlc1tpXTtcbiAgICB9XG4gICAgbG9jc1tsb2NzLmxlbmd0aCAtIDFdID0gaW5kZXg7XG4gICAgcmV0dXJuIGxvY3M7XG4gIH1cblxuICBmaWxsKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpLmZpbGwodmFsdWUpO1xuICB9XG5cbiAgZ2V0RGF0YSgpOiBOREFycmF5RGF0YSB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YTtcbiAgfVxuXG4gIGdldFZhbHVlcygpOiBGbG9hdDMyQXJyYXkge1xuICAgIGlmICh0aGlzLmRhdGEudmFsdWVzID09IG51bGwpIHtcbiAgICAgIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpO1xuICAgICAgdGhpcy5kYXRhLnZhbHVlcyA9IEdQR1BVLmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUhLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMhWzBdLFxuICAgICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyFbMV0pO1xuICAgICAgdGhpcy5kaXNwb3NlVGV4dHVyZSgpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kYXRhLnZhbHVlcztcbiAgfVxuXG4gIHByaXZhdGUgdXBsb2FkVG9HUFUocHJlZmVycmVkVGV4U2hhcGU/OiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDID0gd2ViZ2xfdXRpbC5nZXRUZXh0dXJlU2hhcGVGcm9tTG9naWNhbFNoYXBlKFxuICAgICAgICBHUEdQVS5nbCwgdGhpcy5zaGFwZSwgcHJlZmVycmVkVGV4U2hhcGUpO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlID1cbiAgICAgICAgVEVYVFVSRV9NQU5BR0VSLmFjcXVpcmVUZXh0dXJlKHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBHUEdQVS51cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQ1sxXSwgdGhpcy5kYXRhLnZhbHVlcyEpO1xuXG4gICAgdGhpcy5kYXRhLnZhbHVlcyA9IG51bGwhO1xuICB9XG5cbiAgZ2V0VGV4dHVyZShwcmVmZXJyZWRTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXSk6IFdlYkdMVGV4dHVyZSB7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlID09IG51bGwpIHtcbiAgICAgIHRoaXMudXBsb2FkVG9HUFUocHJlZmVycmVkU2hhcGVSQyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRhdGEudGV4dHVyZSE7XG4gIH1cblxuICBnZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICAgIGlmICh0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMgPT0gbnVsbCkge1xuICAgICAgdGhpcy51cGxvYWRUb0dQVShwcmVmZXJyZWRTaGFwZVJDKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyE7XG4gIH1cblxuICBkaXNwb3NlKCk6IHZvaWQge1xuICAgIHRoaXMuZGF0YS52YWx1ZXMgPSBudWxsITtcbiAgICB0aGlzLnNoYXBlID0gbnVsbCE7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZGlzcG9zZVRleHR1cmUoKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGRpc3Bvc2VUZXh0dXJlKCkge1xuICAgIHRocm93SWZHUFVOb3RJbml0aWFsaXplZCgpO1xuICAgIFRFWFRVUkVfTUFOQUdFUi5yZWxlYXNlVGV4dHVyZShcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUhLCB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMhKTtcbiAgICB0aGlzLmRhdGEudGV4dHVyZSA9IG51bGwhO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyA9IG51bGwhO1xuICB9XG5cbiAgaW5HUFUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlICE9IG51bGw7XG4gIH1cblxuICBlcXVhbHModDogTkRBcnJheSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB1dGlsLmFycmF5c0VxdWFsKHRoaXMuc2hhcGUsIHQuc2hhcGUpICYmXG4gICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5nZXRWYWx1ZXMoKSwgdC5nZXRWYWx1ZXMoKSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZDxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCByYW5kRnVuY3Rpb246ICgpID0+IG51bWJlcik6XG4gICAgICBUIHtcbiAgICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHNoYXBlKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHNpemUpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2l6ZTsgaSsrKSB7XG4gICAgICB2YWx1ZXNbaV0gPSByYW5kRnVuY3Rpb24oKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KHNoYXBlLCB7dmFsdWVzfSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZE5vcm1hbDxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCBtZWFuID0gMCwgc3RkRGV2ID0gMSkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmQ8VD4oc2hhcGUsICgpID0+IHV0aWwucmFuZEdhdXNzKG1lYW4sIHN0ZERldikpO1xuICB9XG5cbiAgc3RhdGljIHJhbmRUcnVuY2F0ZWROb3JtYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgc2hhcGU6IG51bWJlcltdLCBtZWFuID0gMCwgc3RkRGV2ID0gMSkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmQ8VD4oc2hhcGUsICgpID0+IHV0aWwucmFuZEdhdXNzKG1lYW4sIHN0ZERldiwgdHJ1ZSkpO1xuICB9XG5cbiAgc3RhdGljIHJhbmRVbmlmb3JtPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIGE6IG51bWJlciwgYjogbnVtYmVyKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kVW5pZm9ybShhLCBiKSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNjYWxhciBleHRlbmRzIE5EQXJyYXkge1xuICBjb25zdHJ1Y3RvcihkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIGlmIChkYXRhLnRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgZGF0YS50ZXh0dXJlU2hhcGVSQyA9IFsxLCAxXTtcbiAgICB9XG4gICAgc3VwZXIoW10sIGRhdGEpO1xuICB9XG5cbiAgc3RhdGljIG5ldyh2YWx1ZTogbnVtYmVyKSB7XG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3ZhbHVlczogbmV3IEZsb2F0MzJBcnJheShbdmFsdWVdKX0pO1xuICB9XG5cbiAgc3RhdGljIFpFUk8gPSBTY2FsYXIubmV3KDApO1xuICBzdGF0aWMgT05FID0gU2NhbGFyLm5ldygxKTtcbiAgc3RhdGljIFRXTyA9IFNjYWxhci5uZXcoMik7XG4gIHN0YXRpYyBORUdfT05FID0gU2NhbGFyLm5ldygtMSk7XG5cbiAgZ2V0KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbMF07XG4gIH1cblxuICBzZXQodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbMF0gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVswXSArPSB2YWx1ZTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXJyYXkxRCBleHRlbmRzIE5EQXJyYXkge1xuICBzaGFwZTogW251bWJlcl07XG5cbiAgY29uc3RydWN0b3IoZGF0YTogTkRBcnJheURhdGEpIHtcbiAgICBjb25zdCBzaGFwZSA9IChkYXRhLnZhbHVlcyAhPSBudWxsKSA/XG4gICAgICAgIFtkYXRhLnZhbHVlcy5sZW5ndGhdIDpcbiAgICAgICAgW3V0aWwuc2l6ZUZyb21TaGFwZShkYXRhLnRleHR1cmVTaGFwZVJDISldO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcodmFsdWVzOiBGbG9hdDMyQXJyYXl8bnVtYmVyW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICBpbmZlcnJlZFNoYXBlLmxlbmd0aCA9PT0gMSxcbiAgICAgICAgICBgRXJyb3IgY29uc3RydWN0aW5nIEFycmF5MUQuIFNoYXBlIG9mIHZhbHVlcyAke2luZmVycmVkU2hhcGV9IGlzIGAgK1xuICAgICAgICAgICAgICBgbm90IDEgZGltZW5zaW9uYWwuYCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkxRCh7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbaV07XG4gIH1cblxuICBzZXQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVtpXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIsIGk6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbaV0gKz0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvYzogW251bWJlcl0pOiBudW1iZXIge1xuICAgIHJldHVybiBsb2NbMF07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyXSB7XG4gICAgcmV0dXJuIFtpbmRleF07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXJdKTogQXJyYXkxRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkxRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTJEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdO1xuXG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gMiwgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggMicpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gIH1cblxuICBzdGF0aWMgbmV3KFxuICAgICAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICBpZiAoaW5mZXJyZWRTaGFwZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgICAgICAgICBzaGFwZSwgaW5mZXJyZWRTaGFwZSxcbiAgICAgICAgICAgIGBFcnJvciB3aGVuIGNvbnN0cnVjdGluZyBBcnJheTJELiBTaGFwZSBvZiB2YWx1ZXMgYCArXG4gICAgICAgICAgICAgICAgYCR7aW5mZXJyZWRTaGFwZX0gZG9lcyBub3QgbWF0Y2ggdGhlIHByb3ZpZGVkIHNoYXBlIGAgK1xuICAgICAgICAgICAgICAgIGAke3NoYXBlfS4gYCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkyRChzaGFwZSwge3ZhbHVlczogdG9UeXBlZEFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIGdldChpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyBqXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyBqXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jczogW251bWJlciwgbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc3RyaWRlMCAqIGxvY3NbMF0gKyBsb2NzWzFdO1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgcmV0dXJuIFtNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUwKSwgaW5kZXggJSB0aGlzLnN0cmlkZTBdO1xuICB9XG5cbiAgc3RhdGljIHplcm9zKHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkyRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTNEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTE6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gMywgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggMycpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gICAgdGhpcy5zdHJpZGUxID0gdGhpcy5zdHJpZGVzWzFdO1xuICB9XG5cbiAgc3RhdGljIG5ldyhcbiAgICAgIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgICB2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdW10pIHtcbiAgICBpZiAoISh2YWx1ZXMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpKSB7XG4gICAgICBjb25zdCBpbmZlcnJlZFNoYXBlID0gdXRpbC5pbmZlclNoYXBlKHZhbHVlcyk7XG4gICAgICBpZiAoaW5mZXJyZWRTaGFwZS5sZW5ndGggPiAxKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgICAgICAgICBzaGFwZSwgaW5mZXJyZWRTaGFwZSxcbiAgICAgICAgICAgIGBFcnJvciB3aGVuIGNvbnN0cnVjdGluZyBBcnJheTNELiBTaGFwZSBvZiB2YWx1ZXMgYCArXG4gICAgICAgICAgICAgICAgYCR7aW5mZXJyZWRTaGFwZX0gZG9lcyBub3QgbWF0Y2ggdGhlIHByb3ZpZGVkIHNoYXBlIGAgK1xuICAgICAgICAgICAgICAgIGAke3NoYXBlfS4gYCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuZXcgQXJyYXkzRChzaGFwZSwge3ZhbHVlczogdG9UeXBlZEFycmF5KHZhbHVlcyl9KTtcbiAgfVxuXG4gIGdldChpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyBrXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyBrXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jczogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIHRoaXMuc3RyaWRlMSAqIGxvY3NbMV0gKyBsb2NzWzJdO1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBpID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMCk7XG4gICAgaW5kZXggLT0gaSAqIHRoaXMuc3RyaWRlMDtcbiAgICByZXR1cm4gW2ksIE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTEpLCBpbmRleCAlIHRoaXMuc3RyaWRlMV07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IEFycmF5M0Qge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zPEFycmF5M0Q+KHNoYXBlKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXJyYXk0RCBleHRlbmRzIE5EQXJyYXkge1xuICBzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG4gIHByaXZhdGUgc3RyaWRlMDogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTE6IG51bWJlcjtcbiAgcHJpdmF0ZSBzdHJpZGUyOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3Ioc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIHV0aWwuYXNzZXJ0KHNoYXBlLmxlbmd0aCA9PT0gNCwgJ1NoYXBlIHNob3VsZCBiZSBvZiBsZW5ndGggNCcpO1xuICAgIHN1cGVyKHNoYXBlLCBkYXRhKTtcbiAgICB0aGlzLnN0cmlkZTAgPSB0aGlzLnN0cmlkZXNbMF07XG4gICAgdGhpcy5zdHJpZGUxID0gdGhpcy5zdHJpZGVzWzFdO1xuICAgIHRoaXMuc3RyaWRlMiA9IHRoaXMuc3RyaWRlc1syXTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcoXG4gICAgICBzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgICB2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5NEQuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTREKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpXG4gICAgICAgIFt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIHRoaXMuc3RyaWRlMiAqIGsgKyBsXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyLCBsOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpXG4gICAgICAgIFt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIHRoaXMuc3RyaWRlMiAqIGsgKyBsXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc3RyaWRlMCAqIGxvY3NbMF0gKyB0aGlzLnN0cmlkZTEgKiBsb2NzWzFdICtcbiAgICAgICAgdGhpcy5zdHJpZGUyICogbG9jc1syXSArIGxvY3NbM107XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gICAgY29uc3QgaSA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTApO1xuICAgIGluZGV4IC09IGkgKiB0aGlzLnN0cmlkZTA7XG4gICAgY29uc3QgaiA9IE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTEpO1xuICAgIGluZGV4IC09IGogKiB0aGlzLnN0cmlkZTE7XG4gICAgcmV0dXJuIFtpLCBqLCBNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUyKSwgaW5kZXggJSB0aGlzLnN0cmlkZTJdO1xuICB9XG5cbiAgc3RhdGljIHplcm9zKHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IEFycmF5NEQge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zPEFycmF5NEQ+KHNoYXBlKTtcbiAgfVxufVxuXG50eXBlIEFycmF5RGF0YSA9IEZsb2F0MzJBcnJheXxudW1iZXJbXXxudW1iZXJbXVtdfG51bWJlcltdW11bXXxudW1iZXJbXVtdW11bXTtcblxuZnVuY3Rpb24gdG9UeXBlZEFycmF5KGE6IEFycmF5RGF0YSk6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiAoYSBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkgPyBhIDogbmV3IEZsb2F0MzJBcnJheSh1dGlsLmZsYXR0ZW4oYSkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QjtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBU2NhbGFyO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEJTY2FsYXI7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmVGV4ZWwgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGEgPSB0ZXh0dXJlMkQobWF0cml4QSwgcmVzdWx0VVYpLnI7XG4gICAgICBmbG9hdCBiID0gdGV4dHVyZTJEKG1hdHJpeEIsIHJlc3VsdFVWKS5yO1xuICAgICAgZmxvYXQgYVNjYWxhciA9IHRleHR1cmUyRChtYXRyaXhBU2NhbGFyLCBoYWxmVGV4ZWwpLnI7XG4gICAgICBmbG9hdCBiU2NhbGFyID0gdGV4dHVyZTJEKG1hdHJpeEJTY2FsYXIsIGhhbGZUZXhlbCkucjtcbiAgICAgIHZlYzIgYWJTY2FsZWQgPSB2ZWMyKGEsIGIpICogdmVjMihhU2NhbGFyLCBiU2NhbGFyKTtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoYWJTY2FsZWQueCArIGFiU2NhbGVkLnksIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgYWRkU2NhbGVkTWF0cmljZXNQcm9ncmFtOiBXZWJHTFByb2dyYW0sXG4gICAgYTogV2ViR0xUZXh0dXJlLCBiOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIGFTY2FsYXI6IFdlYkdMVGV4dHVyZSwgYlNjYWxhcjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnNldFByb2dyYW0oYWRkU2NhbGVkTWF0cmljZXNQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYVNjYWxhciwgJ21hdHJpeEFTY2FsYXInLCAyKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGJTY2FsYXIsICdtYXRyaXhCU2NhbGFyJywgMyk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRBZGRTY2FsZWRNYXRyaWNlc0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBhU2NhbGFyOiBudW1iZXIsIGJTY2FsYXI6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gIGNvbnN0IGFUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBiVGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgYVNjYWxhclRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGNvbnN0IGJTY2FsYXJUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBjb25zdCByZXN1bHRUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuXG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4LCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJUZXgsIHJvd3MsIGNvbHVtbnMsIGIpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVNjYWxhclRleCwgMSwgMSwgbmV3IEZsb2F0MzJBcnJheShbYVNjYWxhcl0pKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJTY2FsYXJUZXgsIDEsIDEsIG5ldyBGbG9hdDMyQXJyYXkoW2JTY2FsYXJdKSk7XG5cbiAgYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgICBncGdwdSwgcHJvZ3JhbSwgYVRleCwgYlRleCwgcm93cywgY29sdW1ucywgYVNjYWxhclRleCwgYlNjYWxhclRleCxcbiAgICAgIHJlc3VsdFRleCk7XG5cbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXgsIHJvd3MsIGNvbHVtbnMpO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleCk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYlRleCk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhU2NhbGFyVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiU2NhbGFyVGV4KTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuXG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb259IGZyb20gJy4uL21hdGgnO1xuXG5pbXBvcnQgKiBhcyBiaW5hcnlvcF9ncHUgZnJvbSAnLi9iaW5hcnlvcF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCB0eXBlIE9wZXJhdGlvbiA9ICcrJyB8ICctJyB8ICcqJyB8ICcvJztcblxuZXhwb3J0IGVudW0gT3BlcmFuZFR5cGUge1xuICBNQVRSSVgsXG4gIFNDQUxBUlxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgYVR5cGU6IE9wZXJhbmRUeXBlLCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLCBvcDogT3BlcmF0aW9uLFxuICAgIGJUeXBlOiBPcGVyYW5kVHlwZSwgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIGNvbnN0IGFVViA9IG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoYVR5cGUsIGFPcmllbnRhdGlvbik7XG4gIGNvbnN0IGJVViA9IG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoYlR5cGUsIGJPcmllbnRhdGlvbik7XG4gIGNvbnN0IHJlc3VsdE9wID0gYGdsX0ZyYWdDb2xvciA9IHZlYzQoYSAke29wfSBiLCAwLCAwLCAwKTtgO1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGFVViwgYlVWLCByZXN1bHRPcCk7XG59XG5cbmZ1bmN0aW9uIG9wZXJhbmRUb1NoYWRlclNuaXBwZXQoXG4gICAgb3BlcmFuZDogT3BlcmFuZFR5cGUsIG9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIHN3aXRjaCAob3BlcmFuZCkge1xuICAgIGNhc2UgT3BlcmFuZFR5cGUuTUFUUklYOlxuICAgICAgcmV0dXJuICdyZXN1bHRVVicgK1xuICAgICAgICAgIChvcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUiA/ICcuc3QnIDogJy50cycpO1xuICAgIGNhc2UgT3BlcmFuZFR5cGUuU0NBTEFSOlxuICAgICAgcmV0dXJuICd2ZWMyKDAuNSwgMC41KSc7XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcignVW5rbm93biBvcGVyYW5kIHR5cGUnKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYWRkU3ViTXVsRGl2KFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYjogV2ViR0xUZXh0dXJlLFxuICAgIGJTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS5iaW5hcnlPcChcbiAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhLCBhU2hhcGVSb3dDb2wsIGIsIGJTaGFwZVJvd0NvbCwgcmVzdWx0LFxuICAgICAgcmVzdWx0U2hhcGVSb3dDb2wpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkU2NhbGFyUGx1c01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhNaW51c1NjYWxhckRvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBudW1iZXIsXG4gICAgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuTUFUUklYLCBhT3JpZW50YXRpb24sICctJywgT3BlcmFuZFR5cGUuU0NBTEFSLFxuICAgICAgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIGEsIGFTaGFwZSwgbmV3IEZsb2F0MzJBcnJheShbYl0pLCBbMSwgMV0sIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJNaW51c01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnLScsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJUaW1lc01hdHJpeERvd25sb2FkKFxuICAgIGE6IG51bWJlciwgYjogRmxvYXQzMkFycmF5LCBiU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuU0NBTEFSLCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCxcbiAgICAgIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkoW2FdKSwgWzEsIDFdLCBiLCBiU2hhcGUsIHNyYyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUaW1lc01hdHJpeERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCBzaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBzcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgIE9wZXJhbmRUeXBlLk1BVFJJWCwgYU9yaWVudGF0aW9uLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCwgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKGEsIHNoYXBlLCBiLCBzaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFBsdXNNYXRyaXhEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIGI6IEZsb2F0MzJBcnJheSwgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgYU9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5NQVRSSVgsIGFPcmllbnRhdGlvbiwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgsIGJPcmllbnRhdGlvbik7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUudXBsb2FkQmluYXJ5T3BEb3dubG9hZChhLCBzaGFwZSwgYiwgc2hhcGUsIHNyYyk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGFyZ21pbm1heF9ncHUgZnJvbSAnLi9hcmdtaW5tYXhfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtgO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhcmdNYXhBID0gZ2V0QXJnTWluTWF4KG1hdHJpeEEpO1xuICAgICAgZmxvYXQgYXJnTWF4QiA9IGdldEFyZ01pbk1heChtYXRyaXhCKTtcbiAgICAgIGZsb2F0IHZhbHVlO1xuICAgICAgaWYgKGlzTmFOKGFyZ01heEEpKSB7XG4gICAgICAgIHZhbHVlID0gYXJnTWF4QTtcbiAgICAgIH0gZWxzZSBpZiAoaXNOYU4oYXJnTWF4QikpIHtcbiAgICAgICAgdmFsdWUgPSBhcmdNYXhCO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFsdWUgPSBmbG9hdChhcmdNYXhBID09IGFyZ01heEIpO1xuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcmdNYXhFcXVhbHNGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpLFxuICAgIGFyZ21pbm1heF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoJz4nLCByb3dzLCBjb2x1bW5zKSxcbiAgICBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKVxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXJnTWF4RXF1YWxzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIG1heEVxdWFsc1Byb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGI6IFdlYkdMVGV4dHVyZSwgbnVtUm93czogbnVtYmVyLCBudW1Db2xzOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCAxLCAxKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtYXhFcXVhbHNQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCB7SVNfTkFOX1NIQURFUl9GVU5DfSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtgO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGdldEFyZ01pbk1heChtYXRyaXhBKSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmZ1bmN0aW9uIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBjb21wT3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBbXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpLFxuICAgIGdldEZyYWdtZW50U2hhZGVyR2V0QXJnTWluTWF4U291cmNlKGNvbXBPcCwgcm93cywgY29sdW1ucyksXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJNYWluU291cmNlKClcbiAgXS5qb2luKCdcXG4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01pbkZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICc8Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcmdNYXhGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRBcmdNaW5NYXhGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnPicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoXG4gICAgY29tcE9wOiBzdHJpbmcsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gIHJldHVybiBgXG4gICAgY29uc3QgdmVjMiBkaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICAke0lTX05BTl9TSEFERVJfRlVOQ31cblxuICAgIGZsb2F0IGdldEFyZ01pbk1heChpbiBzYW1wbGVyMkQgbWF0cml4KSB7XG4gICAgICB2ZWMyIGJlc3RDUiA9IHZlYzIoMCwgMCk7XG4gICAgICBmbG9hdCBiZXN0VmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4LCBiZXN0Q1IpLnI7XG5cbiAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGRpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGRpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiBjciA9IHZlYzIoYywgcik7XG4gICAgICAgICAgdmVjMiB1diA9IChjciArIGhhbGZDUikgLyBkaW1DUjtcbiAgICAgICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXgsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgICAgIHJldHVybiB2YWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHZhbHVlICR7Y29tcE9wfSBiZXN0VmFsdWUpIHtcbiAgICAgICAgICAgIGJlc3RWYWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgYmVzdENSID0gY3I7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gYmVzdENSLnggKyAoYmVzdENSLnkgKiBkaW1DUi54KTtcbiAgICB9XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcmdNaW5NYXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWluTWF4UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYU51bVJvd3M6IG51bWJlciwgYU51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1pbk1heFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3Bvb2xfZ3B1JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyQXZnUG9vbFNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIHBvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnYXZnJywgZmFsc2UpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYXZnUG9vbChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcG9vbF9ncHUucG9vbENvbW1vbihncGdwdSwgcHJvZ3JhbSwgeCwgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHhUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdLCBtZWFuVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSxcbiAgICB2YXJpYW5jZVRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgb2Zmc2V0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXXxudWxsLFxuICAgIHNjYWxlVGV4U2hhcGVSQz86IFtudW1iZXIsIG51bWJlcl18bnVsbCwgdmFyaWFuY2VFcHNpbG9uID0gMC4wMDEpOiBzdHJpbmcge1xuICBsZXQgb2Zmc2V0U2FtcGxlclNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRDb29yZHNTbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRVVlNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFZhbHVlU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0T3BlcmF0aW9uU25pcHBldCA9ICcwLjAnO1xuXG4gIGxldCBzY2FsZVNhbXBsZXJTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZUNvb3Jkc1NuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlVVZTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZVZhbHVlU25pcHBldCA9ICcnO1xuICBsZXQgc2NhbGVPcGVyYXRpb25TbmlwcGV0ID0gJyc7XG5cbiAgaWYgKG9mZnNldFRleFNoYXBlUkMgIT0gbnVsbCkge1xuICAgIG9mZnNldFNhbXBsZXJTbmlwcGV0ID0gJ3VuaWZvcm0gc2FtcGxlcjJEIG9mZnNldDsnO1xuICAgIG9mZnNldFNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gYGNvbnN0IHZlYzIgb2Zmc2V0U2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICAgICAke29mZnNldFRleFNoYXBlUkNbMV19LCAke29mZnNldFRleFNoYXBlUkNbMF19KTtgO1xuICAgIG9mZnNldENvb3Jkc1NuaXBwZXQgPSAndmVjMiBvZmZzZXRDb29yZHNDUiA9IG1vZCh5VGV4Q1IsIG9mZnNldFNoYXBlQ1IpOyc7XG4gICAgb2Zmc2V0VVZTbmlwcGV0ID1cbiAgICAgICAgJ3ZlYzIgb2Zmc2V0VVYgPSAob2Zmc2V0Q29vcmRzQ1IgKyBoYWxmQ1IpIC8gb2Zmc2V0U2hhcGVDUjsnO1xuICAgIG9mZnNldFZhbHVlU25pcHBldCA9ICdmbG9hdCBvZmZzZXRWYWx1ZSA9IHRleHR1cmUyRChvZmZzZXQsIG9mZnNldFVWKS5yOyc7XG4gICAgb2Zmc2V0T3BlcmF0aW9uU25pcHBldCA9ICdvZmZzZXRWYWx1ZSc7XG4gIH1cblxuICBpZiAoc2NhbGVUZXhTaGFwZVJDICE9IG51bGwpIHtcbiAgICBzY2FsZVNhbXBsZXJTbmlwcGV0ID0gJ3VuaWZvcm0gc2FtcGxlcjJEIHNjYWxlOyc7XG4gICAgc2NhbGVTaGFwZUluaXRpYWxpemF0aW9uU25pcHBldCA9IGBjb25zdCB2ZWMyIHNjYWxlU2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICAgICAke3NjYWxlVGV4U2hhcGVSQ1sxXX0sICR7c2NhbGVUZXhTaGFwZVJDWzBdfSk7YDtcbiAgICBzY2FsZUNvb3Jkc1NuaXBwZXQgPSAndmVjMiBzY2FsZUNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgc2NhbGVTaGFwZUNSKTsnO1xuICAgIHNjYWxlVVZTbmlwcGV0ID0gJ3ZlYzIgc2NhbGVVViA9IChzY2FsZUNvb3Jkc0NSICsgaGFsZkNSKSAvIHNjYWxlU2hhcGVDUjsnO1xuICAgIHNjYWxlVmFsdWVTbmlwcGV0ID0gJ2Zsb2F0IHNjYWxlVmFsdWUgPSB0ZXh0dXJlMkQoc2NhbGUsIHNjYWxlVVYpLnI7JztcbiAgICBzY2FsZU9wZXJhdGlvblNuaXBwZXQgPSAnaW52ICo9IHNjYWxlVmFsdWU7JztcbiAgfVxuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWVhbjtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB2YXJpYW5jZTtcbiAgICAke29mZnNldFNhbXBsZXJTbmlwcGV0fVxuICAgICR7c2NhbGVTYW1wbGVyU25pcHBldH1cblxuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG4gICAgY29uc3QgdmVjMiBtZWFuU2hhcGVDUiA9IHZlYzIoJHttZWFuVGV4U2hhcGVSQ1sxXX0sICR7bWVhblRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHZhcmlhbmNlU2hhcGVDUiA9IHZlYzIoXG4gICAgICAgICR7dmFyaWFuY2VUZXhTaGFwZVJDWzFdfSwgJHt2YXJpYW5jZVRleFNoYXBlUkNbMF19KTtcblxuICAgICR7b2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXR9XG4gICAgJHtzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0fVxuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCBmbG9hdCB2YXJpYW5jZUVwc2lsb24gPSAke3ZhcmlhbmNlRXBzaWxvbn07XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIHZlYzIgbWVhbkNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgbWVhblNoYXBlQ1IpO1xuICAgICAgdmVjMiB2YXJpYW5jZUNvb3Jkc0NSID0gbW9kKHlUZXhDUiwgdmFyaWFuY2VTaGFwZUNSKTtcbiAgICAgICR7b2Zmc2V0Q29vcmRzU25pcHBldH1cbiAgICAgICR7c2NhbGVDb29yZHNTbmlwcGV0fVxuXG4gICAgICB2ZWMyIG1lYW5VViA9IChtZWFuQ29vcmRzQ1IgKyBoYWxmQ1IpIC8gbWVhblNoYXBlQ1I7XG4gICAgICB2ZWMyIHZhcmlhbmNlVVYgPSAodmFyaWFuY2VDb29yZHNDUiArIGhhbGZDUikgLyB2YXJpYW5jZVNoYXBlQ1I7XG4gICAgICAke29mZnNldFVWU25pcHBldH1cbiAgICAgICR7c2NhbGVVVlNuaXBwZXR9XG5cbiAgICAgIGZsb2F0IHhWYWx1ZSA9IHRleHR1cmUyRCh4LCByZXN1bHRVVikucjtcbiAgICAgIGZsb2F0IG1lYW5WYWx1ZSA9IHRleHR1cmUyRChtZWFuLCBtZWFuVVYpLnI7XG4gICAgICBmbG9hdCB2YXJpYW5jZVZhbHVlID0gdGV4dHVyZTJEKHZhcmlhbmNlLCB2YXJpYW5jZVVWKS5yO1xuICAgICAgJHtvZmZzZXRWYWx1ZVNuaXBwZXR9XG4gICAgICAke3NjYWxlVmFsdWVTbmlwcGV0fVxuXG4gICAgICBmbG9hdCBpbnYgPSAxLjAgLyBzcXJ0KHZhcmlhbmNlVmFsdWUgKyB2YXJpYW5jZUVwc2lsb24pO1xuICAgICAgJHtzY2FsZU9wZXJhdGlvblNuaXBwZXR9XG4gICAgICBmbG9hdCB4VGltZXNJbnYgPSB4VmFsdWUgKiBpbnY7XG4gICAgICBmbG9hdCBtZWFuVGltZXNJbnZXaXRoT2Zmc2V0ID0gJHtvZmZzZXRPcGVyYXRpb25TbmlwcGV0fVxuICAgICAgICAgIC0gbWVhblZhbHVlICogaW52O1xuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHhUaW1lc0ludiArIG1lYW5UaW1lc0ludldpdGhPZmZzZXQsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmF0Y2hOb3JtYWxpemF0aW9uKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHhTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgbWVhbjogV2ViR0xUZXh0dXJlLFxuICAgIG1lYW5TaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgdmFyaWFuY2U6IFdlYkdMVGV4dHVyZSxcbiAgICB2YXJpYW5jZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBvZmZzZXQ6IFdlYkdMVGV4dHVyZXxudWxsLFxuICAgIG9mZnNldFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsIHNjYWxlOiBXZWJHTFRleHR1cmV8bnVsbCxcbiAgICBzY2FsZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUobWVhbiwgJ21lYW4nLCAxKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHZhcmlhbmNlLCAndmFyaWFuY2UnLCAyKTtcbiAgbGV0IG5leHRJbmRleCA9IDM7XG4gIGlmIChvZmZzZXQgIT0gbnVsbCkge1xuICAgIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShvZmZzZXQsICdvZmZzZXQnLCBuZXh0SW5kZXgpO1xuICAgIG5leHRJbmRleCsrO1xuICB9XG4gIGlmIChzY2FsZSAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNjYWxlLCAnc2NhbGUnLCBuZXh0SW5kZXgpO1xuICB9XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIGFSZXN1bHRVVjogc3RyaW5nLCBiUmVzdWx0VVY6IHN0cmluZywgb3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhID0gdGV4dHVyZTJEKG1hdHJpeEEsICR7YVJlc3VsdFVWfSkucjtcbiAgICAgIGZsb2F0IGIgPSB0ZXh0dXJlMkQobWF0cml4QiwgJHtiUmVzdWx0VVZ9KS5yO1xuICAgICAgJHtvcH1cbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmFyeU9wKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYjogV2ViR0xUZXh0dXJlLFxuICAgIGJTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkQmluYXJ5T3BEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIGFTaGFwZTogW251bWJlciwgbnVtYmVyXSwgYjogRmxvYXQzMkFycmF5LFxuICAgIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSwgZnJhZ21lbnRTaGFkZXJTb3VyY2U6IHN0cmluZyk6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtID0gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG5cbiAgY29uc3QgYVRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGFTaGFwZVswXSwgYVNoYXBlWzFdKTtcbiAgY29uc3QgYlRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKGJTaGFwZVswXSwgYlNoYXBlWzFdKTtcblxuICBjb25zdCByZXN1bHRTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICBbTWF0aC5tYXgoYVNoYXBlWzBdLCBiU2hhcGVbMF0pLCBNYXRoLm1heChhU2hhcGVbMV0sIGJTaGFwZVsxXSldO1xuXG4gIGNvbnN0IHJlc3VsdFRleHR1cmU6IFdlYkdMVGV4dHVyZSA9XG4gICAgICBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJlc3VsdFNoYXBlWzBdLCByZXN1bHRTaGFwZVsxXSk7XG5cbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCBhU2hhcGVbMF0sIGFTaGFwZVsxXSwgYSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShiVGV4dHVyZSwgYlNoYXBlWzBdLCBiU2hhcGVbMV0sIGIpO1xuXG4gIGJpbmFyeU9wKFxuICAgICAgZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCBhU2hhcGUsIGJUZXh0dXJlLCBiU2hhcGUsIHJlc3VsdFRleHR1cmUsXG4gICAgICByZXN1bHRTaGFwZSk7XG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUoXG4gICAgICByZXN1bHRUZXh0dXJlLCByZXN1bHRTaGFwZVswXSwgcmVzdWx0U2hhcGVbMV0pO1xuXG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGJUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgeDFTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB4MlNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgcmVzdWx0U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgYXhpczogbnVtYmVyKTogc3RyaW5nIHtcbiAgY29uc3QgeDFUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MVNoYXBlUkNEKTtcbiAgY29uc3QgeDJUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MlNoYXBlUkNEKTtcblxuICBjb25zdCB5QXhlcyA9IFsneVInLCAneUMnLCAneUQnXTtcbiAgY29uc3QgY29uY2F0QXhpcyA9IHlBeGVzW2F4aXNdO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHgxO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHgyO1xuXG4gICAgY29uc3QgdmVjMiB4MVNoYXBlQ1IgPSB2ZWMyKCR7eDFUZXhTaGFwZVJDWzFdfSwgJHt4MVRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHgyU2hhcGVDUiA9IHZlYzIoJHt4MlRleFNoYXBlUkNbMV19LjAsICR7eDJUZXhTaGFwZVJDWzBdfS4wKTtcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIHlEKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtyZXN1bHRTaGFwZVJDRFsyXX0uMCk7XG4gICAgICBmbG9hdCB5RCA9IG1vZCh5VGV4Q1IueCwgJHtyZXN1bHRTaGFwZVJDRFsyXX0uMCk7XG5cbiAgICAgIGZsb2F0IHZhbHVlID0gMC4wO1xuXG4gICAgICBpZiAoJHtjb25jYXRBeGlzfSA8ICR7eDFTaGFwZVJDRFtheGlzXX0uMCkge1xuICAgICAgICAvLyBNYXAgeVIsIHlDLCB5RCBiYWNrIHRvIHgxIGNvb3JkaW5hdGVzLlxuICAgICAgICB2ZWMyIHgxQ1IgPSB2ZWMyKHlDICogJHt4MVNoYXBlUkNEWzJdfS4wICsgeUQsIHlSKTtcbiAgICAgICAgdmVjMiB4MVVWID0gKHgxQ1IgKyBoYWxmQ1IpIC8geDFTaGFwZUNSO1xuICAgICAgICB2YWx1ZSA9IHRleHR1cmUyRCh4MSwgeDFVVikucjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgICR7Y29uY2F0QXhpc30gPSAke2NvbmNhdEF4aXN9IC0gJHt4MVNoYXBlUkNEW2F4aXNdfS4wO1xuXG4gICAgICAgIC8vIE1hcCB5UiwgeUMsIHlEIGJhY2sgdG8geDIgY29vcmRpbmF0ZXMuXG4gICAgICAgIHZlYzIgeDJDUiA9IHZlYzIoeUMgKiAke3gyU2hhcGVSQ0RbMl19LjAgKyB5RCwgeVIpO1xuICAgICAgICB2ZWMyIHgyVVYgPSAoeDJDUiArIGhhbGZDUikgLyB4MlNoYXBlQ1I7XG4gICAgICAgIHZhbHVlID0gdGV4dHVyZTJEKHgyLCB4MlVWKS5yO1xuICAgICAgfVxuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLjAsIDAuMCwgMC4wKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbmNhdDNEKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDE6IFdlYkdMVGV4dHVyZSxcbiAgICB4MjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcmVzdWx0U2hhcGVSQ1swXSwgcmVzdWx0U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4MSwgJ3gxJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4MiwgJ3gyJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuXG5pbXBvcnQgKiBhcyBjb252X2dwdSBmcm9tICcuL2NvbnZfZ3B1JztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJXZWlnaHRzU291cmNlKFxuICAgIHhTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsXG4gICAgb3V0cHV0RGVwdGg6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHplcm9QYWQ6IG51bWJlcikge1xuICBjb25zdCBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCA9XG4gICAgICBjb252X2dwdS5nZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk7XG4gIGNvbnN0IGlucHV0RGVwdGggPSB4U2hhcGVSb3dDb2xEZXB0aFsyXTtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUm93Q29sRGVwdGgpO1xuXG4gIGNvbnN0IHlTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgIHhTaGFwZVJvd0NvbERlcHRoLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gIGNvbnN0IHlOdW1Sb3dzID0geVNoYXBlWzBdO1xuICBjb25zdCB5TnVtQ29scyA9IHlTaGFwZVsxXTtcbiAgY29uc3QgeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHlTaGFwZSk7XG5cbiAgY29uc3QgZlNpemVUaW1lc0lucHV0RGVwdGggPSBmU2l6ZSAqIGlucHV0RGVwdGg7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG4gIGA7XG5cbiAgcmV0dXJuIHByb2xvZ3VlICsgJ1xcbicgKyBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCArICdcXG4nICtcbiAgICAgIGBcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG4gICAgY29uc3QgdmVjMiBkeVNoYXBlQ1IgPSB2ZWMyKCR7eVRleFNoYXBlUkNbMV19LCAke3lUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHdUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh3VGV4Uiwgd1RleEMpIHRvIDREICh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICBmbG9hdCB3UiA9IGZsb29yKHdUZXhDUi55IC8gJHtmU2l6ZVRpbWVzSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCB3VGV4UkxlZnRvdmVyID0gd1RleENSLnkgLSB3UiAqICR7ZlNpemVUaW1lc0lucHV0RGVwdGh9LjA7XG4gICAgICBmbG9hdCB3QyA9IGZsb29yKHdUZXhSTGVmdG92ZXIgLyAke2lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDEgPSBtb2Qod1RleFJMZWZ0b3ZlciwgJHtpbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQyID0gd1RleENSLng7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggZHkoOiwgOiwgZDIpIHRvIGdldCBkdyh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHlSID0gMC4wOyB5UiA8ICR7eU51bVJvd3N9LjA7IHlSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHdSICsgeVIgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcbiAgICAgICAgZm9yIChmbG9hdCB5QyA9IDAuMDsgeUMgPCAke3lOdW1Db2xzfS4wOyB5QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHdDICsgeUMgKiAke3N0cmlkZX0uMCAtICR7emVyb1BhZH0uMDtcblxuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh4UiwgeEMsIGQxKSB0byAyRCAoeFRleFIsIHhUZXhDKS5cbiAgICAgICAgICAvLyBNYXAgZnJvbSAzRCAoeVIsIHlDLCBkMikgdG8gMkQgKHlUZXhSLCB5VGV4QykuXG4gICAgICAgICAgdmVjMiB4eVRleEMgPSB2ZWMyKHhDLCB5QykgKiB2ZWMyKCR7aW5wdXREZXB0aH0uMCwgJHtvdXRwdXREZXB0aH0uMCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgdmVjMihkMSwgZDIpO1xuICAgICAgICAgIGZsb2F0IHhUZXhDID0geHlUZXhDLng7XG4gICAgICAgICAgZmxvYXQgeVRleEMgPSB4eVRleEMueTtcblxuICAgICAgICAgIC8vIFJlYWQgZHkoeVIsIHlDLCBkMikuXG4gICAgICAgICAgdmVjMiBkeVVWID0gKHZlYzIoeVRleEMsIHlUZXhSKSArIGhhbGZDUikgLyBkeVNoYXBlQ1I7XG4gICAgICAgICAgZmxvYXQgZHlWYWx1ZSA9IHRleHR1cmUyRChkeSwgZHlVVikucjtcblxuICAgICAgICAgIC8vIFJlYWQgeCh4UiwgeEMsIGQxKSAocG90ZW50aWFsbHkgemVyby1wYWRkZWQpLlxuICAgICAgICAgIGZsb2F0IHhWYWx1ZSA9XG4gICAgICAgICAgICBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCh4LCB4U2hhcGVDUiwgdmVjMih4VGV4QywgeFRleFIpKTtcblxuICAgICAgICAgIGRvdFByb2QgKz0gKHhWYWx1ZSAqIGR5VmFsdWUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJDb252VHJhbnNwb3NlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvcmlnSW5wdXREZXB0aDogbnVtYmVyLFxuICAgIG9yaWdTdHJpZGU6IG51bWJlciwgb3JpZ1BhZDogbnVtYmVyLCBoYXNCaWFzOiBib29sZWFuKSB7XG4gIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gIGNvbnN0IFt4Um93cywgeENvbHMsIG9yaWdPdXRwdXREZXB0aF0gPSB4U2hhcGVSQ0Q7XG5cbiAgY29uc3QgeFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHhTaGFwZVJDRCk7XG4gIGNvbnN0IHdUZXhTaGFwZVJDID1cbiAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKG9yaWdJbnB1dERlcHRoLCBvcmlnT3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICBjb25zdCBnZXRCaWFzVmFsdWUgPSBoYXNCaWFzID9cbiAgICAgIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKG9yaWdJbnB1dERlcHRoKSA6XG4gICAgICAnJztcbiAgY29uc3QgYmlhc1Byb2xvZ3VlID0gaGFzQmlhcyA/ICd1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7JyA6ICcnO1xuICBjb25zdCBiaWFzT3BlcmF0aW9uID0gaGFzQmlhcyA/ICdkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTsnIDogJyc7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICAke2JpYXNQcm9sb2d1ZX1cbiAgICBgO1xuXG4gIHJldHVybiBwcm9sb2d1ZSArICdcXG4nICsgZ2V0Qmlhc1ZhbHVlICsgJ1xcbicgK1xuICAgICAgYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAtIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IHhSQ29ybmVyID0geFJDQ29ybmVyLng7XG4gICAgICBmbG9hdCB4Q0Nvcm5lciA9IHhSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBDb252b2x2ZSB4KD8sID8sIGQxKSB3aXRoIHcoOiwgOiwgZDIsIGQxKSB0byBnZXQgeSh5UiwgeUMsIGQyKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG5cbiAgICAgICAgZmxvYXQgeFIgPSAoeFJDb3JuZXIgKyB3UikgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgIC8vIFRPRE8oc21pbGtvdik6IFNwbGljZSB0aGlzIHdpdGggYW5vdGhlciB2ZXJzaW9uIHdoZXJlIHlvdSBjYWxsXG4gICAgICAgIC8vIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKCkuIEhlcmUgYW5kIGJlbG93LlxuICAgICAgICBpZiAoeFIgPCAwLjAgfHwgeFIgPj0gJHt4Um93c30uMCB8fCBmcmFjdCh4UikgPiAwLjApIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZsb2F0IHdSUGVybSA9ICR7ZlNpemV9LjAgLSAxLjAgLSB3UjtcbiAgICAgICAgZmxvYXQgeFRleFIgPSB4UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHdDID0gMC4wOyB3QyA8ICR7ZlNpemV9LjA7IHdDICs9IDEuMCkge1xuXG4gICAgICAgICAgZmxvYXQgeEMgPSAoeENDb3JuZXIgKyB3QykgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgICAgaWYgKHhDIDwgMC4wIHx8IHhDID49ICR7eENvbHN9LjAgfHwgZnJhY3QoeEMpID4gMC4wKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBmbG9hdCB3Q1Blcm0gPSAke2ZTaXplfS4wIC0gMS4wIC0gd0M7XG4gICAgICAgICAgZmxvYXQgd1RleFIgPSB3UlBlcm0gKiAke2ZTaXplfS4wICogJHtvcmlnSW5wdXREZXB0aH0uMCArXG4gICAgICAgICAgICAgICAgICAgICAgICB3Q1Blcm0gKiAke29yaWdJbnB1dERlcHRofS4wICsgZDI7XG5cbiAgICAgICAgICBmb3IgKGZsb2F0IGQxID0gMC4wOyBkMSA8ICR7b3JpZ091dHB1dERlcHRofS4wOyBkMSArPSAxLjApIHtcbiAgICAgICAgICAgIGZsb2F0IHhUZXhDID0geEMgKiAke29yaWdPdXRwdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleEMgPSBkMTtcblxuICAgICAgICAgICAgLy8gUmVhZCB4KHhSLCB4QywgZDEpLlxuICAgICAgICAgICAgdmVjMiB4VVYgPSAodmVjMih4VGV4QywgeFRleFIpICsgaGFsZkNSKSAvIHhTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgeFZhbHVlID0gdGV4dHVyZTJEKHgsIHhVVikucjtcblxuICAgICAgICAgICAgLy8gUmVhZCB3KHdSUGVybSwgd0NQZXJtLCBkMiwgZDEpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAke2JpYXNPcGVyYXRpb259XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRvdFByb2QsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJEZXJCaWFzU291cmNlKFxuICAgIGR5U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuICBjb25zdCBbeU51bVJvd3MsIHlOdW1Db2xzLCBvdXRwdXREZXB0aF0gPSBkeVNoYXBlUkNEO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGR5O1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHtkeVRleFNoYXBlUkNbMV19LCAke2R5VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBiaWFzVGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBUaGUgYmlhcyB0ZXh0dXJlIFJDIHNoYXBlIGlzIFsxLCBkMl0uXG4gICAgICBmbG9hdCBkMiA9IGJpYXNUZXhDUi54O1xuXG4gICAgICBmbG9hdCBkZXJCaWFzID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB5UiA9IDAuMDsgeVIgPCAke3lOdW1Sb3dzfS4wOyB5UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeVRleFIgPSB5UjtcblxuICAgICAgICBmb3IgKGZsb2F0IHlDID0gMC4wOyB5QyA8ICR7eU51bUNvbHN9LjA7IHlDICs9IDEuMCkge1xuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh5UiwgeUMsIGQyKSB0byAyRCAoeVRleFIsIHlUZXhDKS5cbiAgICAgICAgICBmbG9hdCB5VGV4QyA9IHlDICogJHtvdXRwdXREZXB0aH0uMCArIGQyO1xuXG4gICAgICAgICAgLy8gUmVhZCBkeSh5UiwgeUMsIGQyKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMih5VGV4QywgeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgZGVyQmlhcyArPSBkeVZhbHVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGRlckJpYXMsIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVyQmlhcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGR5VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZXJXZWlnaHRzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeFRleDogV2ViR0xUZXh0dXJlLFxuICAgIGR5VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoZHlUZXgsICdkeScsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29udlRyYW5zcG9zZShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHhUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICB3ZWlnaHRzVGV4OiBXZWJHTFRleHR1cmUsIGJpYXNlc1RleDogV2ViR0xUZXh0dXJlfG51bGwsXG4gICAgcmVzdWx0VGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4VGV4LCAneCcsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUod2VpZ2h0c1RleCwgJ3dlaWdodHMnLCAxKTtcbiAgaWYgKGJpYXNlc1RleCAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGJpYXNlc1RleCwgJ2JpYXNlcycsIDIpO1xuICB9XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHg7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgd2VpZ2h0cztcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBiaWFzZXM7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldE1hdHJpeFZhbHVlT3JaZXJvUGFkU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoaW4gc2FtcGxlcjJEIG1hdHJpeCwgdmVjMiBtYXRyaXhTaGFwZUNSLFxuICAgICAgICB2ZWMyIHJlcXVlc3RlZENSKSB7XG4gICAgICB2ZWMyIHV2ID0gKHJlcXVlc3RlZENSICsgdmVjMigwLjUsIDAuNSkpIC8gbWF0cml4U2hhcGVDUjtcbiAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeCwgdXYpLnI7XG4gICAgICBib29sIGxlc3NUaGFuWmVybyA9IGFueShsZXNzVGhhbih1diwgdmVjMigwLCAwKSkpO1xuICAgICAgYm9vbCBncmVhdGVyVGhhbk9uZSA9IGFueShncmVhdGVyVGhhbih1diwgdmVjMigxLCAxKSkpO1xuICAgICAgYm9vbCBvdXRzaWRlID0gbGVzc1RoYW5aZXJvIHx8IGdyZWF0ZXJUaGFuT25lO1xuICAgICAgcmV0dXJuIG1peCh2YWx1ZSwgMC4wLCBmbG9hdChvdXRzaWRlKSk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlciwgaGFzQmlhczogYm9vbGVhbikge1xuICBjb25zdCBbeFJvd3MsIHhDb2xzLCBpbnB1dERlcHRoXSA9IHhTaGFwZVJDRDtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcbiAgY29uc3Qgd1RleFNoYXBlUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcblxuICByZXR1cm4gYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIHdTaGFwZUNSID0gdmVjMigke3dUZXhTaGFwZVJDWzFdfSwgJHt3VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSBtb2QoeVRleENSLngsICR7b3V0cHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgd1RleEMgPSBkMjtcblxuICAgICAgdmVjMiB4UkNDb3JuZXIgPSB2ZWMyKHlSLCB5QykgKiB2ZWMyKCR7c3RyaWRlfSwgJHtzdHJpZGV9KSAtXG4gICAgICAgICAgdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggdyg6LCA6LCBkMSwgZDIpIHRvIGdldCB5KHlSLCB5QywgZDIpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeFIgPSB4UkNvcm5lciArIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG4gICAgICAgICAgZmxvYXQgeEMgPSB4Q0Nvcm5lciArIHdDO1xuXG4gICAgICAgICAgZm9yIChmbG9hdCBkMSA9IDAuMDsgZDEgPCAke2lucHV0RGVwdGh9LjA7IGQxICs9IDEuMCkge1xuICAgICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuICAgICAgICAgICAgZmxvYXQgd1RleFIgPSB3UiAqICR7ZlNpemUgKiBpbnB1dERlcHRofS4wICtcbiAgICAgICAgICAgICAgICB3QyAqICR7aW5wdXREZXB0aH0uMCArIGQxO1xuXG4gICAgICAgICAgICBmbG9hdCB4VmFsdWUgPVxuICAgICAgICAgICAgICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKHgsIHhTaGFwZUNSLCB2ZWMyKHhUZXhDLCB4VGV4UikpO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHcod1IsIHdDLCBkMSwgZDIpLlxuICAgICAgICAgICAgdmVjMiB3VVYgPSAodmVjMih3VGV4Qywgd1RleFIpICsgaGFsZkNSKSAvIHdTaGFwZUNSO1xuICAgICAgICAgICAgZmxvYXQgd1ZhbHVlID0gdGV4dHVyZTJEKHdlaWdodHMsIHdVVikucjtcblxuICAgICAgICAgICAgZG90UHJvZCArPSB4VmFsdWUgKiB3VmFsdWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoJHtoYXNCaWFzfSkge1xuICAgICAgICBkb3RQcm9kICs9IGdldEJpYXNWYWx1ZShiaWFzZXMsIGQyKTtcbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldEJpYXNWYWx1ZVNvdXJjZShvdXRwdXREZXB0aDogbnVtYmVyKTpcbiAgICBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGZsb2F0IGdldEJpYXNWYWx1ZShpbiBzYW1wbGVyMkQgYmlhcywgZmxvYXQgYmlhc0MpIHtcbiAgICAgIGNvbnN0IHZlYzIgYmlhc1NoYXBlQ1IgPSB2ZWMyKCR7b3V0cHV0RGVwdGh9LCAxKTtcbiAgICAgIHZlYzIgYmlhc0NSID0gdmVjMihtb2QoYmlhc0MsICR7b3V0cHV0RGVwdGh9LjApLCAwKTtcbiAgICAgIHZlYzIgYmlhc1VWID0gKGJpYXNDUiArIHZlYzIoMC41LCAwLjUpKSAvIGJpYXNTaGFwZUNSO1xuICAgICAgcmV0dXJuIHRleHR1cmUyRChiaWFzLCBiaWFzVVYpLnI7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBhU2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCByZXN1bHREZXB0aDogbnVtYmVyLFxuICAgIGZpZWxkU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZDogbnVtYmVyLFxuICAgIGhhc0JpYXM6IGJvb2xlYW4pOiBzdHJpbmcge1xuICBjb25zdCBhU2hhcGVSQzogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGFTaGFwZVJvd0NvbERlcHRoKTtcblxuICBjb25zdCB3ZWlnaHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID0gY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgICBhU2hhcGVSb3dDb2xEZXB0aFsyXSwgcmVzdWx0RGVwdGgsIGZpZWxkU2l6ZSk7XG5cbiAgY29uc3QgcHJvbG9ndWUgPSBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk7XG4gIGNvbnN0IGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkID1cbiAgICAgIGdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTtcbiAgY29uc3QgY29udm9sdmUgPSBnZXRGcmFnbWVudFNoYWRlckNvbnZvbHZlU291cmNlKFxuICAgICAgYVNoYXBlUm93Q29sRGVwdGgsIGZpZWxkU2l6ZSwgcmVzdWx0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCwgaGFzQmlhcyk7XG4gIGNvbnN0IGdldEJpYXNWYWx1ZSA9IGdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKHJlc3VsdERlcHRoKTtcblxuICByZXR1cm4gW1xuICAgIHByb2xvZ3VlLFxuICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkLFxuICAgIGdldEJpYXNWYWx1ZSxcbiAgICBjb252b2x2ZSxcbiAgXS5qb2luKCdcXG4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZvbHZlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHdlaWdodHM6IFdlYkdMVGV4dHVyZSwgYmlhc2VzOiBXZWJHTFRleHR1cmV8bnVsbCwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh3ZWlnaHRzLCAnd2VpZ2h0cycsIDEpO1xuICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYmlhc2VzLCAnYmlhc2VzJywgMik7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgc291cmNlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIHNvdXJjZVNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdW5pZm9ybSB2ZWMyIHNvdXJjZVN0YXJ0Q1I7XG4gICAgdW5pZm9ybSB2ZWMyIGRlc3RTdGFydENSO1xuXG4gICAgY29uc3QgdmVjMiBzb3VyY2VTaGFwZUNSID1cbiAgICAgIHZlYzIoJHtzb3VyY2VTaGFwZVJvd0NvbFsxXX0sICR7c291cmNlU2hhcGVSb3dDb2xbMF19KTtcbiAgICBjb25zdCB2ZWMyIHNvdXJjZVNpemVDUiA9XG4gICAgICB2ZWMyKCR7c291cmNlU2l6ZVJvd0NvbFsxXX0sICR7c291cmNlU2l6ZVJvd0NvbFswXX0pO1xuICAgIGNvbnN0IHZlYzIgZGVzdFNpemVDUiA9XG4gICAgICB2ZWMyKCR7ZGVzdFNpemVSb3dDb2xbMV19LCAke2Rlc3RTaXplUm93Q29sWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIGRlc3RPZmZzZXRDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSkgLSBkZXN0U3RhcnRDUjtcbiAgICAgIGZsb2F0IGRlc3RPZmZzZXRGbGF0ID0gKGRlc3RPZmZzZXRDUi55ICogZGVzdFNpemVDUi54KSArIGRlc3RPZmZzZXRDUi54O1xuICAgICAgdmVjMiBzb3VyY2VPZmZzZXRDUiA9IHZlYzIobW9kKGRlc3RPZmZzZXRGbGF0LCBzb3VyY2VTaXplQ1IueCksXG4gICAgICAgIGZsb29yKGRlc3RPZmZzZXRGbGF0IC8gc291cmNlU2l6ZUNSLngpKTtcbiAgICAgIHZlYzIgc291cmNlQ1IgPSBzb3VyY2VTdGFydENSICsgc291cmNlT2Zmc2V0Q1I7XG4gICAgICB2ZWMyIHNvdXJjZVVWID0gKHNvdXJjZUNSICsgdmVjMigwLjUsIDAuNSkpIC8gc291cmNlU2hhcGVDUjtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRChzb3VyY2UsIHNvdXJjZVVWKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvcHkoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBzb3VyY2U6IFdlYkdMVGV4dHVyZSxcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU3RhcnRSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdDogV2ViR0xUZXh0dXJlLFxuICAgIGRlc3RTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgZGVzdFN0YXJ0Um93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoZGVzdCwgZGVzdFNoYXBlUm93Q29sWzBdLCBkZXN0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbihcbiAgICAgIGRlc3RTdGFydFJvd0NvbFswXSwgZGVzdFNpemVSb3dDb2xbMF0sIGRlc3RTdGFydFJvd0NvbFsxXSxcbiAgICAgIGRlc3RTaXplUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNvdXJjZSwgJ3NvdXJjZScsIDApO1xuICBjb25zdCBzb3VyY2VTdGFydENSTG9jID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdzb3VyY2VTdGFydENSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihcbiAgICAgIHNvdXJjZVN0YXJ0Q1JMb2MsIHNvdXJjZVN0YXJ0Um93Q29sWzFdLCBzb3VyY2VTdGFydFJvd0NvbFswXSk7XG4gIGNvbnN0IGRlc3RTdGFydENSTG9jID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdkZXN0U3RhcnRDUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYoZGVzdFN0YXJ0Q1JMb2MsIGRlc3RTdGFydFJvd0NvbFsxXSwgZGVzdFN0YXJ0Um93Q29sWzBdKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0RXhwVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gJ2dsX0ZyYWdDb2xvciA9IHZlYzQoZXhwKHZhbHVlKSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRFeHBVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZXhwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGV4cFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBleHBQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkRXhwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0RXhwVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgZ3BncHVfdXRpbCBmcm9tICcuL2dwZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgdGV4X3V0aWwgZnJvbSAnLi90ZXhfdXRpbCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmltcG9ydCB7V2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbn0gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGNsYXNzIEdQR1BVQ29udGV4dCB7XG4gIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIHRleHR1cmVGbG9hdEV4dGVuc2lvbjoge307XG4gIGNvbG9yQnVmZmVyRmxvYXRFeHRlbnNpb246IHt9O1xuICBsb3NlQ29udGV4dEV4dGVuc2lvbjogV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgdmVydGV4QnVmZmVyOiBXZWJHTEJ1ZmZlcjtcbiAgaW5kZXhCdWZmZXI6IFdlYkdMQnVmZmVyO1xuICBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcjtcbiAgb3V0cHV0VGV4dHVyZTogV2ViR0xUZXh0dXJlfG51bGwgPSBudWxsO1xuICBwcm9ncmFtOiBXZWJHTFByb2dyYW18bnVsbCA9IG51bGw7XG4gIHByaXZhdGUgZGlzcG9zZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBhdXRvRGVidWdWYWxpZGF0ZSA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKGdsPzogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gICAgaWYgKGdsICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZ2wgPSBnbDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5nbCA9IGdwZ3B1X3V0aWwuY3JlYXRlV2ViR0xDb250ZXh0KCk7XG4gICAgfVxuXG4gICAgLy8gV2ViR0wgMi4wIGVuYWJsZXMgdGV4dHVyZSBmbG9hdHMgd2l0aG91dCBhbiBleHRlbnNpb24uXG4gICAgaWYgKCF3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgICB0aGlzLnRleHR1cmVGbG9hdEV4dGVuc2lvbiA9XG4gICAgICAgICAgd2ViZ2xfdXRpbC5nZXRFeHRlbnNpb25PclRocm93KHRoaXMuZ2wsICdPRVNfdGV4dHVyZV9mbG9hdCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNvbG9yQnVmZmVyRmxvYXRFeHRlbnNpb24gPVxuICAgICAgICAgIHdlYmdsX3V0aWwuZ2V0RXh0ZW5zaW9uT3JUaHJvdyh0aGlzLmdsLCAnRVhUX2NvbG9yX2J1ZmZlcl9mbG9hdCcpO1xuICAgIH1cblxuICAgIHRoaXMubG9zZUNvbnRleHRFeHRlbnNpb24gPVxuICAgICAgICB3ZWJnbF91dGlsLmdldEV4dGVuc2lvbk9yVGhyb3codGhpcy5nbCwgJ1dFQkdMX2xvc2VfY29udGV4dCcpIGFzXG4gICAgICAgIFdlYkdMTG9zZUNvbnRleHRFeHRlbnNpb247XG4gICAgdGhpcy52ZXJ0ZXhCdWZmZXIgPSBncGdwdV91dGlsLmNyZWF0ZVZlcnRleEJ1ZmZlcih0aGlzLmdsKTtcbiAgICB0aGlzLmluZGV4QnVmZmVyID0gZ3BncHVfdXRpbC5jcmVhdGVJbmRleEJ1ZmZlcih0aGlzLmdsKTtcbiAgICB0aGlzLmZyYW1lYnVmZmVyID0gd2ViZ2xfdXRpbC5jcmVhdGVGcmFtZWJ1ZmZlcih0aGlzLmdsKTtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwb3NlKCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSAhPSBudWxsKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ0Rpc3Bvc2luZyBhIEdQR1BVQ29udGV4dCB0aGF0IHN0aWxsIGhhcyBhIGJvdW5kIFdlYkdMUHJvZ3JhbS4nICtcbiAgICAgICAgICAnIFRoaXMgaXMgcHJvYmFibHkgYSByZXNvdXJjZSBsZWFrLCBkZWxldGUgdGhlIHByb2dyYW0gd2l0aCAnICtcbiAgICAgICAgICAnR1BHUFVDb250ZXh0LmRlbGV0ZVByb2dyYW0gYmVmb3JlIGRpc3Bvc2luZy4nKTtcbiAgICB9XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgJ0Rpc3Bvc2luZyBhIEdQR1BVQ29udGV4dCB0aGF0IHN0aWxsIGhhcyBhIGJvdW5kIG91dHB1dCBtYXRyaXggJyArXG4gICAgICAgICAgJ3RleHR1cmUuICBUaGlzIGlzIHByb2JhYmx5IGEgcmVzb3VyY2UgbGVhaywgZGVsZXRlIHRoZSBvdXRwdXQgJyArXG4gICAgICAgICAgJ21hdHJpeCB0ZXh0dXJlIHdpdGggR1BHUFVDb250ZXh0LmRlbGV0ZU1hdHJpeFRleHR1cmUgYmVmb3JlICcgK1xuICAgICAgICAgICdkaXNwb3NpbmcuJyk7XG4gICAgfVxuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZmluaXNoKCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIG51bGwpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlRnJhbWVidWZmZXIodGhpcy5mcmFtZWJ1ZmZlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVCdWZmZXIodGhpcy52ZXJ0ZXhCdWZmZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIG51bGwpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlQnVmZmVyKHRoaXMuaW5kZXhCdWZmZXIpKTtcbiAgICB0aGlzLmxvc2VDb250ZXh0RXh0ZW5zaW9uLmxvc2VDb250ZXh0KCk7XG4gICAgdGhpcy5kaXNwb3NlZCA9IHRydWU7XG4gIH1cblxuICBwdWJsaWMgZW5hYmxlQXV0b21hdGljRGVidWdWYWxpZGF0aW9uKGVuYWJsZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlID0gZW5hYmxlZDtcbiAgICB3ZWJnbF91dGlsLmVuYWJsZURlYnVnV2ViR0xFcnJvckNoZWNraW5nKGVuYWJsZWQpO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZU1hdHJpeFRleHR1cmUocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwuY3JlYXRlTWF0cml4VGV4dHVyZSh0aGlzLmdsLCByb3dzLCBjb2x1bW5zKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGxvYWRQaXhlbERhdGFUb1RleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgICBwaXhlbHM6IEltYWdlRGF0YXxIVE1MSW1hZ2VFbGVtZW50fEhUTUxDYW52YXNFbGVtZW50fEhUTUxWaWRlb0VsZW1lbnQpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGdwZ3B1X3V0aWwudXBsb2FkUGl4ZWxEYXRhVG9UZXh0dXJlKHRoaXMuZ2wsIHRleHR1cmUsIHBpeGVscyk7XG4gIH1cblxuICBwdWJsaWMgY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZShyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6XG4gICAgICBXZWJHTFRleHR1cmUge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwuY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZSh0aGlzLmdsLCByb3dzLCBjb2x1bW5zKTtcbiAgfVxuXG4gIHB1YmxpYyBkZWxldGVNYXRyaXhUZXh0dXJlKHRleHR1cmU6IFdlYkdMVGV4dHVyZSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSA9PT0gdGV4dHVyZSkge1xuICAgICAgd2ViZ2xfdXRpbC51bmJpbmRDb2xvclRleHR1cmVGcm9tRnJhbWVidWZmZXIodGhpcy5nbCwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgICB0aGlzLm91dHB1dFRleHR1cmUgPSBudWxsO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLmRlbGV0ZVRleHR1cmUodGV4dHVyZSkpO1xuICB9XG5cbiAgcHVibGljIHVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgbnVtQ2hhbm5lbHMgPSAxO1xuICAgIHJldHVybiBncGdwdV91dGlsLnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgICAgdGhpcy5nbCwgdGV4dHVyZSwgcm93cywgY29sdW1ucywgbWF0cml4LCBudW1DaGFubmVscyk7XG4gIH1cblxuICBwdWJsaWMgdXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICAgIG1hdHJpeDogRmxvYXQzMkFycmF5KSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC51cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgICAgIHRoaXMuZ2wsIHRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIG1hdHJpeCk7XG4gIH1cblxuICBwdWJsaWMgZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICAgIHJldHVybiB0aGlzLmRvd25sb2FkTWF0cml4RHJpdmVyKFxuICAgICAgICB0ZXh0dXJlLFxuICAgICAgICAoKSA9PlxuICAgICAgICAgICAgZ3BncHVfdXRpbC5kb3dubG9hZE1hdHJpeEZyb21PdXRwdXRUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpKTtcbiAgfVxuXG4gIHB1YmxpYyBkb3dubG9hZE1hdHJpeEZyb21QYWNrZWRUZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICAgIHRleHR1cmUsXG4gICAgICAgICgpID0+IGdwZ3B1X3V0aWwuZG93bmxvYWRNYXRyaXhGcm9tUGFja2VkT3V0cHV0VGV4dHVyZShcbiAgICAgICAgICAgIHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpKTtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFByb2dyYW0ge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIGNvbnN0IGZyYWdtZW50U2hhZGVyOiBXZWJHTFNoYWRlciA9XG4gICAgICAgIHdlYmdsX3V0aWwuY3JlYXRlRnJhZ21lbnRTaGFkZXIoZ2wsIGZyYWdtZW50U2hhZGVyU291cmNlKTtcbiAgICBjb25zdCB2ZXJ0ZXhTaGFkZXI6IFdlYkdMU2hhZGVyID0gZ3BncHVfdXRpbC5jcmVhdGVWZXJ0ZXhTaGFkZXIoZ2wpO1xuICAgIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IHdlYmdsX3V0aWwuY3JlYXRlUHJvZ3JhbShnbCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmF0dGFjaFNoYWRlcihwcm9ncmFtLCB2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIGZyYWdtZW50U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5saW5rUHJvZ3JhbShnbCwgcHJvZ3JhbSk7XG4gICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVQcm9ncmFtKGdsLCBwcm9ncmFtKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRldGFjaFNoYWRlcihwcm9ncmFtLCB2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlU2hhZGVyKHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZXRhY2hTaGFkZXIocHJvZ3JhbSwgZnJhZ21lbnRTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGVsZXRlU2hhZGVyKGZyYWdtZW50U2hhZGVyKSk7XG4gICAgcmV0dXJuIHByb2dyYW07XG4gIH1cblxuICBwdWJsaWMgZGVsZXRlUHJvZ3JhbShwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGlmIChwcm9ncmFtID09PSB0aGlzLnByb2dyYW0pIHtcbiAgICAgIHRoaXMucHJvZ3JhbSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChwcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKSk7XG4gICAgfVxuICB9XG5cbiAgcHVibGljIHNldFByb2dyYW0ocHJvZ3JhbTogV2ViR0xQcm9ncmFtfG51bGwpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMucHJvZ3JhbSA9IHByb2dyYW07XG4gICAgaWYgKCh0aGlzLnByb2dyYW0gIT0gbnVsbCkgJiYgdGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZVByb2dyYW0odGhpcy5nbCwgdGhpcy5wcm9ncmFtKTtcbiAgICB9XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC51c2VQcm9ncmFtKHByb2dyYW0pKTtcbiAgfVxuXG4gIHB1YmxpYyBnZXRVbmlmb3JtTG9jYXRpb24odW5pZm9ybU5hbWU6IHN0cmluZyk6IFdlYkdMVW5pZm9ybUxvY2F0aW9uIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIHJldHVybiB3ZWJnbF91dGlsLmdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KFxuICAgICAgICB0aGlzLmdsLCB0aGlzLnByb2dyYW0hLCB1bmlmb3JtTmFtZSk7XG4gIH1cblxuICBwdWJsaWMgc2V0SW5wdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgaW5wdXRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHVuaWZvcm1OYW1lOiBzdHJpbmcsXG4gICAgICB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnRocm93SWZOb1Byb2dyYW0oKTtcbiAgICB3ZWJnbF91dGlsLmJpbmRUZXh0dXJlVG9Qcm9ncmFtVW5pZm9ybVNhbXBsZXIoXG4gICAgICAgIHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSEsIGlucHV0TWF0cml4VGV4dHVyZSwgdW5pZm9ybU5hbWUsIHRleHR1cmVVbml0KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgb3V0cHV0TWF0cml4VGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcikge1xuICAgIHRoaXMuc2V0T3V0cHV0TWF0cml4VGV4dHVyZURyaXZlcihvdXRwdXRNYXRyaXhUZXh0dXJlLCBjb2x1bW5zLCByb3dzKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRQYWNrZWRNYXRyaXhUZXh0dXJlKFxuICAgICAgb3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKG91dHB1dFBhY2tlZE1hdHJpeFRleHR1cmUsIHdpZHRoLCBoZWlnaHQpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgc3RhcnRSb3c6IG51bWJlciwgbnVtUm93czogbnVtYmVyLCBzdGFydENvbHVtbjogbnVtYmVyLFxuICAgICAgbnVtQ29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbkRyaXZlcihcbiAgICAgICAgc3RhcnRDb2x1bW4sIHN0YXJ0Um93LCBudW1Db2x1bW5zLCBudW1Sb3dzKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRPdXRwdXRQYWNrZWRNYXRyaXhXcml0ZVJlZ2lvbihcbiAgICAgIHN0YXJ0Um93OiBudW1iZXIsIG51bVJvd3M6IG51bWJlciwgc3RhcnRDb2x1bW46IG51bWJlcixcbiAgICAgIG51bUNvbHVtbnM6IG51bWJlcikge1xuICAgIHRocm93IG5ldyBFcnJvcignc2V0T3V0cHV0UGFja2VkTWF0cml4V3JpdGVSZWdpb24gbm90IGltcGxlbWVudGVkLicpO1xuICB9XG5cbiAgcHVibGljIGRlYnVnVmFsaWRhdGUoKSB7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSAhPSBudWxsKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlUHJvZ3JhbSh0aGlzLmdsLCB0aGlzLnByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIodGhpcy5nbCk7XG4gIH1cblxuICBwdWJsaWMgZXhlY3V0ZVByb2dyYW0oKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB0aGlzLnRocm93SWZOb1Byb2dyYW0oKTtcbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgZ3BncHVfdXRpbC5iaW5kVmVydGV4UHJvZ3JhbUF0dHJpYnV0ZVN0cmVhbXMoXG4gICAgICAgIGdsLCB0aGlzLnByb2dyYW0hLCB0aGlzLnZlcnRleEJ1ZmZlcik7XG4gICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgIHRoaXMuZGVidWdWYWxpZGF0ZSgpO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgZ2wsICgpID0+IGdsLmRyYXdFbGVtZW50cyhnbC5UUklBTkdMRVMsIDYsIGdsLlVOU0lHTkVEX1NIT1JULCAwKSk7XG4gIH1cblxuICBwdWJsaWMgYmxvY2tVbnRpbEFsbFByb2dyYW1zQ29tcGxldGVkKCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5maW5pc2goKSk7XG4gIH1cblxuICBwcml2YXRlIGRvd25sb2FkTWF0cml4RHJpdmVyKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLFxuICAgICAgZG93bmxvYWRBbmREZWNvZGU6ICgpID0+IEZsb2F0MzJBcnJheSk6IEZsb2F0MzJBcnJheSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICB0aGlzLmdsLCB0ZXh0dXJlLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICBjb25zdCByZXN1bHQgPSBkb3dubG9hZEFuZERlY29kZSgpO1xuICAgIGlmICh0aGlzLm91dHB1dFRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgd2ViZ2xfdXRpbC5iaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICAgICAgICB0aGlzLmdsLCB0aGlzLm91dHB1dFRleHR1cmUsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgICAgaWYgKHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUpIHtcbiAgICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZUZyYW1lYnVmZmVyKHRoaXMuZ2wpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB3ZWJnbF91dGlsLnVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcih0aGlzLmdsLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgc2V0T3V0cHV0TWF0cml4VGV4dHVyZURyaXZlcihcbiAgICAgIG91dHB1dE1hdHJpeFRleHR1cmVNYXliZVBhY2tlZDogV2ViR0xUZXh0dXJlLCB3aWR0aDogbnVtYmVyLFxuICAgICAgaGVpZ2h0OiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICBnbCwgb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZUZyYW1lYnVmZmVyKGdsKTtcbiAgICB9XG4gICAgdGhpcy5vdXRwdXRUZXh0dXJlID0gb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC52aWV3cG9ydCgwLCAwLCB3aWR0aCwgaGVpZ2h0KSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNjaXNzb3IoMCwgMCwgd2lkdGgsIGhlaWdodCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRPdXRwdXRNYXRyaXhXcml0ZVJlZ2lvbkRyaXZlcihcbiAgICAgIHg6IG51bWJlciwgeTogbnVtYmVyLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICAgIHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuc2Npc3Nvcih4LCB5LCB3aWR0aCwgaGVpZ2h0KSk7XG4gIH1cblxuICBwcml2YXRlIHRocm93SWZEaXNwb3NlZCgpIHtcbiAgICBpZiAodGhpcy5kaXNwb3NlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgdG8gdXNlIGRpc3Bvc2VkIEdQR1BVQ29udGV4dC4nKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHRocm93SWZOb1Byb2dyYW0oKSB7XG4gICAgaWYgKHRoaXMucHJvZ3JhbSA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIEdQVSBwcm9ncmFtIGlzIGN1cnJlbnRseSBzZXQuJyk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIHRleF91dGlsIGZyb20gJy4vdGV4X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0V2ViR0xDb250ZXh0QXR0cmlidXRlcygpOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzIHtcbiAgcmV0dXJuIHtcbiAgICBhbHBoYTogZmFsc2UsXG4gICAgYW50aWFsaWFzOiBmYWxzZSxcbiAgICBwcmVtdWx0aXBsaWVkQWxwaGE6IGZhbHNlLFxuICAgIHByZXNlcnZlRHJhd2luZ0J1ZmZlcjogZmFsc2UsXG4gICAgZGVwdGg6IGZhbHNlLFxuICAgIHN0ZW5jaWw6IGZhbHNlLFxuICAgIGZhaWxJZk1ham9yUGVyZm9ybWFuY2VDYXZlYXQ6IHRydWVcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMQ29udGV4dChjYW52YXM/OiBIVE1MQ2FudmFzRWxlbWVudCkge1xuICBjb25zdCBhdHRyaWJ1dGVzID0gZ2V0V2ViR0xDb250ZXh0QXR0cmlidXRlcygpO1xuICBsZXQgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgaWYgKGNhbnZhcyAhPSBudWxsKSB7XG4gICAgZ2wgPSB3ZWJnbF91dGlsLmNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoY2FudmFzLCBhdHRyaWJ1dGVzKTtcbiAgfSBlbHNlIHtcbiAgICBnbCA9IHdlYmdsX3V0aWwuY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0KGF0dHJpYnV0ZXMpO1xuICB9XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkRFUFRIX1RFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuU1RFTkNJTF9URVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkJMRU5EKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLkRJVEhFUikpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5QT0xZR09OX09GRlNFVF9GSUxMKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLlNBTVBMRV9DT1ZFUkFHRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlKGdsLlNDSVNTT1JfVEVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlKGdsLkNVTExfRkFDRSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuY3VsbEZhY2UoZ2wuQkFDSykpO1xuICByZXR1cm4gZ2w7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhTaGFkZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMU2hhZGVyIHtcbiAgY29uc3QgdmVydGV4U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICBhdHRyaWJ1dGUgdmVjMyBjbGlwU3BhY2VQb3M7XG4gICAgYXR0cmlidXRlIHZlYzIgdXY7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZ2xfUG9zaXRpb24gPSB2ZWM0KGNsaXBTcGFjZVBvcywgMSk7XG4gICAgICByZXN1bHRVViA9IHV2O1xuICAgIH1gO1xuICByZXR1cm4gd2ViZ2xfdXRpbC5jcmVhdGVWZXJ0ZXhTaGFkZXIoZ2wsIHZlcnRleFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhCdWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMQnVmZmVyIHtcbiAgLy8gW3ggeSB6IHUgdl0gKiBbdXBwZXItbGVmdCwgbG93ZXItbGVmdCwgdXBwZXItcmlnaHQsIGxvd2VyLXJpZ2h0XVxuICBjb25zdCB2ZXJ0ZXhBcnJheSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICBbLTEsIDEsIDAsIDAsIDEsIC0xLCAtMSwgMCwgMCwgMCwgMSwgMSwgMCwgMSwgMSwgMSwgLTEsIDAsIDEsIDBdKTtcbiAgcmV0dXJuIHdlYmdsX3V0aWwuY3JlYXRlU3RhdGljVmVydGV4QnVmZmVyKGdsLCB2ZXJ0ZXhBcnJheSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJbmRleEJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xCdWZmZXIge1xuICAvLyBPcGVuR0wgKGFuZCBXZWJHTCkgaGF2ZSBcIkNDVyA9PSBmcm9udFwiIHdpbmRpbmdcbiAgY29uc3QgdHJpYW5nbGVWZXJ0ZXhJbmRpY2VzID0gbmV3IFVpbnQxNkFycmF5KFswLCAxLCAyLCAyLCAxLCAzXSk7XG4gIHJldHVybiB3ZWJnbF91dGlsLmNyZWF0ZVN0YXRpY0luZGV4QnVmZmVyKGdsLCB0cmlhbmdsZVZlcnRleEluZGljZXMpO1xufVxuXG5mdW5jdGlvbiBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbnVtQ2hhbm5lbHM6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgaWYgKG51bUNoYW5uZWxzID09PSA0KSB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICByZXR1cm4gKGdsIGFzIGFueSkuUkdCQTMyRjtcbiAgICB9XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIHJldHVybiAoZ2wgYXMgYW55KS5SMzJGO1xuICB9XG4gIHJldHVybiBnbC5SR0JBO1xufVxuXG5mdW5jdGlvbiBnZXRUZXh0dXJlRm9ybWF0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIG51bUNoYW5uZWxzOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAod2ViZ2xfdXRpbC5pc1dlYkdMMkVuYWJsZWQoKSAmJiBudW1DaGFubmVscyA9PT0gMSkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKGdsIGFzIGFueSkuUkVEO1xuICB9XG4gIHJldHVybiBnbC5SR0JBO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyLFxuICAgIG51bUNoYW5uZWxzOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICB3ZWJnbF91dGlsLnZhbGlkYXRlVGV4dHVyZVNpemUoZ2wsIHdpZHRoLCBoZWlnaHQpO1xuICBjb25zdCB0ZXh0dXJlID0gd2ViZ2xfdXRpbC5jcmVhdGVUZXh0dXJlKGdsKTtcblxuICBjb25zdCB0ZXgyZCA9IGdsLlRFWFRVUkVfMkQ7XG4gIGNvbnN0IGludGVybmFsRm9ybWF0ID0gZ2V0VGV4dHVyZUludGVybmFsRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG4gIGNvbnN0IGZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKHRleDJkLCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfV1JBUF9TLCBnbC5DTEFNUF9UT19FREdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfV1JBUF9ULCBnbC5DTEFNUF9UT19FREdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfTUlOX0ZJTFRFUiwgZ2wuTkVBUkVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC50ZXhQYXJhbWV0ZXJpKHRleDJkLCBnbC5URVhUVVJFX01BR19GSUxURVIsIGdsLk5FQVJFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnRleEltYWdlMkQoXG4gICAgICAgICAgdGV4MmQsIDAsIGludGVybmFsRm9ybWF0LCB3aWR0aCwgaGVpZ2h0LCAwLCBmb3JtYXQsIGdsLkZMT0FULCBudWxsKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG4gIHJldHVybiB0ZXh0dXJlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDE7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVDb2xvck1hdHJpeFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBXZWJHTFRleHR1cmUge1xuICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0Q29sb3JNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICByZXR1cm4gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShnbCwgd2lkdGgsIGhlaWdodCwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUGFja2VkTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgbnVtQ2hhbm5lbHMgPSA0O1xuICByZXR1cm4gY3JlYXRlQW5kQ29uZmlndXJlVGV4dHVyZShnbCwgd2lkdGgsIGhlaWdodCwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFZlcnRleFByb2dyYW1BdHRyaWJ1dGVTdHJlYW1zKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICB2ZXJ0ZXhCdWZmZXI6IFdlYkdMQnVmZmVyKSB7XG4gIGNvbnN0IHBvc09mZnNldCA9IDA7ICAgICAgICAgICAgICAgLy8geCBpcyB0aGUgZmlyc3QgYnVmZmVyIGVsZW1lbnRcbiAgY29uc3QgdXZPZmZzZXQgPSAzICogNDsgICAgICAgICAgICAvLyB1diBjb21lcyBhZnRlciBbeCB5IHpdXG4gIGNvbnN0IHN0cmlkZSA9ICgzICogNCkgKyAoMiAqIDQpOyAgLy8geHl6ICsgdXYsIGVhY2ggZW50cnkgaXMgNC1ieXRlIGZsb2F0LlxuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgdmVydGV4QnVmZmVyKSk7XG4gIHdlYmdsX3V0aWwuYmluZFZlcnRleEJ1ZmZlclRvUHJvZ3JhbUF0dHJpYnV0ZShcbiAgICAgIGdsLCBwcm9ncmFtLCAnY2xpcFNwYWNlUG9zJywgdmVydGV4QnVmZmVyLCAzLCBzdHJpZGUsIHBvc09mZnNldCk7XG4gIHRyeSB7XG4gICAgd2ViZ2xfdXRpbC5iaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgICAgICBnbCwgcHJvZ3JhbSwgJ3V2JywgdmVydGV4QnVmZmVyLCAyLCBzdHJpZGUsIHV2T2Zmc2V0KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIC8vIFByb2dyYW1zIHdpdGggMXgxIG91dHB1dCB0ZXh0dXJlcyBkb24ndCB1c2UgdGhlIHV2IGF0dHJpYnV0ZS5cbiAgICAvLyBUaGlzIGNhbiBjYXVzZSB0aGUgc2hhZGVyIGxpbmtlciB0byBkZWFkLXN0cmlwIGl0LCBzbyB3ZSBzaG91bGRuJ3RcbiAgICAvLyBjb21wbGFpbiBvciBmYWlsIGlmIGl0J3Mgbm90IHByZXNlbnQuXG4gICAgaWYgKCFlLmhhc093blByb3BlcnR5KCduYW1lZFZlcnRleEF0dHJpYnV0ZU5vdEZvdW5kJykpIHtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRQaXhlbERhdGFUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLFxuICAgIHBpeGVsczogSW1hZ2VEYXRhfEhUTUxJbWFnZUVsZW1lbnR8SFRNTENhbnZhc0VsZW1lbnR8SFRNTFZpZGVvRWxlbWVudCkge1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIGNvbnN0IGludGVybmFsRm9ybWF0ID0gZ2V0VGV4dHVyZUludGVybmFsRm9ybWF0KGdsLCBudW1DaGFubmVscyk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC50ZXhJbWFnZTJEKFxuICAgICAgICAgIGdsLlRFWFRVUkVfMkQsIDAsIGludGVybmFsRm9ybWF0LCBnbC5SR0JBLCBnbC5GTE9BVCwgcGl4ZWxzKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmZ1bmN0aW9uIHVwbG9hZERhdGFUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCB3aWR0aDogbnVtYmVyLFxuICAgIGhlaWdodDogbnVtYmVyLCBkYXRhOiBGbG9hdDMyQXJyYXksIG51bUNoYW5uZWxzOiBudW1iZXIpIHtcbiAgY29uc3QgdGV4dHVyZUZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcblxuICB3ZWJnbF91dGlsLnZhbGlkYXRlVGV4dHVyZVNpemUoZ2wsIHdpZHRoLCBoZWlnaHQpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUoZ2wuVEVYVFVSRV8yRCwgdGV4dHVyZSkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudGV4U3ViSW1hZ2UyRChcbiAgICAgICAgICBnbC5URVhUVVJFXzJELCAwLCAwLCAwLCB3aWR0aCwgaGVpZ2h0LCB0ZXh0dXJlRm9ybWF0LCBnbC5GTE9BVCxcbiAgICAgICAgICBkYXRhKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUb1RleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCBtYXRyaXg6IEZsb2F0MzJBcnJheSwgbnVtQ2hhbm5lbHM6IG51bWJlcikge1xuICBjb25zdCBbdywgaF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPVxuICAgICAgbnVtQ2hhbm5lbHMgPT09IDEgPyB3ZWJnbF91dGlsLmdldENoYW5uZWxzUGVyVGV4dHVyZSgpIDogbnVtQ2hhbm5lbHM7XG4gIGNvbnN0IHVucGFja2VkQXJyYXkgPVxuICAgICAgbmV3IEZsb2F0MzJBcnJheSh0ZXhfdXRpbC5nZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgICAgICAgIG1hdHJpeC5sZW5ndGgsIGNoYW5uZWxzUGVyVGV4dHVyZSkpO1xuICB0ZXhfdXRpbC5lbmNvZGVNYXRyaXhUb1VucGFja2VkQXJyYXkoXG4gICAgICBtYXRyaXgsIHVucGFja2VkQXJyYXksIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG5cbiAgdXBsb2FkRGF0YVRvVGV4dHVyZShnbCwgdGV4dHVyZSwgdywgaCwgdW5wYWNrZWRBcnJheSwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTWF0cml4VG9QYWNrZWRUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLFxuICAgIGNvbHVtbnM6IG51bWJlciwgbWF0cml4OiBGbG9hdDMyQXJyYXkpIHtcbiAgY29uc3QgW3csIGhdID0gdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHBhY2tlZFJHQkEgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKSk7XG4gIHRleF91dGlsLmVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShtYXRyaXgsIHJvd3MsIGNvbHVtbnMsIHBhY2tlZFJHQkEpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHVwbG9hZERhdGFUb1RleHR1cmUoZ2wsIHRleHR1cmUsIHcsIGgsIHBhY2tlZFJHQkEsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRvd25sb2FkTWF0cml4RnJvbU91dHB1dFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBbdywgaF0gPVxuICAgICAgdGV4X3V0aWwuZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICBjb25zdCBjaGFubmVsc1BlclRleHR1cmUgPSA0O1xuICBjb25zdCB1bnBhY2tlZEFycmF5ID1cbiAgICAgIG5ldyBGbG9hdDMyQXJyYXkodGV4X3V0aWwuZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICAgICAgICByb3dzICogY29sdW1ucywgY2hhbm5lbHNQZXJUZXh0dXJlKSk7XG4gIGNvbnN0IHRleHR1cmVGb3JtYXQgPSBnZXRUZXh0dXJlRm9ybWF0KGdsLCBjaGFubmVsc1BlclRleHR1cmUpO1xuXG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnJlYWRQaXhlbHMoMCwgMCwgdywgaCwgZ2wuUkdCQSwgZ2wuRkxPQVQsIHVucGFja2VkQXJyYXkpKTtcblxuICBjb25zdCBtYXRyaXggPSBuZXcgRmxvYXQzMkFycmF5KHJvd3MgKiBjb2x1bW5zKTtcbiAgdGV4X3V0aWwuZGVjb2RlTWF0cml4RnJvbVVucGFja2VkQXJyYXkoXG4gICAgICB1bnBhY2tlZEFycmF5LCBtYXRyaXgsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIHJldHVybiBtYXRyaXg7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkb3dubG9hZE1hdHJpeEZyb21QYWNrZWRPdXRwdXRUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgW3csIGhdID0gdGV4X3V0aWwuZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHBhY2tlZFJHQkEgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgdGV4X3V0aWwuZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnJlYWRQaXhlbHMoMCwgMCwgdywgaCwgZ2wuUkdCQSwgZ2wuRkxPQVQsIHBhY2tlZFJHQkEpKTtcbiAgY29uc3QgbWF0cml4ID0gbmV3IEZsb2F0MzJBcnJheShyb3dzICogY29sdW1ucyk7XG4gIHJldHVybiB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tUGFja2VkUkdCQShwYWNrZWRSR0JBLCByb3dzLCBjb2x1bW5zLCBtYXRyaXgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRMb2dVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNChsb2codmFsdWUpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldExvZ1VuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2coXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbG9nUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIGxvZ1Byb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRMb2dEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRMb2dVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGFEaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBhTWF4ID0gdGV4dHVyZTJEKG1hdHJpeEEsIGhhbGZDUiAvIGFEaW1DUikucjtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGFDdXIgPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgICAgYU1heCA9IG1heChhTWF4LCBhQ3VyKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmbG9hdCBleHBTdW0gPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGMsIHIpICsgaGFsZkNSKSAvIGFEaW1DUjtcbiAgICAgICAgICBmbG9hdCBhQ3VyID0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICAgIGV4cFN1bSArPSBleHAoYUN1ciAtIGFNYXgpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoYU1heCArIGxvZyhleHBTdW0pLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1N1bUV4cChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBsb2dTdW1FeHBQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKGxvZ1N1bUV4cFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZExvZ1N1bUV4cERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgcHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucykpO1xuICBjb25zdCBhVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHJlc3VsdFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICBsb2dTdW1FeHAoZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCAxLCAxKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdFswXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbEJhY2twcm9wKFxuICAgIGR5U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgIG9yaWdQYWQ6IG51bWJlcikge1xuICBjb25zdCBvcmlnSW5wdXREZXB0aCA9IGR5U2hhcGVSQ0RbMl07XG4gIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gIGNvbnN0IFtkeVJvd3MsIGR5Q29scywgZGVwdGhdID0gZHlTaGFwZVJDRDtcblxuICBjb25zdCBkeVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5U2hhcGVSQ0QpO1xuXG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGR5O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1heFBvcztcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiBkeVNoYXBlQ1IgPSB2ZWMyKCR7ZHlUZXhTaGFwZVJDWzFdfSwgJHtkeVRleFNoYXBlUkNbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgZHhUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEIChkeFRleFIsIGR4VGV4QykgdG8gM0QgKGR4UiwgZHhDLCBkKS5cbiAgICAgIGZsb2F0IGR4UiA9IGR4VGV4Q1IueTtcbiAgICAgIGZsb2F0IGR4QyA9IGZsb29yKGR4VGV4Q1IueCAvICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZCA9IG1vZChkeFRleENSLngsICR7b3JpZ0lucHV0RGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIGR5UkNDb3JuZXIgPSB2ZWMyKGR4UiwgZHhDKSAtIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IGR5UkNvcm5lciA9IGR5UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IGR5Q0Nvcm5lciA9IGR5UkNDb3JuZXIueTtcblxuICAgICAgLy8gQ29udm9sdmUgZHkoPywgPywgZCkgd2l0aCBwb3MgbWFzayg6LCA6LCBkKSB0byBnZXQgZHgoeVIsIGR4QywgZCkuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuXG4gICAgICAgIGZsb2F0IGR5UiA9IChkeVJDb3JuZXIgKyB3UikgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgIC8vIFRPRE8obnN0aG9yYXQpOiBTcGxpY2UgdGhpcyB3aXRoIGFub3RoZXIgdmVyc2lvbiB3aGVyZSB5b3UgY2FsbFxuICAgICAgICAvLyBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZCgpLiBIZXJlIGFuZCBiZWxvdy5cbiAgICAgICAgaWYgKGR5UiA8IDAuMCB8fCBkeVIgPj0gJHtkeVJvd3N9LjAgfHwgZnJhY3QoZHlSKSA+IDAuMCkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgZmxvYXQgZHlUZXhSID0gZHlSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG5cbiAgICAgICAgICBmbG9hdCBkeUMgPSAoZHlDQ29ybmVyICsgd0MpIC8gJHtvcmlnU3RyaWRlfS4wO1xuICAgICAgICAgIGlmIChkeUMgPCAwLjAgfHwgZHlDID49ICR7ZHlDb2xzfS4wIHx8IGZyYWN0KGR5QykgPiAwLjApIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IGR5VGV4QyA9IGR5QyAqICR7ZGVwdGh9LjAgKyBkO1xuXG4gICAgICAgICAgLy8gUmVhZCBkeShkeVIsIGR5QywgZCkuXG4gICAgICAgICAgdmVjMiBkeVVWID0gKHZlYzIoZHlUZXhDLCBkeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgLy8gUmVhZCBtYXhQb3MoZHlSLCBkeUMsIGQpLlxuICAgICAgICAgIGZsb2F0IG1heFBvc1ZhbHVlID1cbiAgICAgICAgICAgICAgJHtmU2l6ZSAqIGZTaXplIC0gMX0uMCAtIHRleHR1cmUyRChtYXhQb3MsIGR5VVYpLnI7XG5cbiAgICAgICAgICAvLyBHZXQgdGhlIGN1cnJlbnQgdmFsdWUsIGNoZWNrIGl0IGFnYWluc3QgdGhlIHZhbHVlIGZyb20gdGhlXG4gICAgICAgICAgLy8gcG9zaXRpb24gbWF0cml4LlxuICAgICAgICAgIGZsb2F0IGN1clBvc1ZhbHVlID0gd1IgKiAke2ZTaXplfS4wICsgd0M7XG4gICAgICAgICAgZmxvYXQgbWFzayA9IGZsb2F0KG1heFBvc1ZhbHVlID09IGN1clBvc1ZhbHVlID8gMS4wIDogMC4wKTtcblxuICAgICAgICAgIGRvdFByb2QgKz0gZHlWYWx1ZSAqIG1hc2s7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXhQb29sQmFja3Byb3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBkeVRleDogV2ViR0xUZXh0dXJlLFxuICAgIG1heFBvc2l0aW9uc1RleDogV2ViR0xUZXh0dXJlLCByZXN1bHRUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlUkNbMF0sIHJlc3VsdFRleFNoYXBlUkNbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoZHlUZXgsICdkeScsIDApO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUobWF4UG9zaXRpb25zVGV4LCAnbWF4UG9zJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgcG9vbF9ncHUgZnJvbSAnLi9wb29sX2dwdSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xQb3NpdGlvbnNTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgdHJ1ZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgZmFsc2UpO1xufVxuXG5mdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1heFBvb2xDb21tb25Tb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyLCBjb21wdXRlTWF4UG9zaXRpb25zOiBib29sZWFuKSB7XG4gIHJldHVybiBwb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlclBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21heCcsIGNvbXB1dGVNYXhQb3NpdGlvbnMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWF4UG9vbENvbW1vbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgcG9vbF9ncHUucG9vbENvbW1vbihncGdwdSwgcHJvZ3JhbSwgeCwgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vcG9vbF9ncHUnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNaW5Qb29sU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdtaW4nLCBmYWxzZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtaW5Qb29sKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBjb21wT3A6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdmFyeWluZyB2ZWMyIG91dHB1dENvbHVtblJvdztcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4QSwgaGFsZkNSIC8gYURpbUNSKS5yO1xuICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgYURpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGFEaW1DUi54OyBjICs9IDEuMCkge1xuICAgICAgICAgIHZlYzIgY3IgPSB2ZWMyKGMsIHIpO1xuICAgICAgICAgIHZlYzIgdXYgPSAoY3IgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGNhbmRpZGF0ZSA9IHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICAgICAgICBpZiAoaXNOYU4oY2FuZGlkYXRlKSkge1xuICAgICAgICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChjYW5kaWRhdGUsIDAsIDAsIDApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YWx1ZSA9ICR7Y29tcE9wfSh2YWx1ZSwgY2FuZGlkYXRlKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNaW5GcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnbWluJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNYXhGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IHN0cmluZyB7XG4gIHJldHVybiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zLCAnbWF4Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtaW5NYXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWluTWF4UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCAxLCAxKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtaW5NYXhQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb259IGZyb20gJy4uL21hdGgnO1xuaW1wb3J0IHtBcnJheTJEfSBmcm9tICcuLi9uZGFycmF5JztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBzaGFkZXJfY29tcGlsZXIgZnJvbSAnLi9zaGFkZXJfY29tcGlsZXInO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXIoXG4gICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgb3V0OiBBcnJheTJELCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLFxuICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBzdHJpbmcge1xuICBjb25zdCBzaGFyZWREaW0gPVxuICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUiA/IGEuc2hhcGVbMV0gOiBhLnNoYXBlWzBdKTtcbiAgY29uc3QgYVNuaXBwZXQgPVxuICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyAnYVJvdywgaScgOiAnaSwgYVJvdyc7XG4gIGNvbnN0IGJTbmlwcGV0ID1cbiAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gJ2ksIGJDb2wnIDogJ2JDb2wsIGknO1xuXG4gIGNvbnN0IGlucHV0cyA9IFt7bmFtZTogJ21hdHJpeEEnLCBhcnJheTogYX0sIHtuYW1lOiAnbWF0cml4QicsIGFycmF5OiBifV07XG4gIGNvbnN0IHVzZXJDb2RlID0gYFxuICAgIGNvbnN0IGZsb2F0IHNoYXJlZERpbSA9ICR7c2hhcmVkRGltfS4wO1xuXG4gICAgZmxvYXQgZG90QVJvd0JDb2woZmxvYXQgYVJvdywgZmxvYXQgYkNvbCkge1xuICAgICAgZmxvYXQgcmVzdWx0ID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCBpID0gMC4wOyBpIDwgc2hhcmVkRGltOyBpICs9IDEuMCkge1xuICAgICAgICBmbG9hdCBhID0gZ2V0TWF0cml4QSgke2FTbmlwcGV0fSk7XG4gICAgICAgIGZsb2F0IGIgPSBnZXRNYXRyaXhCKCR7YlNuaXBwZXR9KTtcbiAgICAgICAgcmVzdWx0ICs9IChhICogYik7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgcmVzUkMgPSBnZXRPdXRwdXRDb29yZHMoKTtcbiAgICAgIHNldE91dHB1dChkb3RBUm93QkNvbChyZXNSQy54LCByZXNSQy55KSk7XG4gICAgfVxuICBgO1xuICByZXR1cm4gc2hhZGVyX2NvbXBpbGVyLm1ha2VTaGFkZXIoaW5wdXRzLCBvdXQsIHVzZXJDb2RlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG11bHRpcGx5TWF0cml4KFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIG11bHRpcGx5UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYjogV2ViR0xUZXh0dXJlLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgb3V0VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIG91dFRleFNoYXBlWzBdLCBvdXRUZXhTaGFwZVsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obXVsdGlwbHlQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiLCAnbWF0cml4QicsIDEpO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXROZWdVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNCgtdmFsdWUsIDAsIDAsIDApOyc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0TmVnVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG5lZyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLFxuICAgIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgcHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE5lZ0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldE5lZ1VuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIsIHBvb2xUeXBlOiAnbWF4J3wnbWluJ3wnYXZnJywgY29tcHV0ZVBvc2l0aW9uczogYm9vbGVhbikge1xuICBpZiAocG9vbFR5cGUgPT09ICdhdmcnICYmIGNvbXB1dGVQb3NpdGlvbnMpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBjb21wdXRlIHBvc2l0aW9ucyBmb3IgYXZlcmFnZSBwb29sLicpO1xuICB9XG5cbiAgY29uc3QgZGVwdGggPSB4U2hhcGVSQ0RbMl07XG5cbiAgY29uc3QgeFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHhTaGFwZVJDRCk7XG5cbiAgbGV0IHJldHVyblZhbHVlID0gJ21pbk1heFZhbHVlJztcbiAgaWYgKGNvbXB1dGVQb3NpdGlvbnMpIHtcbiAgICByZXR1cm5WYWx1ZSA9ICdtaW5NYXhQb3NpdGlvbic7XG4gIH0gZWxzZSBpZiAocG9vbFR5cGUgPT09ICdhdmcnKSB7XG4gICAgcmV0dXJuVmFsdWUgPSAnYXZnVmFsdWUnO1xuICB9XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgeFNoYXBlQ1IgPSB2ZWMyKCR7eFRleFNoYXBlUkNbMV19LCAke3hUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICAke0lTX05BTl9TSEFERVJfRlVOQ31cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgZDIpLlxuICAgICAgZmxvYXQgeVIgPSB5VGV4Q1IueTtcbiAgICAgIGZsb2F0IHlDID0gZmxvb3IoeVRleENSLnggLyAke2RlcHRofS4wKTtcbiAgICAgIGZsb2F0IGQgPSBtb2QoeVRleENSLngsICR7ZGVwdGh9LjApO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAqIHZlYzIoJHtzdHJpZGV9LCAke3N0cmlkZX0pIC1cbiAgICAgICAgICB2ZWMyKCR7cGFkfS4wLCAke3BhZH0uMCk7XG4gICAgICBmbG9hdCB4UkNvcm5lciA9IHhSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgeENDb3JuZXIgPSB4UkNDb3JuZXIueTtcblxuICAgICAgLy8gbWF4L21pbiB4KD8sID8sIGQpIHRvIGdldCB5KHlSLCB5QywgZCkuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZFxuICAgICAgZmxvYXQgbWluTWF4VmFsdWUgPSAwLjA7XG4gICAgICBmbG9hdCBtaW5NYXhWYWx1ZUZvdW5kID0gMC4wO1xuICAgICAgZmxvYXQgbWluTWF4UG9zaXRpb24gPSAwLjA7XG4gICAgICBmbG9hdCBhdmdWYWx1ZSA9IDAuMDtcblxuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcbiAgICAgICAgZmxvYXQgeFIgPSB4UkNvcm5lciArIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG4gICAgICAgICAgZmxvYXQgeEMgPSB4Q0Nvcm5lciArIHdDO1xuICAgICAgICAgIGZsb2F0IHhUZXhDID0geEMgKiAke2RlcHRofS4wICsgZDtcblxuICAgICAgICAgIHZlYzIgdGV4Q1IgPSB2ZWMyKHhUZXhDLCB4VGV4Uik7XG5cbiAgICAgICAgICAvLyBDaGVjayBpZiB0aGUgcmVxdWVzdGVkIFVWIGlzIGludmFsaWQuXG4gICAgICAgICAgdmVjMiB1diA9ICh0ZXhDUiArIGhhbGZDUikgLyB4U2hhcGVDUjtcbiAgICAgICAgICBib29sIGxlc3NUaGFuWmVybyA9IGFueShsZXNzVGhhbih1diwgdmVjMigwLCAwKSkpO1xuICAgICAgICAgIGJvb2wgZ3JlYXRlclRoYW5PbmUgPSBhbnkoZ3JlYXRlclRoYW4odXYsIHZlYzIoMSwgMSkpKTtcbiAgICAgICAgICBib29sIG91dHNpZGUgPSBsZXNzVGhhblplcm8gfHwgZ3JlYXRlclRoYW5PbmU7XG4gICAgICAgICAgaWYgKG91dHNpZGUpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKHgsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTih2YWx1ZSkpIHtcbiAgICAgICAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsdWUsIDAsIDAsIDApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoJHtwb29sVHlwZSA9PT0gJ2F2Zyd9KSB7XG4gICAgICAgICAgICBhdmdWYWx1ZSArPSB2YWx1ZSAvICR7ZlNpemUgKiBmU2l6ZX0uMDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gSWYgYSBtaW4gLyBtYXggdmFsdWUgaGFzIGFscmVhZHkgYmVlbiBmb3VuZCwgdXNlIGl0LiBJZiBub3QsIHVzZVxuICAgICAgICAgICAgLy8gdGhlIGN1cnJlbnQgdmFsdWUuXG4gICAgICAgICAgICBmbG9hdCBjdXJyZW50TWluTWF4VmFsdWUgPSBtaXgoXG4gICAgICAgICAgICAgICAgdmFsdWUsIG1pbk1heFZhbHVlLCBtaW5NYXhWYWx1ZUZvdW5kKTtcbiAgICAgICAgICAgIGlmICh2YWx1ZSAke3Bvb2xUeXBlID09PSAnbWluJyA/ICc8PScgOiAnPj0nfSBjdXJyZW50TWluTWF4VmFsdWUpIHtcbiAgICAgICAgICAgICAgbWluTWF4VmFsdWUgPSB2YWx1ZTtcbiAgICAgICAgICAgICAgbWluTWF4VmFsdWVGb3VuZCA9IDEuMDtcbiAgICAgICAgICAgICAgaWYgKCR7Y29tcHV0ZVBvc2l0aW9uc30pIHtcbiAgICAgICAgICAgICAgICBtaW5NYXhQb3NpdGlvbiA9IHdSICogJHtmU2l6ZX0uMCArIHdDO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCR7cmV0dXJuVmFsdWV9LCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBvb2xDb21tb24oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh4LCAneCcsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGFEaW1DUiA9IHZlYzIoJHtjb2x1bW5zfS4wLCAke3Jvd3N9LjApO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCBzdW0gPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGMsIHIpICsgaGFsZkNSKSAvIGFEaW1DUjtcbiAgICAgICAgICBzdW0gKz0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHN1bSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZWR1Y2VTdW0oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVkdWNlU3VtUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYU51bVJvd3M6IG51bWJlciwgYU51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKHJlZHVjZVN1bVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFJlZHVjZVN1bURvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgcHJvZ3JhbTogV2ViR0xQcm9ncmFtID1cbiAgICAgIGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucykpO1xuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSgxLCAxKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgcmVkdWNlU3VtKGdwZ3B1LCBwcm9ncmFtLCBhVGV4dHVyZSwgcm93cywgY29sdW1ucywgcmVzdWx0VGV4dHVyZSk7XG4gIGNvbnN0IHJlc3VsdCA9IGdwZ3B1LmRvd25sb2FkTWF0cml4RnJvbVRleHR1cmUocmVzdWx0VGV4dHVyZSwgMSwgMSlbMF07XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYVRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHJlc3VsdFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFJlbHVVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgcmVzdWx0ID0gKHZhbHVlIDwgMC4wID8gMC4wIDogdmFsdWUpO1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQocmVzdWx0LCAwLCAwLCAwKTtcbiAgYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRSZWx1VW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbHUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVsdVByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCByZWx1UHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFJlbHVEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRSZWx1VW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldFJlbmRlclJHQlNoYWRlcihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBkZXN0aW5hdGlvbldpZHRoOiBudW1iZXIpOiBXZWJHTFByb2dyYW0ge1xuICBjb25zdCBmcmFnbWVudFNoYWRlclNvdXJjZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgc291cmNlO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IGZsb2F0IGRlc3RpbmF0aW9uV2lkdGggPSAke2Rlc3RpbmF0aW9uV2lkdGh9LjA7XG4gICAgY29uc3QgZmxvYXQgYSA9IDEuMDtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHhyID0gZmxvb3IocmVzdWx0VVYucyAqIGRlc3RpbmF0aW9uV2lkdGgpICogMy4wO1xuICAgICAgdmVjMyB4ID0geHIgKyB2ZWMzKDAsIDEsIDIpO1xuXG4gICAgICBmbG9hdCBzb3VyY2VXaWR0aCA9IGRlc3RpbmF0aW9uV2lkdGggKiAzLjA7XG4gICAgICB2ZWMzIHUgPSAoeCArIDAuNSkgLyBzb3VyY2VXaWR0aDtcbiAgICAgIGZsb2F0IHYgPSAxLjAgLSByZXN1bHRVVi50O1xuXG4gICAgICBmbG9hdCByID0gdGV4dHVyZTJEKHNvdXJjZSwgdmVjMih1WzBdLCB2KSkucjtcbiAgICAgIGZsb2F0IGcgPSB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHVbMV0sIHYpKS5yO1xuICAgICAgZmxvYXQgYiA9IHRleHR1cmUyRChzb3VyY2UsIHZlYzIodVsyXSwgdikpLnI7XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQociwgZywgYiwgYSk7XG4gICAgfWA7XG5cbiAgcmV0dXJuIGdwZ3B1LmNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTb3VyY2UpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9DYW52YXMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVuZGVyU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlKSB7XG4gIHdlYmdsX3V0aWwuYmluZENhbnZhc1RvRnJhbWVidWZmZXIoZ3BncHUuZ2wpO1xuICByZW5kZXJUb0ZyYW1lYnVmZmVyKGdwZ3B1LCByZW5kZXJTaGFkZXIsIHNvdXJjZVRleCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb0ZyYW1lYnVmZmVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlbmRlclNoYWRlcjogV2ViR0xQcm9ncmFtLCBzb3VyY2VUZXg6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRQcm9ncmFtKHJlbmRlclNoYWRlcik7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShzb3VyY2VUZXgsICdzb3VyY2UnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHZlYzIgaW5wdXREaW1DUjtcbiAgICB1bmlmb3JtIHZlYzIgcmVzdWx0RGltQ1I7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHJlc3VsdENSID0gZmxvb3IocmVzdWx0VVYgKiByZXN1bHREaW1DUik7XG4gICAgICAvLyBpbmRleEluRmxhdCA9IHJvdyAqIHN0cmlkZSArIGNvbHVtbiwgd2hlcmUgc3RyaWRlID09IG51bU91dHB1dENvbHVtbnNcbiAgICAgIGZsb2F0IGluZGV4SW5GbGF0ID0gcmVzdWx0Q1IueSAqIHJlc3VsdERpbUNSLnggKyByZXN1bHRDUi54O1xuXG4gICAgICB2ZWMyIGlucHV0Q1IgPSB2ZWMyKFxuICAgICAgICBtb2QoaW5kZXhJbkZsYXQsIGlucHV0RGltQ1IueCksIC8vIGNvbCA9IGluZGV4SW5GbGF0ICUgbnVtSW5wdXRDb2x1bW5zXG4gICAgICAgIGZsb29yKGluZGV4SW5GbGF0IC8gaW5wdXREaW1DUi54KSAvLyByb3cgPSBpbmRleEluRmxhdCAvIG51bUlucHV0Q29sdW1uc1xuICAgICAgKSArIGhhbGZDUjtcblxuICAgICAgdmVjMiBpbnB1dFVWID0gaW5wdXRDUiAvIGlucHV0RGltQ1I7XG4gICAgICBnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQobWF0cml4QSwgaW5wdXRVVik7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXNoYXBlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlc2hhcGVQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHROdW1Sb3dzOiBudW1iZXIsIHJlc3VsdE51bUNvbHM6IG51bWJlcikge1xuICBjb25zdCBpbnB1dFNpemUgPSBhTnVtUm93cyAqIGFOdW1Db2xzO1xuICBjb25zdCBvdXRwdXRTaXplID0gcmVzdWx0TnVtQ29scyAqIHJlc3VsdE51bVJvd3M7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgaW5wdXRTaXplID09PSBvdXRwdXRTaXplLFxuICAgICAgYFRoZSBpbnB1dCBzaXplICgke2lucHV0U2l6ZX0pIGFuZCBvdXRwdXQgc2l6ZSAoJHtvdXRwdXRTaXplfSkgYCArXG4gICAgICAgICAgYG11c3QgbWF0Y2hgKTtcblxuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcmVzdWx0TnVtUm93cywgcmVzdWx0TnVtQ29scyk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVzaGFwZVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcblxuICBjb25zdCBpbnB1dERpbUNSTG9jYXRpb24gPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2lucHV0RGltQ1InKTtcbiAgZ3BncHUuZ2wudW5pZm9ybTJmKGlucHV0RGltQ1JMb2NhdGlvbiwgYU51bUNvbHMsIGFOdW1Sb3dzKTtcblxuICBjb25zdCByZXN1bHREaW1DUkxvY2F0aW9uID0gZ3BncHUuZ2V0VW5pZm9ybUxvY2F0aW9uKCdyZXN1bHREaW1DUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYocmVzdWx0RGltQ1JMb2NhdGlvbiwgcmVzdWx0TnVtQ29scywgcmVzdWx0TnVtUm93cyk7XG5cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgaW5wdXRTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLFxuICAgIG91dHB1dERpbWVuc2lvbnNSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIGFsaWduQ29ybmVyczogYm9vbGVhbik6IHN0cmluZyB7XG4gIGNvbnN0IGRlcHRoID0gaW5wdXRTaGFwZVJDRFsyXTtcblxuICBjb25zdCBpbnB1dFRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGlucHV0U2hhcGVSQ0QpO1xuXG4gIGNvbnN0IGVmZmVjdGl2ZUlucHV0U2hhcGVSQ0QgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgW2lucHV0U2hhcGVSQ0RbMF0gLSAxLCBpbnB1dFNoYXBlUkNEWzFdIC0gMSwgZGVwdGhdIDpcbiAgICAgIGlucHV0U2hhcGVSQ0Q7XG5cbiAgY29uc3QgZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0QgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgW291dHB1dERpbWVuc2lvbnNSb3dDb2xbMF0gLSAxLCBvdXRwdXREaW1lbnNpb25zUm93Q29sWzFdIC0gMSwgZGVwdGhdIDpcbiAgICAgIFtvdXRwdXREaW1lbnNpb25zUm93Q29sWzBdLCBvdXRwdXREaW1lbnNpb25zUm93Q29sWzFdLCBkZXB0aF07XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIGNvbnN0IHZlYzIgaW5wdXRTaGFwZUNSID0gdmVjMigke2lucHV0U2hhcGVSQ0RbMV19LCAke2lucHV0U2hhcGVSQ0RbMF19KTtcbiAgICBjb25zdCB2ZWMyIGlucHV0U2hhcGVUZXhDUiA9IHZlYzIoXG4gICAgICAgICR7aW5wdXRUZXhTaGFwZVJDWzFdfSwgJHtpbnB1dFRleFNoYXBlUkNbMF19KTtcblxuICAgIGNvbnN0IHZlYzIgZWZmZWN0aXZlSW5wdXRPdmVyT3V0cHV0UmF0aW9DUiA9IHZlYzIoXG4gICAgICAgICR7ZWZmZWN0aXZlSW5wdXRTaGFwZVJDRFsxXSAvIGVmZmVjdGl2ZU91dHB1dFNoYXBlUkNEWzFdfSxcbiAgICAgICAgJHtlZmZlY3RpdmVJbnB1dFNoYXBlUkNEWzBdIC8gZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0RbMF19KTtcblxuICAgIGZsb2F0IHNhbXBsZUlucHV0KGZsb2F0IGNvbCwgZmxvYXQgcm93LCBmbG9hdCBkKSB7XG4gICAgICB2ZWMyIHV2ID0gKHZlYzIoY29sICogJHtkZXB0aH0uMCArIGQsIHJvdykgKyBoYWxmQ1IpIC8gaW5wdXRTaGFwZVRleENSO1xuICAgICAgcmV0dXJuIHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICB9XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQpLlxuICAgICAgdmVjMiB5Q1IgPSB2ZWMyKGZsb29yKHlUZXhDUi54IC8gJHtkZXB0aH0uMCksIHlUZXhDUi55KTtcbiAgICAgIGZsb2F0IGQgPSBtb2QoeVRleENSLngsICR7ZGVwdGh9LjApO1xuXG4gICAgICAvLyBGcmFjdGlvbmFsIHNvdXJjZSBpbmRleC5cbiAgICAgIHZlYzIgc291cmNlRnJhY0luZGV4Q1IgPSB5Q1IgKiBlZmZlY3RpdmVJbnB1dE92ZXJPdXRwdXRSYXRpb0NSO1xuXG4gICAgICAvLyBDb21wdXRlIHRoZSBmb3VyIGludGVnZXIgaW5kaWNlcy5cbiAgICAgIHZlYzIgc291cmNlRmxvb3JDUiA9IGZsb29yKHNvdXJjZUZyYWNJbmRleENSKTtcbiAgICAgIHZlYzIgc291cmNlQ2VpbENSID0gbWluKGlucHV0U2hhcGVDUiAtIDEuMCwgY2VpbChzb3VyY2VGcmFjSW5kZXhDUikpO1xuXG4gICAgICBmbG9hdCB0b3BMZWZ0ID0gc2FtcGxlSW5wdXQoc291cmNlRmxvb3JDUlswXSwgc291cmNlRmxvb3JDUlsxXSwgZCk7XG4gICAgICBmbG9hdCBib3R0b21MZWZ0ID0gc2FtcGxlSW5wdXQoc291cmNlRmxvb3JDUlswXSwgc291cmNlQ2VpbENSWzFdLCBkKTtcbiAgICAgIGZsb2F0IHRvcFJpZ2h0ID0gc2FtcGxlSW5wdXQoc291cmNlQ2VpbENSWzBdLCBzb3VyY2VGbG9vckNSWzFdLCBkKTtcbiAgICAgIGZsb2F0IGJvdHRvbVJpZ2h0ID0gc2FtcGxlSW5wdXQoc291cmNlQ2VpbENSWzBdLCBzb3VyY2VDZWlsQ1JbMV0sIGQpO1xuXG4gICAgICB2ZWMyIGZyYWNDUiA9IHNvdXJjZUZyYWNJbmRleENSIC0gc291cmNlRmxvb3JDUjtcblxuICAgICAgZmxvYXQgdG9wID0gdG9wTGVmdCArICh0b3BSaWdodCAtIHRvcExlZnQpICogZnJhY0NSWzBdO1xuICAgICAgZmxvYXQgYm90dG9tID0gYm90dG9tTGVmdCArIChib3R0b21SaWdodCAtIGJvdHRvbUxlZnQpICogZnJhY0NSWzBdO1xuICAgICAgZmxvYXQgbmV3VmFsdWUgPSB0b3AgKyAoYm90dG9tIC0gdG9wKSAqIGZyYWNDUlsxXTtcblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChuZXdWYWx1ZSwgMC4wLCAwLjAsIDAuMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXNpemVCaWxpbmVhcihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZXNpemVCaWxpbmVhclByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHJlc2l6ZUJpbGluZWFyUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbmRhcnJheSc7XG5cbmV4cG9ydCB0eXBlIElucHV0ID0ge1xuICBuYW1lOiBzdHJpbmc7IGFycmF5OiBOREFycmF5O1xufTtcblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXJLZXkoaW5wdXRzOiBOREFycmF5W10sIG91dHB1dDogTkRBcnJheSk6IHN0cmluZyB7XG4gIGNvbnN0IGlucyA9IGlucHV0cy5tYXAoeCA9PiB4LnNoYXBlICsgJ18nICsgeC5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgcmV0dXJuIGlucy5qb2luKCdfJykgKyAnXycgKyBvdXRwdXQuc2hhcGUgKyAnXycgKyBvdXRwdXQuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1ha2VTaGFkZXIoXG4gICAgaW5wdXRzOiBJbnB1dFtdLCBvdXRwdXQ6IE5EQXJyYXksIHVzZXJDb2RlOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBpbnB1dFByZWZpeFNuaXBwZXQgPVxuICAgICAgaW5wdXRzLm1hcCh4ID0+IGB1bmlmb3JtIHNhbXBsZXIyRCAke3gubmFtZX07YCkuam9pbignXFxuJyk7XG4gIGNvbnN0IGlucHV0U2FtcGxpbmdTbmlwcGV0ID1cbiAgICAgIGlucHV0cy5tYXAoeCA9PiBnZXRJbnB1dFNhbXBsaW5nU25pcHBldCh4KSkuam9pbignXFxuJyk7XG4gIGNvbnN0IG91dFRleFNoYXBlID0gb3V0cHV0LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gIGNvbnN0IG91dHB1dFNhbXBsaW5nU25pcHBldCA9XG4gICAgICBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQob3V0cHV0LnNoYXBlLCBvdXRUZXhTaGFwZSk7XG4gIGNvbnN0IHNvdXJjZSA9IFtcbiAgICBTSEFERVJfUFJFRklYLCBpbnB1dFByZWZpeFNuaXBwZXQsIFNBTVBMRV8yRF9TTklQUEVULCBpbnB1dFNhbXBsaW5nU25pcHBldCxcbiAgICBvdXRwdXRTYW1wbGluZ1NuaXBwZXQsIHVzZXJDb2RlXG4gIF0uam9pbignXFxuJyk7XG4gIHJldHVybiBzb3VyY2U7XG59XG5cbmZ1bmN0aW9uIGdldElucHV0U2FtcGxpbmdTbmlwcGV0KGlucHV0OiBJbnB1dCkge1xuICBjb25zdCBhcnIgPSBpbnB1dC5hcnJheTtcbiAgY29uc3Qgc2hhcGUgPSBhcnIuc2hhcGU7XG4gIGNvbnN0IHRleFNoYXBlID0gYXJyLmdldFRleHR1cmVTaGFwZVJDKHNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0pO1xuICBzd2l0Y2ggKHNoYXBlLmxlbmd0aCkge1xuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiBnZXRTYW1wbGVyMkQoaW5wdXQubmFtZSwgc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGUpO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYCR7YXJyLnJhbmt9LUQgaW5wdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRPdXRwdXRTYW1wbGluZ1NuaXBwZXQoXG4gICAgb3V0U2hhcGU6IG51bWJlcltdLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIHN3aXRjaCAob3V0U2hhcGUubGVuZ3RoKSB7XG4gICAgY2FzZSAyOlxuICAgICAgcmV0dXJuIGdldE91dHB1dDJEQ29vcmRzKG91dFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl0sIG91dFRleFNoYXBlKTtcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGAke291dFNoYXBlLmxlbmd0aH0tRCBvdXRwdXQgc2FtcGxpbmcgaXMgbm90IHlldCBzdXBwb3J0ZWRgKTtcbiAgfVxufVxuXG5jb25zdCBTSEFERVJfUFJFRklYID0gYFxuICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcbiAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICB2b2lkIHNldE91dHB1dChmbG9hdCB2YWwpIHtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbCwgMCwgMCwgMCk7XG4gIH1cbmA7XG5cbmNvbnN0IFNBTVBMRV8yRF9TTklQUEVUID0gYFxuICBmbG9hdCBzYW1wbGUyRChzYW1wbGVyMkQgdGV4dHVyZSwgZmxvYXQgdGV4TnVtUiwgZmxvYXQgdGV4TnVtQywgZmxvYXQgbnVtQyxcbiAgICAgIGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgZmxvYXQgaW5kZXggPSBkb3QodmVjMihyb3csIGNvbCksIHZlYzIobnVtQywgMS4wKSk7XG4gICAgZmxvYXQgdGV4UiA9IGZsb29yKGluZGV4IC8gdGV4TnVtQyk7XG4gICAgZmxvYXQgdGV4QyA9IG1vZChpbmRleCwgdGV4TnVtQyk7XG4gICAgdmVjMiB1diA9ICh2ZWMyKHRleEMsIHRleFIpICsgaGFsZkNSKSAvIHZlYzIodGV4TnVtQywgdGV4TnVtUik7XG4gICAgcmV0dXJuIHRleHR1cmUyRCh0ZXh0dXJlLCB1dikucjtcbiAgfVxuYDtcblxuZnVuY3Rpb24gZ2V0T3V0cHV0MkRDb29yZHMoXG4gICAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIHRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGlmICh1dGlsLmFycmF5c0VxdWFsKHNoYXBlLCB0ZXhTaGFwZSkpIHtcbiAgICByZXR1cm4gYFxuICAgICAgdmVjMiBnZXRPdXRwdXRDb29yZHMoKSB7XG4gICAgICAgIHJldHVybiBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgfVxuICAgIGA7XG4gIH1cbiAgcmV0dXJuIGBcbiAgICB2ZWMyIGdldE91dHB1dENvb3JkcygpIHtcbiAgICAgIHZlYzIgcmVzVGV4UkMgPSBmbG9vcihnbF9GcmFnQ29vcmQueXgpO1xuICAgICAgZmxvYXQgaW5kZXggPSBkb3QocmVzVGV4UkMsIHZlYzIoJHt0ZXhTaGFwZVsxXX0uMCwgMS4wKSk7XG4gICAgICBmbG9hdCByID0gZmxvb3IoaW5kZXggLyAke3NoYXBlWzFdfS4wKTtcbiAgICAgIGZsb2F0IGMgPSBtb2QoaW5kZXgsICR7c2hhcGVbMV19LjApO1xuICAgICAgcmV0dXJuIHZlYzIociwgYyk7XG4gICAgfVxuICBgO1xufVxuXG5mdW5jdGlvbiBnZXRTYW1wbGVyMkQoXG4gICAgdGV4TmFtZTogc3RyaW5nLCBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgY29uc3QgZnVuY05hbWUgPSAnZ2V0JyArIHRleE5hbWUuY2hhckF0KDApLnRvVXBwZXJDYXNlKCkgKyB0ZXhOYW1lLnNsaWNlKDEpO1xuICBjb25zdCB0UiA9IHRleFNoYXBlWzBdO1xuICBjb25zdCB0QyA9IHRleFNoYXBlWzFdO1xuICBpZiAodXRpbC5hcnJheXNFcXVhbChzaGFwZSwgdGV4U2hhcGUpKSB7XG4gICAgcmV0dXJuIGBcbiAgICAgIGZsb2F0ICR7ZnVuY05hbWV9KGZsb2F0IHJvdywgZmxvYXQgY29sKSB7XG4gICAgICAgIHZlYzIgdXYgPSAodmVjMihjb2wsIHJvdykgKyBoYWxmQ1IpIC8gdmVjMigke3RDfS4wLCAke3RSfS4wKTtcbiAgICAgICAgcmV0dXJuIHRleHR1cmUyRCgke3RleE5hbWV9LCB1dikucjtcbiAgICAgIH1cbiAgICBgO1xuICB9XG4gIHJldHVybiBgXG4gICAgZmxvYXQgJHtmdW5jTmFtZX0oZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICAgIHJldHVybiBzYW1wbGUyRCgke3RleE5hbWV9LCAke3RSfS4wLCAke3RDfS4wLCAke3NoYXBlWzFdfS4wLCByb3csIGNvbCk7XG4gICAgfVxuICBgO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRTaWdtb2lkVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gJ2dsX0ZyYWdDb2xvciA9IHZlYzQoMS4wIC8gKDEuMCArIGV4cCgtMS4wICogdmFsdWUpKSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNpZ21vaWRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U2lnbW9pZFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaWdtb2lkKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHNpZ21vaWRQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc2lnbW9pZFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTaWdtb2lkRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoXG4gICAgICBhLCByb3dzLCBjb2x1bW5zLCBnZXRTaWdtb2lkVW5hcnlPcCgpKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFN0ZXBVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgcmVzID0gdmFsdWUgPT0gdmFsdWUgPyAodmFsdWUgPiAwLjAgPyAxLjAgOiAwLjApIDogdmFsdWU7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyZXMsIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFN0ZXBVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc3RlcChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBzdGVwUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHN0ZXBQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkU3RlcERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFN0ZXBVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW2NvbHVtbnMsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShcbiAgICBtYXRyaXhTaXplOiBudW1iZXIsIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIG1hdHJpeFNpemUgKiBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb2xvck1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbY29sdW1ucyAqIDQsIHJvd3NdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWF0cml4U2l6ZUZyb21VbnBhY2tlZEFycmF5U2l6ZShcbiAgICB1bnBhY2tlZFNpemU6IG51bWJlciwgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAodW5wYWNrZWRTaXplICUgY2hhbm5lbHNQZXJUZXh0dXJlICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAndW5wYWNrZWRTaXplICgnICsgdW5wYWNrZWRTaXplICsgJykgbXVzdCBiZSBhIG11bHRpcGxlIG9mICcgK1xuICAgICAgICBjaGFubmVsc1BlclRleHR1cmUpO1xuICB9XG4gIHJldHVybiB1bnBhY2tlZFNpemUgLyBjaGFubmVsc1BlclRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBlbmNvZGVNYXRyaXhUb1VucGFja2VkQXJyYXkoXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXksIHVucGFja2VkQXJyYXk6IEZsb2F0MzJBcnJheSxcbiAgICBjaGFubmVsc1BlclRleHR1cmU6IG51bWJlcikge1xuICBjb25zdCByZXF1aXJlZFNpemUgPVxuICAgICAgZ2V0VW5wYWNrZWRBcnJheVNpemVGcm9tTWF0cml4U2l6ZShtYXRyaXgubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBpZiAodW5wYWNrZWRBcnJheS5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd1bnBhY2tlZEFycmF5IGxlbmd0aCAoJyArIHVucGFja2VkQXJyYXkubGVuZ3RoICtcbiAgICAgICAgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgc3JjID0gMDsgc3JjIDwgbWF0cml4Lmxlbmd0aDsgKytzcmMpIHtcbiAgICB1bnBhY2tlZEFycmF5W2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICBkc3QgKz0gY2hhbm5lbHNQZXJUZXh0dXJlO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICB1bnBhY2tlZEFycmF5OiBGbG9hdDMyQXJyYXksIG1hdHJpeDogRmxvYXQzMkFycmF5LFxuICAgIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldE1hdHJpeFNpemVGcm9tVW5wYWNrZWRBcnJheVNpemUoXG4gICAgICB1bnBhY2tlZEFycmF5Lmxlbmd0aCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcbiAgaWYgKG1hdHJpeC5sZW5ndGggPCByZXF1aXJlZFNpemUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgbGV0IGRzdCA9IDA7XG4gIGZvciAobGV0IHNyYyA9IDA7IHNyYyA8IHVucGFja2VkQXJyYXkubGVuZ3RoOyBzcmMgKz0gY2hhbm5lbHNQZXJUZXh0dXJlKSB7XG4gICAgbWF0cml4W2RzdCsrXSA9IHVucGFja2VkQXJyYXlbc3JjXTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtNYXRoLmNlaWwoY29sdW1ucyAvIDIpLCBNYXRoLmNlaWwocm93cyAvIDIpXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBbdywgaF0gPSBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgcmV0dXJuIHcgKiBoICogNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZU1hdHJpeFRvUGFja2VkUkdCQShcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgcGFja2VkUkdCQTogRmxvYXQzMkFycmF5KSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IGdldFBhY2tlZFJHQkFBcnJheVNpemVGcm9tTWF0cml4U2hhcGUocm93cywgY29sdW1ucyk7XG4gIGlmIChwYWNrZWRSR0JBLmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ3BhY2tlZFJHQkEgbGVuZ3RoICgnICsgcGFja2VkUkdCQS5sZW5ndGggK1xuICAgICAgICAnKSBtdXN0IGJlID49ICcgKyByZXF1aXJlZFNpemUpO1xuICB9XG4gIC8qXG4gICAgVW5wYWNrZWQgbWF0cml4LCByb3ctbWFqb3Igb3JkZXIgaW4gRmxvYXQzMkFycmF5WzE2XTogIEEgQiBDIERcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRSBGIEcgSFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJIEogSyBMXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE0gTiBPIFBcblxuICAgIFBhY2tlZCBtYXRyaXgsIDJ4MiBSR0JBMzIgdGV4dHVyZSAobWVtb3J5IHZpZXcpOiAgICAgICBBQkVGIENER0ggSUpNTiBLTE9QXG5cbiAgICBQYWNrZWQgbWF0cml4LCAyeDIgUkdCQTMyIHRleHR1cmUgKG1hdHJpeCB2aWV3KTogICAgICAgQUJ8Q0RcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRUZ8R0hcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0rLS1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSUp8S0xcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTU58T1BcbiAgICovXG4gIGNvbnN0IFt0ZXh0dXJlV2lkdGgsIHRleHR1cmVIZWlnaHRdID1cbiAgICAgIGdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBvZGRXaWR0aCA9IChjb2x1bW5zICUgMikgPT09IDE7XG4gIGNvbnN0IG9kZEhlaWdodCA9IChyb3dzICUgMikgPT09IDE7XG4gIGNvbnN0IHdpZHRoSW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihjb2x1bW5zIC8gMik7XG4gIGNvbnN0IGhlaWdodEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3Iocm93cyAvIDIpO1xuXG4gIC8vIGxvb3Agb3ZlciBmdWxsIDJ4MiBibG9ja3NcbiAge1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IChvZGRXaWR0aCA/IDQgOiAwKTtcbiAgICBjb25zdCBvbmVSb3cgPSBjb2x1bW5zO1xuICAgIGxldCBkc3QgPSAwO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIGNvbnN0IG1hdHJpeFNyY1JvdyA9IChibG9ja1kgKiAyICogY29sdW1ucyk7XG4gICAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgICAgY29uc3QgbWF0cml4U3JjQ29sID0gYmxvY2tYICogMjtcbiAgICAgICAgY29uc3Qgc3JjID0gbWF0cml4U3JjUm93ICsgbWF0cml4U3JjQ29sO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAxXSA9IG1hdHJpeFtzcmMgKyAxXTtcbiAgICAgICAgcGFja2VkUkdCQVtkc3QgKyAyXSA9IG1hdHJpeFtzcmMgKyBvbmVSb3ddO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDNdID0gbWF0cml4W3NyYyArIG9uZVJvdyArIDFdO1xuICAgICAgICBkc3QgKz0gNDtcbiAgICAgIH1cbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBkb3duIGZpbmFsIG9kZCBjb2x1bW5cbiAgaWYgKG9kZFdpZHRoKSB7XG4gICAgbGV0IHNyYyA9IGNvbHVtbnMgLSAxO1xuICAgIGxldCBkc3QgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGNvbnN0IHNyY1N0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgcGFja2VkUkdCQVtkc3RdID0gbWF0cml4W3NyY107XG4gICAgICBwYWNrZWRSR0JBW2RzdCArIDJdID0gbWF0cml4W3NyYyArIGNvbHVtbnNdO1xuICAgICAgc3JjICs9IHNyY1N0cmlkZTtcbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBhY3Jvc3MgZmluYWwgcm93XG4gIGlmIChvZGRIZWlnaHQpIHtcbiAgICBsZXQgc3JjID0gKHJvd3MgLSAxKSAqIGNvbHVtbnM7XG4gICAgbGV0IGRzdCA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgcGFja2VkUkdCQVtkc3QrK10gPSBtYXRyaXhbc3JjKytdO1xuICAgICAgZHN0ICs9IDI7XG4gICAgfVxuICB9XG5cbiAgLy8gZmlsbCBpbiBib3R0b20tcmlnaHQgdGV4ZWxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIHBhY2tlZFJHQkFbcGFja2VkUkdCQS5sZW5ndGggLSA0XSA9IG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV07XG4gIH1cblxuICByZXR1cm4gcGFja2VkUkdCQTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU1hdHJpeEZyb21QYWNrZWRSR0JBKFxuICAgIHBhY2tlZFJHQkE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCByZXF1aXJlZFNpemUgPSByb3dzICogY29sdW1ucztcbiAgaWYgKHJlcXVpcmVkU2l6ZSA8IG1hdHJpeC5sZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdtYXRyaXggbGVuZ3RoICgnICsgbWF0cml4Lmxlbmd0aCArICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgY29uc3Qgb2RkV2lkdGggPSAoY29sdW1ucyAlIDIpID09PSAxO1xuICBjb25zdCBvZGRIZWlnaHQgPSAocm93cyAlIDIpID09PSAxO1xuICBjb25zdCB3aWR0aEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3IoY29sdW1ucyAvIDIpO1xuICBjb25zdCBoZWlnaHRJbkZ1bGxCbG9ja3MgPSBNYXRoLmZsb29yKHJvd3MgLyAyKTtcbiAgY29uc3QgW3RleHR1cmVXaWR0aCwgdGV4dHVyZUhlaWdodF0gPVxuICAgICAgZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG5cbiAgLy8gbG9vcCBvdmVyIGZ1bGwgMngyIGJsb2Nrc1xuICB7XG4gICAgY29uc3Qgc3JjU3RyaWRlID0gb2RkV2lkdGggPyA0IDogMDtcbiAgICBjb25zdCBkc3RTdHJpZGUgPSBjb2x1bW5zICsgKG9kZFdpZHRoID8gMSA6IDApO1xuICAgIGxldCBzcmMgPSAwO1xuICAgIGxldCBkc3RSb3cxID0gMDtcbiAgICBsZXQgZHN0Um93MiA9IGNvbHVtbnM7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgZm9yIChsZXQgYmxvY2tYID0gMDsgYmxvY2tYIDwgd2lkdGhJbkZ1bGxCbG9ja3M7ICsrYmxvY2tYKSB7XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cxKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICAgIG1hdHJpeFtkc3RSb3cyKytdID0gcGFja2VkUkdCQVtzcmMrK107XG4gICAgICB9XG4gICAgICBzcmMgKz0gc3JjU3RyaWRlO1xuICAgICAgZHN0Um93MSArPSBkc3RTdHJpZGU7XG4gICAgICBkc3RSb3cyICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGRvd24gZmluYWwgY29sdW1uXG4gIGlmIChvZGRXaWR0aCkge1xuICAgIGxldCBzcmMgPSAodGV4dHVyZVdpZHRoIC0gMSkgKiA0O1xuICAgIGxldCBkc3QgPSBjb2x1bW5zIC0gMTtcbiAgICBjb25zdCBzcmNTdHJpZGUgPSB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IDIgKiBjb2x1bW5zO1xuICAgIGZvciAobGV0IGJsb2NrWSA9IDA7IGJsb2NrWSA8IGhlaWdodEluRnVsbEJsb2NrczsgKytibG9ja1kpIHtcbiAgICAgIG1hdHJpeFtkc3RdID0gcGFja2VkUkdCQVtzcmNdO1xuICAgICAgbWF0cml4W2RzdCArIGNvbHVtbnNdID0gcGFja2VkUkdCQVtzcmMgKyAyXTtcbiAgICAgIHNyYyArPSBzcmNTdHJpZGU7XG4gICAgICBkc3QgKz0gZHN0U3RyaWRlO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvb3AgYWNyb3NzIGZpbmFsIHJvd1xuICBpZiAob2RkSGVpZ2h0KSB7XG4gICAgbGV0IHNyYyA9ICh0ZXh0dXJlSGVpZ2h0IC0gMSkgKiB0ZXh0dXJlV2lkdGggKiA0O1xuICAgIGxldCBkc3QgPSAocm93cyAtIDEpICogY29sdW1ucztcbiAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIG1hdHJpeFtkc3QrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIHNyYyArPSAyO1xuICAgIH1cbiAgfVxuXG4gIC8vIGZpbGwgaW4gYm90dG9tLXJpZ2h0IGNlbGxcbiAgaWYgKG9kZFdpZHRoICYmIG9kZEhlaWdodCkge1xuICAgIG1hdHJpeFttYXRyaXgubGVuZ3RoIC0gMV0gPSBwYWNrZWRSR0JBW3BhY2tlZFJHQkEubGVuZ3RoIC0gNF07XG4gIH1cblxuICByZXR1cm4gbWF0cml4O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGNsYXNzIFRleHR1cmVNYW5hZ2VyIHtcbiAgcHJpdmF0ZSBudW1Vc2VkVGV4dHVyZXMgPSAwO1xuICBwcml2YXRlIG51bUZyZWVUZXh0dXJlcyA9IDA7XG4gIHByaXZhdGUgZnJlZVRleHR1cmVzOiB7W3NoYXBlOiBzdHJpbmddOiBXZWJHTFRleHR1cmVbXX0gPSB7fTtcbiAgcHJpdmF0ZSBsb2dFbmFibGVkID0gZmFsc2U7XG4gIHByaXZhdGUgdXNlZFRleHR1cmVDb3VudDoge1tzaGFwZTogc3RyaW5nXTogbnVtYmVyfSA9IHt9O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZ3BncHU6IEdQR1BVQ29udGV4dCkge31cblxuICBhY3F1aXJlVGV4dHVyZShzaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKTogV2ViR0xUZXh0dXJlIHtcbiAgICBjb25zdCBzaGFwZUtleSA9IGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGVSQyk7XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy5mcmVlVGV4dHVyZXMpKSB7XG4gICAgICB0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0gPSBbXTtcbiAgICB9XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy51c2VkVGV4dHVyZUNvdW50KSkge1xuICAgICAgdGhpcy51c2VkVGV4dHVyZUNvdW50W3NoYXBlS2V5XSA9IDA7XG4gICAgfVxuICAgIHRoaXMudXNlZFRleHR1cmVDb3VudFtzaGFwZUtleV0rKztcblxuICAgIGlmICh0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0ubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5udW1GcmVlVGV4dHVyZXMtLTtcbiAgICAgIHRoaXMubnVtVXNlZFRleHR1cmVzKys7XG4gICAgICB0aGlzLmxvZygpO1xuICAgICAgcmV0dXJuIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5zaGlmdCgpITtcbiAgICB9XG4gICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMrKztcbiAgICB0aGlzLmxvZygpO1xuXG4gICAgcmV0dXJuIHRoaXMuZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShzaGFwZVJDWzBdLCBzaGFwZVJDWzFdKTtcbiAgfVxuXG4gIHJlbGVhc2VUZXh0dXJlKHRleHR1cmU6IFdlYkdMVGV4dHVyZSwgc2hhcGU6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb25zdCBzaGFwZUtleSA9IGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGUpO1xuICAgIGlmICghKHNoYXBlS2V5IGluIHRoaXMuZnJlZVRleHR1cmVzKSkge1xuICAgICAgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldID0gW107XG4gICAgfVxuICAgIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5wdXNoKHRleHR1cmUpO1xuICAgIHRoaXMubnVtRnJlZVRleHR1cmVzKys7XG4gICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMtLTtcbiAgICB0aGlzLnVzZWRUZXh0dXJlQ291bnRbc2hhcGVLZXldLS07XG4gICAgdGhpcy5sb2coKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9nKCkge1xuICAgIGlmICghdGhpcy5sb2dFbmFibGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHRvdGFsID0gdGhpcy5udW1GcmVlVGV4dHVyZXMgKyB0aGlzLm51bVVzZWRUZXh0dXJlcztcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgJ0ZyZWUvVXNlZCcsIHRoaXMubnVtRnJlZVRleHR1cmVzICsgJyAvICcgKyB0aGlzLm51bVVzZWRUZXh0dXJlcyxcbiAgICAgICAgYCgke3RvdGFsfSlgKTtcbiAgfVxuXG4gIGdldE51bVVzZWRUZXh0dXJlcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLm51bVVzZWRUZXh0dXJlcztcbiAgfVxuXG4gIGdldE51bUZyZWVUZXh0dXJlcygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLm51bUZyZWVUZXh0dXJlcztcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgZm9yIChjb25zdCBzaGFwZSBpbiB0aGlzLmZyZWVUZXh0dXJlcykge1xuICAgICAgaWYgKHRoaXMuZnJlZVRleHR1cmVzLmhhc093blByb3BlcnR5KHNoYXBlKSkge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlXS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIHRoaXMuZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZSh0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZV1baV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIGdldEtleUZyb21UZXh0dXJlU2hhcGUoc2hhcGVSb3dzQ29sOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgcmV0dXJuIHNoYXBlUm93c0NvbFswXSArICdfJyArIHNoYXBlUm93c0NvbFsxXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuLyoqXG4gKiBTaW5lXG4gKi9cbmZ1bmN0aW9uIGdldFNpblVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHNpbih2YWx1ZSksIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2luRnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFNpblVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaW4oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgc2luUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHNpblByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTaW5Eb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRTaW5VbmFyeU9wKCkpO1xufVxuXG4vKipcbiAqIFRhbmhcbiAqL1xuZnVuY3Rpb24gZ2V0VGFuaFVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCBlMnggPSBleHAoLTIuMCAqIHZhbHVlKTtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KCgxLjAgLSBlMngpIC8gKDEuMCArIGUyeCksIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGFuaEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRUYW5oVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhbmgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgdGFuaFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCB0YW5oUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFRhbmhEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRUYW5oVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyZXN1bHRPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXhBLCByZXN1bHRVVikucjtcbiAgICAgICR7cmVzdWx0T3B9XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmFyeU9wKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHVuYXJ5T3BQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIHJvd3MsIGNvbHVtbnMpO1xuICBncGdwdS5zZXRQcm9ncmFtKHVuYXJ5T3BQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRVbmFyeU9wRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICByZXN1bHRPcDogc3RyaW5nKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UocmVzdWx0T3ApO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU3JjKTtcbiAgY29uc3QgYVRleHR1cmU6IFdlYkdMVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IHJlc3VsdFRleHR1cmU6IFdlYkdMVGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4dHVyZSwgcm93cywgY29sdW1ucywgYSk7XG4gIHVuYXJ5T3AoZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCByb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxubGV0IFVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUgPSBmYWxzZTtcbmxldCBXRUJHTDJfRU5BQkxFRDogYm9vbGVhbnx1bmRlZmluZWQgPSBudWxsITtcbmxldCBNQVhfVEVYVFVSRV9TSVpFOiBudW1iZXIgPSBudWxsITtcblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi8uLi91dGlsJztcblxuZXhwb3J0IGludGVyZmFjZSBXZWJHTENvbnRleHRBdHRyaWJ1dGVzIHtcbiAgYWxwaGE/OiBib29sZWFuO1xuICBhbnRpYWxpYXM/OiBib29sZWFuO1xuICBwcmVtdWx0aXBsaWVkQWxwaGE/OiBib29sZWFuO1xuICBwcmVzZXJ2ZURyYXdpbmdCdWZmZXI/OiBib29sZWFuO1xuICBkZXB0aD86IGJvb2xlYW47XG4gIHN0ZW5jaWw/OiBib29sZWFuO1xuICBmYWlsSWZNYWpvclBlcmZvcm1hbmNlQ2F2ZWF0PzogYm9vbGVhbjtcbn1cblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBjb25zdCBJU19OQU5fU0hBREVSX0ZVTkMgPSBgXG5ib29sIGlzTmFOKGZsb2F0IHZhbCkge1xuICByZXR1cm4gdmFsID09IHZhbCA/IGZhbHNlIDogdHJ1ZTtcbn1cbmA7XG5cbmV4cG9ydCBpbnRlcmZhY2UgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbiB7IGxvc2VDb250ZXh0KCk6IHZvaWQ7IH1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dChhdHRyaWJ1dGVzOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzKTpcbiAgICBXZWJHTFJlbmRlcmluZ0NvbnRleHQge1xuICBjb25zdCBjYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgY2FudmFzLndpZHRoID0gMTtcbiAgY2FudmFzLmhlaWdodCA9IDE7XG4gIHJldHVybiBjcmVhdGVXZWJHTFJlbmRlcmluZ0NvbnRleHRGcm9tQ2FudmFzKGNhbnZhcywgYXR0cmlidXRlcyk7XG59XG5cbi8qKlxuICogRm9yY2UgdGhlIGxpYnJhcnkgdG8gcHJlZmVyIFdlYkdMIDEuMCBpbnN0ZWFkIG9mIFdlYkdMIDIuMCBldmVuIHdoZW4gV2ViR0xcbiAqIDIuMCBpcyBhdmFpbGFibGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwcmVmZXJXZWJHTDEoKSB7XG4gIFVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUgPSBmYWxzZTtcbiAgV0VCR0wyX0VOQUJMRUQgPSB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUHJlZmVyIFdlYkdMIDIuMCB0byBXZWJHTCAxLjAuIFRoaXMgaXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZWZlcldlYkdMMigpIHtcbiAgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IHRydWU7XG4gIFdFQkdMMl9FTkFCTEVEID0gdW5kZWZpbmVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNXZWJHTDJFbmFibGVkKCkge1xuICBpZiAoIVVTRV9XRUJHTDJfV0hFTl9BVkFJTEFCTEUpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBpZiAoV0VCR0wyX0VOQUJMRUQgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHRlbXBDYW52YXMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdjYW52YXMnKTtcbiAgICBjb25zdCBnbCA9IHRlbXBDYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wyJyk7XG4gICAgaWYgKGdsICE9IG51bGwpIHtcbiAgICAgIFdFQkdMMl9FTkFCTEVEID0gdHJ1ZTtcblxuICAgICAgY29uc3QgbG9zZUNvbnRleHRFeHRlbnNpb24gPVxuICAgICAgICAgIGdldEV4dGVuc2lvbk9yVGhyb3coXG4gICAgICAgICAgICAgIGdsIGFzIFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgJ1dFQkdMX2xvc2VfY29udGV4dCcpIGFzXG4gICAgICAgICAgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgICAgIGxvc2VDb250ZXh0RXh0ZW5zaW9uLmxvc2VDb250ZXh0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIFdFQkdMMl9FTkFCTEVEID0gZmFsc2U7XG4gICAgfVxuICB9XG4gIHJldHVybiBXRUJHTDJfRU5BQkxFRDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoXG4gICAgY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCxcbiAgICBhdHRyaWJ1dGVzOiBXZWJHTENvbnRleHRBdHRyaWJ1dGVzKTogV2ViR0xSZW5kZXJpbmdDb250ZXh0IHtcbiAgbGV0IGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIGlmIChpc1dlYkdMMkVuYWJsZWQoKSkge1xuICAgIGdsID0gY2FudmFzLmdldENvbnRleHQoJ3dlYmdsMicsIGF0dHJpYnV0ZXMpIGFzIFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgfSBlbHNlIHtcbiAgICBnbCA9IChjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wnLCBhdHRyaWJ1dGVzKSB8fFxuICAgICAgICAgIGNhbnZhcy5nZXRDb250ZXh0KCdleHBlcmltZW50YWwtd2ViZ2wnLCBhdHRyaWJ1dGVzKSkgYXNcbiAgICAgICAgV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB9XG5cbiAgaWYgKGdsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoaXMgYnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IFdlYkdMLicpO1xuICB9XG4gIHJldHVybiBnbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGxBbmRDaGVjazxUPihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmdW5jOiAoKSA9PiBUKTogVCB7XG4gIGNvbnN0IHJldHVyblZhbHVlID0gZnVuYygpO1xuICBjaGVja1dlYkdMRXJyb3IoZ2wpO1xuICByZXR1cm4gcmV0dXJuVmFsdWU7XG59XG5cbmxldCB3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQgPSBmYWxzZTtcblxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZURlYnVnV2ViR0xFcnJvckNoZWNraW5nKGVuYWJsZWQ6IGJvb2xlYW4pIHtcbiAgd2ViR0xEZWJ1Z0Vycm9yQ2hlY2tpbmdFbmFibGVkID0gZW5hYmxlZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNoZWNrV2ViR0xFcnJvcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gIGlmICh3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQpIHtcbiAgICBjb25zdCBlcnJvciA9IGdsLmdldEVycm9yKCk7XG4gICAgaWYgKGVycm9yICE9PSBnbC5OT19FUlJPUikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdXZWJHTCBFcnJvcjogJyArIGdldFdlYkdMRXJyb3JNZXNzYWdlKGdsLCBlcnJvcikpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0V2ViR0xFcnJvck1lc3NhZ2UoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgc3RhdHVzOiBudW1iZXIpOiBzdHJpbmcge1xuICBzd2l0Y2ggKHN0YXR1cykge1xuICAgIGNhc2UgZ2wuTk9fRVJST1I6XG4gICAgICByZXR1cm4gJ05PX0VSUk9SJztcbiAgICBjYXNlIGdsLklOVkFMSURfRU5VTTpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9FTlVNJztcbiAgICBjYXNlIGdsLklOVkFMSURfVkFMVUU6XG4gICAgICByZXR1cm4gJ0lOVkFMSURfVkFMVUUnO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9PUEVSQVRJT046XG4gICAgICByZXR1cm4gJ0lOVkFMSURfT1BFUkFUSU9OJztcbiAgICBjYXNlIGdsLklOVkFMSURfRlJBTUVCVUZGRVJfT1BFUkFUSU9OOlxuICAgICAgcmV0dXJuICdJTlZBTElEX0ZSQU1FQlVGRkVSX09QRVJBVElPTic7XG4gICAgY2FzZSBnbC5PVVRfT0ZfTUVNT1JZOlxuICAgICAgcmV0dXJuICdPVVRfT0ZfTUVNT1JZJztcbiAgICBjYXNlIGdsLkNPTlRFWFRfTE9TVF9XRUJHTDpcbiAgICAgIHJldHVybiAnQ09OVEVYVF9MT1NUX1dFQkdMJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICdVbmtub3duIGVycm9yIGNvZGUgJyArIHN0YXR1cztcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RXh0ZW5zaW9uT3JUaHJvdyhcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBleHRlbnNpb25OYW1lOiBzdHJpbmcpOiB7fSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDx7fT4oXG4gICAgICBnbCwgKCkgPT4gZ2wuZ2V0RXh0ZW5zaW9uKGV4dGVuc2lvbk5hbWUpLFxuICAgICAgJ0V4dGVuc2lvbiBcIicgKyBleHRlbnNpb25OYW1lICsgJ1wiIG5vdCBzdXBwb3J0ZWQgb24gdGhpcyBicm93c2VyLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVmVydGV4U2hhZGVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHZlcnRleFNoYWRlclNvdXJjZTogc3RyaW5nKTogV2ViR0xTaGFkZXIge1xuICBjb25zdCB2ZXJ0ZXhTaGFkZXI6IFdlYkdMU2hhZGVyID0gdGhyb3dJZk51bGw8V2ViR0xTaGFkZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVNoYWRlcihnbC5WRVJURVhfU0hBREVSKSxcbiAgICAgICdVbmFibGUgdG8gY3JlYXRlIHZlcnRleCBXZWJHTFNoYWRlci4nKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5zaGFkZXJTb3VyY2UodmVydGV4U2hhZGVyLCB2ZXJ0ZXhTaGFkZXJTb3VyY2UpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jb21waWxlU2hhZGVyKHZlcnRleFNoYWRlcikpO1xuICBpZiAoZ2wuZ2V0U2hhZGVyUGFyYW1ldGVyKHZlcnRleFNoYWRlciwgZ2wuQ09NUElMRV9TVEFUVVMpID09PSBmYWxzZSkge1xuICAgIGNvbnNvbGUubG9nKGdsLmdldFNoYWRlckluZm9Mb2codmVydGV4U2hhZGVyKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gY29tcGlsZSB2ZXJ0ZXggc2hhZGVyLicpO1xuICB9XG4gIHJldHVybiB2ZXJ0ZXhTaGFkZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGcmFnbWVudFNoYWRlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmcmFnbWVudFNoYWRlclNvdXJjZTogc3RyaW5nKTogV2ViR0xTaGFkZXIge1xuICBjb25zdCBmcmFnbWVudFNoYWRlcjogV2ViR0xTaGFkZXIgPSB0aHJvd0lmTnVsbDxXZWJHTFNoYWRlcj4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlU2hhZGVyKGdsLkZSQUdNRU5UX1NIQURFUiksXG4gICAgICAnVW5hYmxlIHRvIGNyZWF0ZSBmcmFnbWVudCBXZWJHTFNoYWRlci4nKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5zaGFkZXJTb3VyY2UoZnJhZ21lbnRTaGFkZXIsIGZyYWdtZW50U2hhZGVyU291cmNlKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuY29tcGlsZVNoYWRlcihmcmFnbWVudFNoYWRlcikpO1xuICBpZiAoZ2wuZ2V0U2hhZGVyUGFyYW1ldGVyKGZyYWdtZW50U2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0U2hhZGVySW5mb0xvZyhmcmFnbWVudFNoYWRlcikpO1xuICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGNvbXBpbGUgZnJhZ21lbnQgc2hhZGVyLicpO1xuICB9XG4gIHJldHVybiBmcmFnbWVudFNoYWRlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVByb2dyYW0oZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMUHJvZ3JhbSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTFByb2dyYW0+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVByb2dyYW0oKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xQcm9ncmFtLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbGlua1Byb2dyYW0oZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtKSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wubGlua1Byb2dyYW0ocHJvZ3JhbSkpO1xuICBpZiAoZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5MSU5LX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0UHJvZ3JhbUluZm9Mb2cocHJvZ3JhbSkpO1xuICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGxpbmsgdmVydGV4IGFuZCBmcmFnbWVudCBzaGFkZXJzLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVByb2dyYW0oXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtKSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmFsaWRhdGVQcm9ncmFtKHByb2dyYW0pKTtcbiAgaWYgKGdsLmdldFByb2dyYW1QYXJhbWV0ZXIocHJvZ3JhbSwgZ2wuVkFMSURBVEVfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRQcm9ncmFtSW5mb0xvZyhwcm9ncmFtKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdTaGFkZXIgcHJvZ3JhbSB2YWxpZGF0aW9uIGZhaWxlZC4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU3RhdGljVmVydGV4QnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGRhdGE6IEZsb2F0MzJBcnJheSk6IFdlYkdMQnVmZmVyIHtcbiAgY29uc3QgYnVmZmVyOiBXZWJHTEJ1ZmZlciA9IHRocm93SWZOdWxsPFdlYkdMQnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVCdWZmZXIoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xCdWZmZXInKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYnVmZmVyRGF0YShnbC5BUlJBWV9CVUZGRVIsIGRhdGEsIGdsLlNUQVRJQ19EUkFXKSk7XG4gIHJldHVybiBidWZmZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdGF0aWNJbmRleEJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBkYXRhOiBVaW50MTZBcnJheSk6IFdlYkdMQnVmZmVyIHtcbiAgY29uc3QgYnVmZmVyOiBXZWJHTEJ1ZmZlciA9IHRocm93SWZOdWxsPFdlYkdMQnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVCdWZmZXIoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xCdWZmZXInKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkVMRU1FTlRfQVJSQVlfQlVGRkVSLCBidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLmJ1ZmZlckRhdGEoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGRhdGEsIGdsLlNUQVRJQ19EUkFXKSk7XG4gIHJldHVybiBidWZmZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBxdWVyeU1heFRleHR1cmVTaXplKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBudW1iZXIge1xuICBpZiAoTUFYX1RFWFRVUkVfU0laRSAhPSBudWxsKSB7XG4gICAgcmV0dXJuIE1BWF9URVhUVVJFX1NJWkU7XG4gIH1cbiAgTUFYX1RFWFRVUkVfU0laRSA9XG4gICAgICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsIS5nZXRQYXJhbWV0ZXIoZ2whLk1BWF9URVhUVVJFX1NJWkUpKTtcbiAgcmV0dXJuIE1BWF9URVhUVVJFX1NJWkU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRDaGFubmVsc1BlclRleHR1cmUoKTogbnVtYmVyIHtcbiAgaWYgKGlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgcmV0dXJuIDQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVUZXh0dXJlKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTFRleHR1cmUge1xuICByZXR1cm4gdGhyb3dJZk51bGw8V2ViR0xUZXh0dXJlPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVUZXh0dXJlKCksICdVbmFibGUgdG8gY3JlYXRlIFdlYkdMVGV4dHVyZS4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlVGV4dHVyZVNpemUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpIHtcbiAgY29uc3QgbWF4VGV4dHVyZVNpemU6IG51bWJlciA9IHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2wpO1xuICBpZiAoKHdpZHRoIDw9IDApIHx8IChoZWlnaHQgPD0gMCkpIHtcbiAgICBjb25zdCByZXF1ZXN0ZWQgPSAnWycgKyB3aWR0aCArICd4JyArIGhlaWdodCArICddJztcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1JlcXVlc3RlZCB0ZXh0dXJlIHNpemUgJyArIHJlcXVlc3RlZCArICcgaXMgaW52YWxpZC4nKTtcbiAgfVxuICBpZiAoKHdpZHRoID4gbWF4VGV4dHVyZVNpemUpIHx8IChoZWlnaHQgPiBtYXhUZXh0dXJlU2l6ZSkpIHtcbiAgICBjb25zdCByZXF1ZXN0ZWQgPSAnWycgKyB3aWR0aCArICd4JyArIGhlaWdodCArICddJztcbiAgICBjb25zdCBtYXggPSAnWycgKyBtYXhUZXh0dXJlU2l6ZSArICd4JyArIG1heFRleHR1cmVTaXplICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ1JlcXVlc3RlZCB0ZXh0dXJlIHNpemUgJyArIHJlcXVlc3RlZCArXG4gICAgICAgICcgZ3JlYXRlciB0aGFuIFdlYkdMIG1heGltdW0gb24gdGhpcyBicm93c2VyIC8gR1BVICcgKyBtYXggKyAnLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVGcmFtZWJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xGcmFtZWJ1ZmZlciB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTEZyYW1lYnVmZmVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVGcmFtZWJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEZyYW1lYnVmZmVyLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFZlcnRleEJ1ZmZlclRvUHJvZ3JhbUF0dHJpYnV0ZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGF0dHJpYnV0ZTogc3RyaW5nLFxuICAgIGJ1ZmZlcjogV2ViR0xCdWZmZXIsIGFycmF5RW50cmllc1Blckl0ZW06IG51bWJlciwgaXRlbVN0cmlkZUluQnl0ZXM6IG51bWJlcixcbiAgICBpdGVtT2Zmc2V0SW5CeXRlczogbnVtYmVyKSB7XG4gIGNvbnN0IGxvYyA9IGdsLmdldEF0dHJpYkxvY2F0aW9uKHByb2dyYW0sIGF0dHJpYnV0ZSk7XG4gIGlmIChsb2MgPT09IC0xKSB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoXG4gICAgICAgICdVbmFibGUgdG8gZ2V0IGF0dHJpYnV0ZSBcIicgKyBhdHRyaWJ1dGUgKyAnXCIgb24gV2ViR0xQcm9ncmFtLicpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAoZXJyb3IgYXMgYW55KS5uYW1lZFZlcnRleEF0dHJpYnV0ZU5vdEZvdW5kID0gYXR0cmlidXRlO1xuICAgIHRocm93IGVycm9yO1xuICB9XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5BUlJBWV9CVUZGRVIsIGJ1ZmZlcikpO1xuICBjYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnZlcnRleEF0dHJpYlBvaW50ZXIoXG4gICAgICAgICAgbG9jLCBhcnJheUVudHJpZXNQZXJJdGVtLCBnbC5GTE9BVCwgZmFsc2UsIGl0ZW1TdHJpZGVJbkJ5dGVzLFxuICAgICAgICAgIGl0ZW1PZmZzZXRJbkJ5dGVzKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZW5hYmxlVmVydGV4QXR0cmliQXJyYXkobG9jKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVGV4dHVyZVVuaXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmVVbml0KTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hY3RpdmVUZXh0dXJlKGdsLlRFWFRVUkUwICsgdGV4dHVyZVVuaXQpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmJpbmRUZXh0dXJlVW5pdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlVW5pdDogbnVtYmVyKSB7XG4gIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmVVbml0KTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hY3RpdmVUZXh0dXJlKGdsLlRFWFRVUkUwICsgdGV4dHVyZVVuaXQpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCBudWxsKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRQcm9ncmFtVW5pZm9ybUxvY2F0aW9uT3JUaHJvdyhcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sXG4gICAgdW5pZm9ybU5hbWU6IHN0cmluZyk6IFdlYkdMVW5pZm9ybUxvY2F0aW9uIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMVW5pZm9ybUxvY2F0aW9uPihcbiAgICAgIGdsLCAoKSA9PiBnbC5nZXRVbmlmb3JtTG9jYXRpb24ocHJvZ3JhbSwgdW5pZm9ybU5hbWUpLFxuICAgICAgJ3VuaWZvcm0gXCInICsgdW5pZm9ybU5hbWUgKyAnXCIgbm90IHByZXNlbnQgaW4gcHJvZ3JhbS4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRUZXh0dXJlVG9Qcm9ncmFtVW5pZm9ybVNhbXBsZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgdW5pZm9ybVNhbXBsZXJOYW1lOiBzdHJpbmcsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBiaW5kVGV4dHVyZVVuaXQoZ2wsIHRleHR1cmUsIHRleHR1cmVVbml0KSk7XG4gIGNvbnN0IHNhbXBsZXJMb2NhdGlvbiA9XG4gICAgICBnZXRQcm9ncmFtVW5pZm9ybUxvY2F0aW9uT3JUaHJvdyhnbCwgcHJvZ3JhbSwgdW5pZm9ybVNhbXBsZXJOYW1lKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC51bmlmb3JtMWkoc2FtcGxlckxvY2F0aW9uLCB0ZXh0dXJlVW5pdCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZENhbnZhc1RvRnJhbWVidWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgbnVsbCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnZpZXdwb3J0KDAsIDAsIGdsLmNhbnZhcy53aWR0aCwgZ2wuY2FudmFzLmhlaWdodCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNjaXNzb3IoMCwgMCwgZ2wuY2FudmFzLndpZHRoLCBnbC5jYW52YXMuaGVpZ2h0KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kQ29sb3JUZXh0dXJlVG9GcmFtZWJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgZnJhbWVidWZmZXI6IFdlYkdMRnJhbWVidWZmZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgICAgZ2wuRlJBTUVCVUZGRVIsIGdsLkNPTE9SX0FUVEFDSE1FTlQwLCBnbC5URVhUVVJFXzJELCB0ZXh0dXJlLCAwKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1bmJpbmRDb2xvclRleHR1cmVGcm9tRnJhbWVidWZmZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgZnJhbWVidWZmZXI6IFdlYkdMRnJhbWVidWZmZXIpIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kRnJhbWVidWZmZXIoZ2wuRlJBTUVCVUZGRVIsIGZyYW1lYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wuZnJhbWVidWZmZXJUZXh0dXJlMkQoXG4gICAgICAgICAgZ2wuRlJBTUVCVUZGRVIsIGdsLkNPTE9SX0FUVEFDSE1FTlQwLCBnbC5URVhUVVJFXzJELCBudWxsLCAwKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUZyYW1lYnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgY29uc3Qgc3RhdHVzID0gZ2wuY2hlY2tGcmFtZWJ1ZmZlclN0YXR1cyhnbC5GUkFNRUJVRkZFUik7XG4gIGlmIChzdGF0dXMgIT09IGdsLkZSQU1FQlVGRkVSX0NPTVBMRVRFKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnRXJyb3IgYmluZGluZyBmcmFtZWJ1ZmZlcjogJyArIGdldEZyYW1lYnVmZmVyRXJyb3JNZXNzYWdlKGdsLCBzdGF0dXMpKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhbWVidWZmZXJFcnJvck1lc3NhZ2UoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgc3RhdHVzOiBudW1iZXIpOiBzdHJpbmcge1xuICBzd2l0Y2ggKHN0YXR1cykge1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9BVFRBQ0hNRU5UOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9NSVNTSU5HX0FUVEFDSE1FTlQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfTUlTU0lOR19BVFRBQ0hNRU5UJztcbiAgICBjYXNlIGdsLkZSQU1FQlVGRkVSX0lOQ09NUExFVEVfRElNRU5TSU9OUzpcbiAgICAgIHJldHVybiAnRlJBTUVCVUZGRVJfSU5DT01QTEVURV9ESU1FTlNJT05TJztcbiAgICBjYXNlIGdsLkZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9VTlNVUFBPUlRFRCc7XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiAndW5rbm93biBlcnJvciAnICsgc3RhdHVzO1xuICB9XG59XG5cbmZ1bmN0aW9uIHRocm93SWZOdWxsPFQ+KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJldHVyblRPck51bGw6ICgpID0+IFQgfCBudWxsLFxuICAgIGZhaWx1cmVNZXNzYWdlOiBzdHJpbmcpOiBUIHtcbiAgY29uc3QgdE9yTnVsbDogVHxudWxsID0gY2FsbEFuZENoZWNrKGdsLCAoKSA9PiByZXR1cm5UT3JOdWxsKCkpO1xuICBpZiAodE9yTnVsbCA9PSBudWxsKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGZhaWx1cmVNZXNzYWdlKTtcbiAgfVxuICByZXR1cm4gdE9yTnVsbCBhcyBUO1xufVxuXG5mdW5jdGlvbiB2YWxpZGF0ZVRleHR1cmVVbml0KGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgY29uc3QgbWF4VGV4dHVyZVVuaXQgPSBnbC5NQVhfQ09NQklORURfVEVYVFVSRV9JTUFHRV9VTklUUyAtIDE7XG4gIGNvbnN0IGdsVGV4dHVyZVVuaXQgPSB0ZXh0dXJlVW5pdCArIGdsLlRFWFRVUkUwO1xuICBpZiAoZ2xUZXh0dXJlVW5pdCA8IGdsLlRFWFRVUkUwIHx8IGdsVGV4dHVyZVVuaXQgPiBtYXhUZXh0dXJlVW5pdCkge1xuICAgIGNvbnN0IHRleHR1cmVVbml0UmFuZ2UgPSAnW2dsLlRFWFRVUkUwLCBnbC5URVhUVVJFJyArIG1heFRleHR1cmVVbml0ICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcigndGV4dHVyZVVuaXQgbXVzdCBiZSBpbiAnICsgdGV4dHVyZVVuaXRSYW5nZSArICcuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFRleHR1cmVTaGFwZUZyb21Mb2dpY2FsU2hhcGUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbG9naWNhbFNoYXBlOiBudW1iZXJbXSxcbiAgICBwcmVmZXJyZWRUZXhTaGFwZT86IFtudW1iZXIsIG51bWJlcl0pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgbWF4VGV4U2l6ZSA9IHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2wpO1xuICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKGxvZ2ljYWxTaGFwZSk7XG4gIGlmIChwcmVmZXJyZWRUZXhTaGFwZSAhPSBudWxsKSB7XG4gICAgY29uc3Qgc2l6ZVByZWZlcnJlZCA9IHV0aWwuc2l6ZUZyb21TaGFwZShwcmVmZXJyZWRUZXhTaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHNpemUgPT09IHNpemVQcmVmZXJyZWQsXG4gICAgICAgIGBTaXplIG9mIHNoYXBlICgke3NpemV9KSBtdXN0IG1hdGNoIHNpemUgb2YgYCArXG4gICAgICAgICAgICBgcHJlZmVycmVkU2hhcGUgKCR7c2l6ZVByZWZlcnJlZH0pYCk7XG4gICAgaWYgKHByZWZlcnJlZFRleFNoYXBlWzBdIDw9IG1heFRleFNpemUgJiZcbiAgICAgICAgcHJlZmVycmVkVGV4U2hhcGVbMV0gPD0gbWF4VGV4U2l6ZSkge1xuICAgICAgcmV0dXJuIHByZWZlcnJlZFRleFNoYXBlO1xuICAgIH1cbiAgfVxuXG4gIGlmIChsb2dpY2FsU2hhcGUubGVuZ3RoIDw9IDEgJiYgc2l6ZSA8PSBtYXhUZXhTaXplKSB7XG4gICAgcmV0dXJuIFtzaXplLCAxXTtcbiAgfSBlbHNlIGlmIChcbiAgICAgIGxvZ2ljYWxTaGFwZS5sZW5ndGggPT09IDIgJiYgbG9naWNhbFNoYXBlWzBdIDw9IG1heFRleFNpemUgJiZcbiAgICAgIGxvZ2ljYWxTaGFwZVsxXSA8PSBtYXhUZXhTaXplKSB7XG4gICAgcmV0dXJuIGxvZ2ljYWxTaGFwZSBhcyBbbnVtYmVyLCBudW1iZXJdO1xuICB9IGVsc2UgaWYgKFxuICAgICAgbG9naWNhbFNoYXBlLmxlbmd0aCA9PT0gMyAmJiBsb2dpY2FsU2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgbG9naWNhbFNoYXBlWzFdICogbG9naWNhbFNoYXBlWzJdIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gW2xvZ2ljYWxTaGFwZVswXSwgbG9naWNhbFNoYXBlWzFdICogbG9naWNhbFNoYXBlWzJdXTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gdXRpbC5zaXplVG9TcXVhcmlzaFNoYXBlKHNpemUpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7QWRkTm9kZSwgQXJnTWF4RXF1YWxzTm9kZSwgQXJnTWF4Tm9kZSwgQ29uY2F0M0ROb2RlLCBDb252b2x1dGlvbjJETm9kZSwgRGl2aWRlTm9kZSwgRXhwTm9kZSwgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUsIEdyYXBoLCBMb2dOb2RlLCBNYXRNdWxOb2RlLCBNYXhQb29sTm9kZSwgTWVhblNxdWFyZWRDb3N0Tm9kZSwgTXVsdGlwbHlOb2RlLCBOb2RlLCBSZWR1Y2VTdW1Ob2RlLCBSZUxVTm9kZSwgUmVzaGFwZU5vZGUsIFNpZ21vaWROb2RlLCBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUsIFNvZnRtYXhOb2RlLCBTcGxpdE5vZGUsIFNxdWFyZU5vZGUsIFN1YnRyYWN0Tm9kZSwgVGFuSE5vZGUsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge0FkZH0gZnJvbSAnLi9vcHMvYWRkJztcbmltcG9ydCB7QXJnTWF4fSBmcm9tICcuL29wcy9hcmdtYXgnO1xuaW1wb3J0IHtBcmdNYXhFcXVhbHN9IGZyb20gJy4vb3BzL2FyZ21heGVxdWFscyc7XG5pbXBvcnQge0NvbmNhdDNEfSBmcm9tICcuL29wcy9jb25jYXQzZCc7XG5pbXBvcnQge0NvbnZvbHV0aW9uMkR9IGZyb20gJy4vb3BzL2NvbnZvbHV0aW9uJztcbmltcG9ydCB7RGl2aWRlfSBmcm9tICcuL29wcy9kaXZpZGUnO1xuaW1wb3J0IHtSZUxVLCBTaWdtb2lkLCBTcXVhcmUsIFRhbkh9IGZyb20gJy4vb3BzL2VsZW1lbnRfd2lzZV9hY3RpdmF0aW9uJztcbmltcG9ydCB7TWVhblNxdWFyZWRDb3N0fSBmcm9tICcuL29wcy9lbGVtZW50X3dpc2VfY29zdCc7XG5pbXBvcnQge0V4cH0gZnJvbSAnLi9vcHMvZXhwJztcbmltcG9ydCB7TGluZWFyQ29tYmluYXRpb259IGZyb20gJy4vb3BzL2xpbmVhcl9jb21iaW5hdGlvbic7XG5pbXBvcnQge0xvZ30gZnJvbSAnLi9vcHMvbG9nJztcbmltcG9ydCB7TWF0TXVsfSBmcm9tICcuL29wcy9tYXRtdWwnO1xuaW1wb3J0IHtNYXhQb29sfSBmcm9tICcuL29wcy9tYXhfcG9vbCc7XG5pbXBvcnQge011bHRpcGx5fSBmcm9tICcuL29wcy9tdWx0aXBseSc7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtSZWR1Y2VTdW19IGZyb20gJy4vb3BzL3JlZHVjZV9zdW0nO1xuaW1wb3J0IHtSZXNoYXBlfSBmcm9tICcuL29wcy9yZXNoYXBlJztcbmltcG9ydCB7U29mdG1heCwgU29mdG1heENyb3NzRW50cm9weUNvc3R9IGZyb20gJy4vb3BzL3NvZnRtYXgnO1xuaW1wb3J0IHtTcGxpdH0gZnJvbSAnLi9vcHMvc3BsaXQnO1xuaW1wb3J0IHtTdWJ0cmFjdH0gZnJvbSAnLi9vcHMvc3VidHJhY3QnO1xuXG5leHBvcnQgZnVuY3Rpb24gZW1pdEZyb21HcmFwaE5vZGVzKG5vZGVzOiBOb2RlW10pOiBPcGVyYXRpb25bXSB7XG4gIGNvbnN0IG9wczogT3BlcmF0aW9uW10gPSBbXTtcbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IEFycmF5LnByb3RvdHlwZS5wdXNoLmFwcGx5KG9wcywgZW1pdE9wRnJvbU5vZGUobm9kZSkpKTtcbiAgcmV0dXJuIG9wcztcbn1cblxuZnVuY3Rpb24gZW1pdE9wRnJvbU5vZGUobm9kZTogTm9kZSk6IE9wZXJhdGlvbltdIHtcbiAgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZXNoYXBlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFJlc2hhcGUobm9kZS5pbnB1dHNbUmVzaGFwZU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE1hdE11bE5vZGUpIHtcbiAgICBjb25zdCB4MSA9IG5vZGUuaW5wdXRzW01hdE11bE5vZGUuWDFdO1xuICAgIGNvbnN0IHgyID0gbm9kZS5pbnB1dHNbTWF0TXVsTm9kZS5YMl07XG4gICAgcmV0dXJuIFtuZXcgTWF0TXVsKHgxLCB4Miwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQ29udm9sdXRpb24yRE5vZGUpIHtcbiAgICBjb25zdCB3ID0gbm9kZS5pbnB1dHNbQ29udm9sdXRpb24yRE5vZGUuV107XG4gICAgY29uc3QgeCA9IG5vZGUuaW5wdXRzW0NvbnZvbHV0aW9uMkROb2RlLlhdO1xuICAgIGNvbnN0IGIgPSBub2RlLmlucHV0c1tDb252b2x1dGlvbjJETm9kZS5CXTtcbiAgICByZXR1cm4gW25ldyBDb252b2x1dGlvbjJEKFxuICAgICAgICB3LCB4LCBiLCBub2RlLm91dHB1dCwgbm9kZS5maWVsZFNpemUsIG5vZGUub3V0cHV0RGVwdGgsIG5vZGUuc3RyaWRlLFxuICAgICAgICBub2RlLnplcm9QYWQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTWF4UG9vbE5vZGUpIHtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbTWF4UG9vbE5vZGUuWF07XG4gICAgcmV0dXJuIFtuZXcgTWF4UG9vbChcbiAgICAgICAgeCwgbm9kZS5vdXRwdXQsIG5vZGUuZmllbGRTaXplLCBub2RlLnN0cmlkZSwgbm9kZS56ZXJvUGFkKV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEV4cE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBFeHAobm9kZS5pbnB1dHNbRXhwTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTG9nTm9kZSkge1xuICAgIHJldHVybiBbbmV3IExvZyhub2RlLmlucHV0c1tMb2dOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZUxVTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFJlTFUobm9kZS5pbnB1dHNbUmVMVU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFRhbkhOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgVGFuSChub2RlLmlucHV0c1tUYW5ITm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU2lnbW9pZE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTaWdtb2lkKG5vZGUuaW5wdXRzW1NpZ21vaWROb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUpIHtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlLlhdO1xuICAgIGNvbnN0IHRhcmdldCA9IG5vZGUuaW5wdXRzW1NvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZS5UQVJHRVRdO1xuICAgIHJldHVybiBbbmV3IFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0KHgsIHRhcmdldCwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU29mdG1heE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTb2Z0bWF4KG5vZGUuaW5wdXRzW1NvZnRtYXhOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNZWFuU3F1YXJlZENvc3ROb2RlKSB7XG4gICAgY29uc3QgbGFiZWwgPSBub2RlLmlucHV0c1tNZWFuU3F1YXJlZENvc3ROb2RlLkxBQkVMXTtcbiAgICBjb25zdCBwcmVkaWN0aW9uID0gbm9kZS5pbnB1dHNbTWVhblNxdWFyZWRDb3N0Tm9kZS5QUkVESUNUSU9OXTtcbiAgICByZXR1cm4gW25ldyBNZWFuU3F1YXJlZENvc3QobGFiZWwsIHByZWRpY3Rpb24sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEFyZ01heEVxdWFsc05vZGUpIHtcbiAgICByZXR1cm4gW25ldyBBcmdNYXhFcXVhbHMoXG4gICAgICAgIG5vZGUuaW5wdXRzW0FyZ01heEVxdWFsc05vZGUuWDFdLCBub2RlLmlucHV0c1tBcmdNYXhFcXVhbHNOb2RlLlgyXSxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQXJnTWF4Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IEFyZ01heChub2RlLngsIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEZ1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgTGluZWFyQ29tYmluYXRpb24oXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLlQxXSxcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuVDJdLFxuICAgICAgICBub2RlLmlucHV0c1tGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZS5DMV0sXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLkMyXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQ29uY2F0M0ROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQ29uY2F0M0QoXG4gICAgICAgIG5vZGUuaW5wdXRzW0NvbmNhdDNETm9kZS5YMV0sIG5vZGUuaW5wdXRzW0NvbmNhdDNETm9kZS5YMl0sIG5vZGUuYXhpcyxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU3F1YXJlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNxdWFyZShub2RlLmlucHV0c1tTcXVhcmVOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBBZGROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQWRkKFxuICAgICAgICBub2RlLmlucHV0c1tBZGROb2RlLlQxXSwgbm9kZS5pbnB1dHNbQWRkTm9kZS5UMl0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFN1YnRyYWN0Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFN1YnRyYWN0KFxuICAgICAgICBub2RlLmlucHV0c1tTdWJ0cmFjdE5vZGUuVDFdLCBub2RlLmlucHV0c1tTdWJ0cmFjdE5vZGUuVDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNdWx0aXBseU5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBNdWx0aXBseShcbiAgICAgICAgbm9kZS5pbnB1dHNbTXVsdGlwbHlOb2RlLlQxXSwgbm9kZS5pbnB1dHNbTXVsdGlwbHlOb2RlLlQyXSxcbiAgICAgICAgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRGl2aWRlTm9kZSkge1xuICAgIHJldHVybiBbbmV3IERpdmlkZShcbiAgICAgICAgbm9kZS5pbnB1dHNbRGl2aWRlTm9kZS5UMV0sIG5vZGUuaW5wdXRzW0RpdmlkZU5vZGUuVDJdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTcGxpdE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBTcGxpdChub2RlLmlucHV0c1tTcGxpdE5vZGUuWF0sIG5vZGUub3V0cHV0cyldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBSZWR1Y2VTdW1Ob2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVkdWNlU3VtKG5vZGUuaW5wdXRzW1JlZHVjZVN1bU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAoZ3JhcGhfdXRpbC5pc0lucHV0Tm9kZShub2RlKSkge1xuICAgIHJldHVybiBbXTtcbiAgfSBlbHNlIHtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgdGhyb3cgRXJyb3IoJ1Vuc3VwcG9ydGVkIG5vZGUgdHlwZTogJyArIChub2RlLmNvbnN0cnVjdG9yIGFzIGFueSkubmFtZSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBZGQgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIGR5U2l6ZVNjYWxhcjogU2NhbGFyO1xuXG4gIC8qKiBFbGVtZW50LXdpc2UgYWRkIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLiAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDJUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyUGx1c0FycmF5KHgxLCB4Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoeDIsIHgxKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYWRkKHgxLCB4Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDFUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBkeSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLngyVGVuc29yLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5kaXZpZGUoc3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwgZHkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmR5U2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXggZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQW4gQXJnTWF4IG9wZXJhdGlvbi5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5hcmdNYXgoeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FyZ01heCBiYWNrcHJvcCB1bmltcGxlbWVudGVkJyk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFyZ01heEVxdWFscyBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBbiBBcmdNYXhFcXVhbHMgb3BlcmF0aW9uLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmFyZ01heEVxdWFscyh4MSwgeDIpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdBcmdNYXhFcXVhbHMgYmFja3Byb3AgdW5pbXBsZW1lbnRlZCcpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4uL21hdGgvY29uY2F0M2RfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTNELCBOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQ29uY2F0M0QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQSBDb25jYXQgM0Qgb3BlcmF0aW9uLlxuICAgKlxuICAgKiBDb25jYXRzIHR3byAzRCB0ZW5zb3JzIGFsb25nIGFuIGF4aXMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLCBwcml2YXRlIGF4aXM6IG51bWJlcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSwgYXhpcyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgY29uY2F0UmVzdWx0ID0gbWF0aC5jb25jYXQzRCh4MSwgeDIsIHRoaXMuYXhpcyk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChjb25jYXRSZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvbmNhdDNEIGJhY2twcm9wIG5vdCBpbXBsZW1lbnRlZC4nKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb252b2x1dGlvbjJEIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSB6ZXJvUGFkOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYSBjb252b2x1dGlvbiBvcCB3aXRoIHRoZSBzcGVjaWZpZWQgcHJvcGVydGllcy5cbiAgICpcbiAgICogQHBhcmFtIGlucHV0U2hhcGUgVGhlIHNoYXBlIG9mIHRoZSBpbnB1dCBuZGFycmF5LlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBmaWx0ZXIgKHJvd3MvY29scyBvZiBzbGlkaW5nIHdpbmRvdykuXG4gICAqIEBwYXJhbSBvdXRwdXREZXB0aCBUaGUgZGVwdGggb2YgdGhlIG91dHB1dCAoTnVtYmVyIG9mIGZpbHRlcnMpLlxuICAgKiBAcGFyYW0gc3RyaWRlIEhvdyBtYW55IHBpeGVscyB0byBzaGlmdCB0aGUgZmlsdGVyIGJ5IHdoZW4gc2xpZGluZy5cbiAgICogICAgIERlZmF1bHRzIHRvIDEuXG4gICAqIEBwYXJhbSB6ZXJvUGFkIEhvdyBtYW55IHBpeGVscyB0byBwYWQgdGhlIGlucHV0IGZyb20gZWFjaCBzaWRlLiBEZWZhdWx0cyB0b1xuICAgKiAgICAgYSB2YWx1ZSBzbyB0aGF0IHRoZSByb3dzIGFuZCBjb2x1bW5zIG9mIHRoZSBvdXRwdXQgbmRhcnJheSBpc1xuICAgKiAgICAgdGhlIHNhbWUgYXMgdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSB3ZWlnaHRzIE9wdGlvbmFsLiBUaGUgd2VpZ2h0cyBvZiB0aGUgZmlsdGVycy5cbiAgICogQHBhcmFtIGJpYXNlcyBPcHRpb25hbC4gVGhlIGJpYXMgdGVybXMgb2YgdGhlIGZpbHRlcnMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgd1RlbnNvcjogVGVuc29yLCBwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBiVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBmaWVsZFNpemU6IG51bWJlcixcbiAgICAgIHByaXZhdGUgb3V0cHV0RGVwdGg6IG51bWJlciwgcHJpdmF0ZSBzdHJpZGUgPSAxLCB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLmFzc2VydFdlaWdodHNTaGFwZSh3VGVuc29yLnNoYXBlKTtcbiAgICB0aGlzLnplcm9QYWQgPSB6ZXJvUGFkICE9IG51bGwgP1xuICAgICAgICB6ZXJvUGFkIDpcbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKFxuICAgICAgICAgICAgdGhpcy54VGVuc29yLnNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgdGhpcy5maWVsZFNpemUsXG4gICAgICAgICAgICB0aGlzLnN0cmlkZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuaXNJbnQodGhpcy56ZXJvUGFkKSxcbiAgICAgICAgYFRoZSB6ZXJvIHBhZGRpbmcgKCR7dGhpcy56ZXJvUGFkfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB3ZWlnaHRzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLndUZW5zb3IpIGFzIEFycmF5NEQ7XG4gICAgY29uc3QgYmlhc2VzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmJUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLmNvbnYyZCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3Qgd2VpZ2h0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy53VGVuc29yKSBhcyBBcnJheTREO1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcikgYXMgQXJyYXkzRDtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCB7ZHcsIGRiLCBkeH0gPVxuICAgICAgICAgIG1hdGguY29udjJkQmFja1Byb3AoeCwgZHksIHdlaWdodHMsIHRoaXMuc3RyaWRlLCB0aGlzLnplcm9QYWQpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMud1RlbnNvciwga2VlcChkdykpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuYlRlbnNvciwga2VlcChkYikpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChkeCkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3NlcnRXZWlnaHRzU2hhcGUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB3ZWlnaHRzU2hhcGVbMF0gPT09IHRoaXMuZmllbGRTaXplICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbMV0gPT09IHRoaXMuZmllbGRTaXplICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbMl0gPT09IHRoaXMueFRlbnNvci5zaGFwZVsyXSAmJlxuICAgICAgICAgICAgd2VpZ2h0c1NoYXBlWzNdID09PSB0aGlzLm91dHB1dERlcHRoLFxuICAgICAgICBgd2VpZ2h0cyBtdXN0IGJlIG9mIHNoYXBlIFske3RoaXMuZmllbGRTaXplfSwke3RoaXMuZmllbGRTaXplfSxgICtcbiAgICAgICAgICAgIGAke3RoaXMueFRlbnNvci5zaGFwZVsyXX0sJHt0aGlzLm91dHB1dERlcHRofV0gYnV0IHRoZXkgYXJlIG9mYCArXG4gICAgICAgICAgICBgc2hhcGUgWyR7d2VpZ2h0c1NoYXBlfV1gKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIERpdmlkZSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgb25lczogTkRBcnJheTtcblxuICAvKipcbiAgICogRWxlbWVudC13aXNlIGRpdmlkZSBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJEaXZpZGVkQnlBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIodDEsIHQyKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguZGl2aWRlKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBjb25zdCB4MUlzU2NhbGFyID0gdXRpbC5pc1NjYWxhclNoYXBlKHgxLnNoYXBlKTtcbiAgICBjb25zdCB4MklzU2NhbGFyID0gdXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGlmICh4MUlzU2NhbGFyKSB7XG4gICAgICAgICAgY29uc3QgZGl2ID0gbWF0aC5kaXZpZGUoZHksIHgyKTtcblxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc3VtKGRpdikpKTtcblxuICAgICAgICAgIGRpdi5kaXNwb3NlKCk7XG4gICAgICAgIH0gZWxzZSBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLmFycmF5RGl2aWRlZEJ5U2NhbGFyKGR5LCB4MikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcChtYXRoLmRpdmlkZShkeSwgeDIpKSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgLy8gZHgyID0gLTEgKiB4MSAqIHgyIF4gLTIuXG4gICAgICAgIGNvbnN0IHgyU3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIHgyKTtcblxuICAgICAgICBsZXQgeDFPdmVyWDJTcXVhcmVkOiBOREFycmF5O1xuICAgICAgICBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIHgxT3ZlclgyU3F1YXJlZCA9IG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH0gZWxzZSBpZiAoeDFJc1NjYWxhcikge1xuICAgICAgICAgIHgxT3ZlclgyU3F1YXJlZCA9IG1hdGguc2NhbGFyRGl2aWRlZEJ5QXJyYXkoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5kaXZpZGUoeDEsIHgyU3F1YXJlZCk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBkeDIgPSBtYXRoLm5lZyh4MU92ZXJYMlNxdWFyZWQpO1xuICAgICAgICBjb25zdCBkeVRpbWVzRGVyaXZhdGl2ZSA9IG1hdGguZWxlbWVudFdpc2VNdWwoZHksIGR4Mik7XG5cbiAgICAgICAgaWYgKHgySXNTY2FsYXIpIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChtYXRoLnN1bShkeVRpbWVzRGVyaXZhdGl2ZSkpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChkeVRpbWVzRGVyaXZhdGl2ZSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7QWN0aXZhdGlvbkZ1bmN0aW9uLCBSZUxVRnVuYywgU2lnbW9pZEZ1bmMsIFNxdWFyZUZ1bmMsIFRhbkhGdW5jfSBmcm9tICcuLi9tYXRoL2FjdGl2YXRpb25fZnVuY3Rpb25zJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJvdGVjdGVkIHhUZW5zb3I6IFRlbnNvciwgcHJvdGVjdGVkIHlUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgZnVuYzogQWN0aXZhdGlvbkZ1bmN0aW9uKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHRoaXMuZnVuYy5vdXRwdXQobWF0aCwgeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICAvLyBkRS9keF9pID0gc3VtX2ogZEUvZHlfaiAqIGR5X2ovZHhfaVxuICAgIC8vICAgICAgICAgPSBkRS9keV9pICogZHlfaS9keF9pXG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcbiAgICBjb25zdCB5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBkeWR4ID0gdGhpcy5mdW5jLmRlcihtYXRoLCB4LCB5KTtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bChkeSwgZHlkeCkpKTtcbiAgICAgIGR5ZHguZGlzcG9zZSgpO1xuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgUmVMVSBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFJlTFVGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgVGFuSCBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFRhbkhGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU2lnbW9pZCBleHRlbmRzIEVsZW1lbnRXaXNlQWN0aXZhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHhUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeFRlbnNvciwgeVRlbnNvciwgbmV3IFNpZ21vaWRGdW5jKCkpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3F1YXJlIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgU3F1YXJlRnVuYygpKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7RWxlbWVudFdpc2VDb3N0RnVuY3Rpb24sIFNxdWFyZUNvc3RGdW5jfSBmcm9tICcuLi9tYXRoL2Nvc3RfZnVuY3Rpb25zJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEVsZW1lbnRXaXNlQ29zdDxUIGV4dGVuZHMgTkRBcnJheT4gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIG9uZU92ZXJOU2NhbGFyOiBTY2FsYXI7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcm90ZWN0ZWQgeDFUZW5zb3I6IFRlbnNvciwgcHJvdGVjdGVkIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcm90ZWN0ZWQgeVRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgZnVuYzogRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24pIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMub25lT3Zlck5TY2FsYXIgPSBTY2FsYXIubmV3KDEgLyB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGVsZW1lbnRXaXNlQ29zdCA9IHRoaXMuZnVuYy5jb3N0KG1hdGgsIHgxLCB4Mik7XG4gICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShlbGVtZW50V2lzZUNvc3QpO1xuICAgICAgY29uc3QgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHRoaXMub25lT3Zlck5TY2FsYXIsIHN1bSk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAodGhpcy5mdW5jLmRlcihtYXRoLCB4MSwgeDIpKSk7XG4gICAgICB9XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcCh0aGlzLmZ1bmMuZGVyKG1hdGgsIHgyLCB4MSkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5mdW5jLmRpc3Bvc2UoKTtcbiAgICB0aGlzLm9uZU92ZXJOU2NhbGFyLmRpc3Bvc2UoKTtcbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1lYW5TcXVhcmVkQ29zdCBleHRlbmRzIEVsZW1lbnRXaXNlQ29zdDxBcnJheTFEPiB7XG4gIGNvbnN0cnVjdG9yKHgxVGVuc29yOiBUZW5zb3IsIHgyVGVuc29yOiBUZW5zb3IsIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKHgxVGVuc29yLCB4MlRlbnNvciwgeVRlbnNvciwgbmV3IFNxdWFyZUNvc3RGdW5jKCkpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRXhwIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEV4cG9uZW50YXRpb24gb3BlcmF0aW9uIC0gZV54LlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKG1hdGguZXhwKHgpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54VGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54VGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeSwgZHkpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTGluZWFyQ29tYmluYXRpb24gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogQSAyLXRlbnNvciBsaW5lYXIgY29tYmluYXRpb24gb3BlcmF0aW9uLlxuICAgKlxuICAgKiBDb21iaW5lcyB0ZW5zb3JzIHgxIGFuZCB4MiAob2YgdGhlIHNhbWUgc2hhcGUpIHdpdGggd2VpZ2h0cyBjMSAmIGMyO1xuICAgKiBDb21wdXRlcyBjMSp4MSArIGMyKngyLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgYzFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBjMlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBvdXRUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgYzEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzFUZW5zb3IpLmFzU2NhbGFyKCk7XG4gICAgY29uc3QgYzIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzJUZW5zb3IpLmFzU2NhbGFyKCk7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMub3V0VGVuc29yLCBrZWVwKG1hdGguc2NhbGVkQXJyYXlBZGQoYzEsIHgxLCBjMiwgeDIpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBjMSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMVRlbnNvcik7XG4gICAgY29uc3QgYzIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYzJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShjMSwgZHkpKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShjMiwgZHkpKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMuYzFUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGRvdFByb2R1Y3QxID0gbWF0aC5lbGVtZW50V2lzZU11bCh4MSwgZHkpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5jMVRlbnNvciwga2VlcChtYXRoLnN1bShkb3RQcm9kdWN0MSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy5jMlRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZG90UHJvZHVjdDIgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHgyLCBkeSk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmMyVGVuc29yLCBrZWVwKG1hdGguc3VtKGRvdFByb2R1Y3QyKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTG9nIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIE5hdHVyYWwgbG9nIG9wZXJhdGlvbiAtIGxuKHgpXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5sb2coeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnhUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5kaXZpZGUoZHksIHgpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge01hdHJpeE9yaWVudGF0aW9uLCBOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgTkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1hdE11bCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHgyLnNoYXBlLmxlbmd0aCA9PT0gMikge1xuICAgICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgICAgdGhpcy55VGVuc29yLCBrZWVwKG1hdGgubWF0TXVsKHgxIGFzIEFycmF5MkQsIHgyIGFzIEFycmF5MkQpKSk7XG4gICAgICB9IGVsc2UgaWYgKHgxLnNoYXBlLmxlbmd0aCA9PT0gMiAmJiB4Mi5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICAgIGtlZXAobWF0aC5tYXRyaXhUaW1lc1ZlY3Rvcih4MSBhcyBBcnJheTJELCB4MiBhcyBBcnJheTFEKSkpO1xuICAgICAgfSBlbHNlIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDEgJiYgeDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKG1hdGgudmVjdG9yVGltZXNNYXRyaXgoeDEgYXMgQXJyYXkxRCwgeDIgYXMgQXJyYXkyRCkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBsZXQgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGxldCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgbGV0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB4MSA9IHgxLnJlc2hhcGUoWzEsIHgxLnNpemVdKTtcbiAgICAgIGR5ID0gZHkucmVzaGFwZShbMSwgZHkuc2l6ZV0pO1xuICAgIH1cbiAgICBpZiAoeDIuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB4MiA9IHgyLnJlc2hhcGUoW3gyLnNpemUsIDFdKTtcbiAgICAgIGR5ID0gZHkucmVzaGFwZShbZHkuc2l6ZSwgMV0pO1xuICAgIH1cblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIC8vIHkgPSB4MSAqIHgyXG4gICAgICAvLyBkeDEgPSBkeSAqIHgyVFxuICAgICAgLy8gZHgyID0geDFUICogZHlcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGR4MSA9IG1hdGgubWF0TXVsKFxuICAgICAgICAgICAgZHkgYXMgQXJyYXkyRCwgeDIgYXMgQXJyYXkyRCwgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUixcbiAgICAgICAgICAgIE1hdHJpeE9yaWVudGF0aW9uLlRSQU5TUE9TRUQpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLngxVGVuc29yLFxuICAgICAgICAgICAga2VlcCh0aGlzLngxVGVuc29yLnNoYXBlLmxlbmd0aCA9PT0gMSA/IGR4MS5hczFEKCkgOiBkeDEpKTtcbiAgICAgIH1cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGNvbnN0IGR4MiA9IG1hdGgubWF0TXVsKFxuICAgICAgICAgICAgeDEgYXMgQXJyYXkyRCwgZHkgYXMgQXJyYXkyRCwgTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRCxcbiAgICAgICAgICAgIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpO1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLngyVGVuc29yLFxuICAgICAgICAgICAga2VlcCh0aGlzLngyVGVuc29yLnNoYXBlLmxlbmd0aCA9PT0gMSA/IGR4Mi5hczFEKCkgOiBkeDIpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MkQsIEFycmF5M0QsIE5EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNYXhQb29sIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBwYWQ6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgZmllbGRTaXplOiBudW1iZXIsIHByaXZhdGUgc3RyaWRlID0gMSwgcGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoKTtcblxuICAgIGlmIChwYWQgIT0gbnVsbCkge1xuICAgICAgdGhpcy5wYWQgPSBwYWQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMucGFkID0gY29udl91dGlsLmNvbXB1dGVEZWZhdWx0UGFkKFxuICAgICAgICAgIHhUZW5zb3Iuc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB0aGlzLmZpZWxkU2l6ZSxcbiAgICAgICAgICB0aGlzLnN0cmlkZSk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuaXNJbnQodGhpcy5wYWQpLFxuICAgICAgICBgVGhlIHplcm8gcGFkZGluZyAoJHt0aGlzLnBhZH0pIG11c3QgYmUgYW4gaW50ZWdlci4gQ2hhbmdlIHRoZSBgICtcbiAgICAgICAgICAgIGBzdHJpZGUgYW5kL29yIHplcm8gcGFkIHBhcmFtZXRlcnNgKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgIGtlZXAobWF0aC5tYXhQb29sKHgsIHRoaXMuZmllbGRTaXplLCB0aGlzLnN0cmlkZSwgdGhpcy5wYWQpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgQXJyYXkzRDtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnhUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLm1heFBvb2xCYWNrcHJvcChcbiAgICAgICAgICAgICAgZHksIHgsIHRoaXMuZmllbGRTaXplLCB0aGlzLnN0cmlkZSwgdGhpcy5wYWQpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNdWx0aXBseSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2UgbXVsdGlwbHkgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDFUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUoeDJUZW5zb3Iuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHgxVGVuc29yLnNoYXBlLCB4MlRlbnNvci5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB0MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgdDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyVGltZXNBcnJheSh0MiwgdDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5lbGVtZW50V2lzZU11bCh0MSwgdDIpO1xuICAgICAgfVxuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAocmVzdWx0KSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLngxVGVuc29yLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IG11bCA9IG1hdGguZWxlbWVudFdpc2VNdWwoZHksIHgyKTtcblxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguc3VtKG11bCkpKTtcblxuICAgICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4Mi5zaGFwZSkpIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KHgyLCBkeSkpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcChtYXRoLmVsZW1lbnRXaXNlTXVsKHgyLCBkeSkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDJUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3QgbXVsID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgeDEpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5zdW0obXVsKSkpO1xuXG4gICAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgxLnNoYXBlKSkge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MlRlbnNvciwga2VlcChtYXRoLnNjYWxhclRpbWVzQXJyYXkoeDEsIGR5KSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeDEsIGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgT3BlcmF0aW9uIHtcbiAgYWJzdHJhY3QgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApOlxuICAgICAgdm9pZDtcblxuICBhYnN0cmFjdCBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKTogdm9pZDtcblxuICBkaXNwb3NlVHJhbnNpZW50QXJyYXlzKFxuICAgICAgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7fVxuXG4gIGRpc3Bvc2UoKSB7fVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZWR1Y2VTdW0gZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKiogRWxlbWVudC13aXNlIGFkZCBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzIHNjYWxhci4gKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgb3V0VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2gob3V0VGVuc29yLnNoYXBlLCBbXSk7XG4gIH1cblxuICBwcml2YXRlIG9uZXM6IE5EQXJyYXk7XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLm91dFRlbnNvciwga2VlcChtYXRoLnN1bSh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGlmICghZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuICAgICAgaWYgKHRoaXMub25lcyA9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IHhBcnJheSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54KTtcbiAgICAgICAgdGhpcy5vbmVzID0gTkRBcnJheS56ZXJvc0xpa2UoeEFycmF5KTtcbiAgICAgICAgdGhpcy5vbmVzLmZpbGwoMSk7XG4gICAgICB9XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54LCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheShkeSwgdGhpcy5vbmVzKSkpO1xuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBSZXNoYXBlPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgY29uc3QgeFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoeFRlbnNvci5zaGFwZSk7XG4gICAgY29uc3QgeVNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoeVRlbnNvci5zaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHhTaXplID09PSB5U2l6ZSxcbiAgICAgICAgYFRoZSBpbnB1dCBzaXplICgke3hTaXplfSkgYW5kIG91dHB1dCBzaXplICgke3lTaXplfSkgbXVzdCBtYXRjaGApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIFQxO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5yZXNoYXBlPFQxLCBUMj4oeCwgdGhpcy55VGVuc29yLnNoYXBlKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgVDI7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy54VGVuc29yLCBrZWVwKG1hdGgucmVzaGFwZTxUMiwgVDE+KGR5LCB0aGlzLnhUZW5zb3Iuc2hhcGUpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG5leHBvcnQgY2xhc3MgU29mdG1heCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbG9naXRzVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgb3V0cHV0OiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBsb2dpdHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubG9naXRzVGVuc29yKSBhcyBBcnJheTFEO1xuICAgIHJldHVybiBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0cHV0LCBrZWVwKG1hdGguc29mdG1heChsb2dpdHMpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcCgpIHtcbiAgICB0aHJvdyBFcnJvcignU29mdG1heCBiYWNrcHJvcCBpcyBub3QgeWV0IGltcGxlbWVudGVkJyk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIGxvZ2l0c1RlbnNvcjogVGVuc29yLCBwcml2YXRlIGxhYmVsVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5zb2Z0bWF4VGVuc29yID0gbmV3IFRlbnNvcihsb2dpdHNUZW5zb3Iuc2hhcGUpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBsb2dpdHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubG9naXRzVGVuc29yKSBhcyBBcnJheTFEO1xuICAgIGNvbnN0IGxhYmVsID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmxhYmVsVGVuc29yKSBhcyBBcnJheTFEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3Qgc29mdG1heFJlc3VsdCA9IG1hdGguc29mdG1heChsb2dpdHMpO1xuXG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMuc29mdG1heFRlbnNvciwga2VlcChzb2Z0bWF4UmVzdWx0KSk7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICBrZWVwKGNyb3NzRW50cm9weUNvc3QobWF0aCwgc29mdG1heFJlc3VsdCwgbGFiZWwsIHRoaXMuZXBzaWxvbikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBzb2Z0bWF4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnNvZnRtYXhUZW5zb3IpO1xuICAgIGNvbnN0IGxhYmVsID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmxhYmVsVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmxvZ2l0c1RlbnNvciwga2VlcChtYXRoLnN1Yihzb2Z0bWF4LCBsYWJlbCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2VUcmFuc2llbnRBcnJheXMoXG4gICAgICBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBpbmZlcmVuY2VBcnJheXMuZGlzcG9zZUFycmF5KHRoaXMuc29mdG1heFRlbnNvcik7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIHRoaXMuZXBzaWxvbi5kaXNwb3NlKCk7XG4gIH1cblxuICBwcml2YXRlIHNvZnRtYXhUZW5zb3I6IFRlbnNvcjtcbiAgcHJpdmF0ZSBlcHNpbG9uID0gU2NhbGFyLm5ldygxZS01KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyb3NzRW50cm9weUNvc3QoXG4gICAgbWF0aDogTkRBcnJheU1hdGgsIHk6IEFycmF5MUQsIHRhcmdldDogQXJyYXkxRCwgZXBzaWxvbjogU2NhbGFyKTogU2NhbGFyIHtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB5LnNpemUgPT09IHRhcmdldC5zaXplLCAnVGhlIG91dHB1dCBhbmQgdGFyZ2V0IG11c3QgYmUgdGhlIHNhbWUgc2l6ZScpO1xuXG4gIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICBjb25zdCB5UGx1c0VwcyA9IG1hdGguc2NhbGFyUGx1c0FycmF5KGVwc2lsb24sIHkpO1xuICAgIGNvbnN0IGxvZ091dHB1dCA9IG1hdGgubG9nKHlQbHVzRXBzKTtcbiAgICBjb25zdCB0YXJMb2dPdXRwdXQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHRhcmdldCwgbG9nT3V0cHV0KTtcbiAgICBjb25zdCBjb3N0VmVjdG9yID0gbWF0aC5uZWcodGFyTG9nT3V0cHV0KTtcbiAgICByZXR1cm4gbWF0aC5zdW0oY29zdFZlY3Rvcik7XG4gIH0pO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIFNwbGl0IG9wcyBhcmUgdXNlZCB0byBhY2N1bXVsYXRlIGJhY2twcm9wIGRlcml2YXRpdmVzIHdoZW4gYSBub2RlJ3Mgb3V0cHV0XG4gKiB0ZW5zb3IgaXMgY29uc3VtZWQgYnkgbXVsdGlwbGUgbm9kZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcGxpdCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgaW5wdXQ6IFRlbnNvciwgcHJpdmF0ZSBvdXRwdXRzOiBUZW5zb3JbXSkge1xuICAgIHN1cGVyKCk7XG4gICAgb3V0cHV0cy5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGlucHV0LnNoYXBlLCBvdXRwdXQuc2hhcGUpO1xuICAgIH0pO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCBpbnB1dEFycmF5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmlucHV0KTtcbiAgICB0aGlzLm91dHB1dHMuZm9yRWFjaChvdXRwdXQgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChvdXRwdXQsIGlucHV0QXJyYXkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGlmICghZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLmlucHV0KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCBkeCA9IG1hdGguYWRkKFxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dHB1dHNbMF0pLFxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dHB1dHNbMV0pKTtcbiAgICAgIC8vIFN1bSBhY3Jvc3MgYWxsIHRoZSBkZXJpdmF0aXZlcyBvZiB0aGUgY29uc3VtZXJzIG9mIHRoaXMgbm9kZS5cbiAgICAgIHRoaXMub3V0cHV0cy5zbGljZSgyKS5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICAgIGR4ID0gbWF0aC5hZGQoZHgsIGdyYWRpZW50QXJyYXlzLmdldChvdXRwdXQpKTtcbiAgICAgIH0pO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuaW5wdXQsIGtlZXAoZHgpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG5leHBvcnQgY2xhc3MgU3VidHJhY3QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIGR5U2l6ZVNjYWxhcjogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2Ugc3VidHJhY3Qgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpc1xuICAgKiBzY2FsYXIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yLCBwcml2YXRlIG91dFRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0MS5zaGFwZSwgdDIuc2hhcGUpLFxuICAgICAgICAnT25lIG9mIHQxIG9yIHQyIG11c3QgYmUgYSBzY2FsYXIsIG9yIHQxIGFuZCB0MiBtdXN0IGhhdmUgJyArXG4gICAgICAgICAgICAndGhlIHNhbWUgc2hhcGUnKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDEpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQyKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCByZXN1bHQ6IE5EQXJyYXk7XG4gICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQxLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhck1pbnVzQXJyYXkodDEsIHQyKTtcbiAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQyLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLmFycmF5TWludXNTY2FsYXIodDEsIHQyKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc3ViKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQxKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy50Mik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy50MSkpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLnQxLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMudDEsIGtlZXAobWF0aC5kaXZpZGUoc3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy50MSwga2VlcChkeSkpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMudDIpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy50Mi5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShkeSk7XG4gICAgICAgICAgY29uc3QgbmVnU3VtID0gbWF0aC5uZWcoc3VtKTtcbiAgICAgICAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgPT0gbnVsbCkge1xuICAgICAgICAgICAgdGhpcy5keVNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGR5LnNpemUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQoXG4gICAgICAgICAgICAgIHRoaXMudDIsIGtlZXAobWF0aC5kaXZpZGUobmVnU3VtLCB0aGlzLmR5U2l6ZVNjYWxhcikpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy50Miwga2VlcChtYXRoLm5lZyhkeSkpKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5keVNpemVTY2FsYXIgIT0gbnVsbCkge1xuICAgICAgdGhpcy5keVNpemVTY2FsYXIuZGlzcG9zZSgpO1xuICAgIH1cbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7U2Vzc2lvblJ1bnRpbWV9IGZyb20gJy4vc2Vzc2lvbic7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgT3B0aW1pemVyIHtcbiAgcHJvdGVjdGVkIHZhcmlhYmxlTm9kZXM6IFZhcmlhYmxlTm9kZVtdO1xuICBwcm90ZWN0ZWQgc3BlY2lmaWVkVmFyaWFibGVOb2RlczogVmFyaWFibGVOb2RlW118bnVsbDtcblxuICBjb25zdHJ1Y3RvcihzcGVjaWZpZWRWYXJpYWJsZUxpc3Q/OiBOb2RlW10pIHtcbiAgICBpZiAoc3BlY2lmaWVkVmFyaWFibGVMaXN0ICE9IG51bGwpIHtcbiAgICAgIHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcyA9IHNwZWNpZmllZFZhcmlhYmxlTGlzdCBhcyBWYXJpYWJsZU5vZGVbXTtcbiAgICB9XG4gIH1cblxuICBhYnN0cmFjdCBiZWZvcmVCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGFic3RyYWN0IGFmdGVyRXhhbXBsZShcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgYWZ0ZXJCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGFic3RyYWN0IGRpc3Bvc2UoKTogdm9pZDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuLyoqXG4gKiBEZWZhdWx0IGNvbXBhcmlzb24gZnVuY3Rpb24gZm9yIHRoZSBwcmlvcml0eSBxdWV1ZS5cbiAqIEBwYXJhbSBhIFRoZSBmaXJzdCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0gYiBUaGUgc2Vjb25kIGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqIEByZXR1cm4gXCJhID4gYlwiIHJldHVybnMgPiAwLiBcImEgPCBiXCIgcmV0dXJucyA8IDAuIFwiYSA9PT0gYlwiIHJldHVybnMgMC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmF1bHRDb21wYXJlPFQ+KGE6IFQsIGI6IFQpOiBudW1iZXIge1xuICBpZiAoYSA9PT0gYikge1xuICAgIHJldHVybiAwO1xuICB9IGVsc2UgaWYgKGEgPCBiKSB7XG4gICAgcmV0dXJuIC0xO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiAxO1xuICB9XG59XG5cbi8qKlxuICogQSBDb21wYXJhdG9yIGlzIGEgdXNlci1wcm92aWRlZCBmdW5jdGlvbiB0aGF0IGNvbXBhcmVzIHR3byBUIGluc3RhbmNlcy4gVGhlXG4gKiBjb252ZW50aW9uIGZvciBkZWZhdWx0Q29tcGFyZSBpcyBleHBlY3RlZCB0byBiZSBmb2xsb3dlZCB0byBtYWludGFpbiB0aGVcbiAqIGJpbmFyeSBtaW4taGVhcCBpbnRlZ3JpdHkuXG4gKiBAcGFyYW0gYSBUaGUgZmlyc3QgZWxlbWVudCB0byBjb21wYXJlLlxuICogQHBhcmFtIGIgVGhlIHNlY29uZCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKi9cbmV4cG9ydCB0eXBlIENvbXBhcmF0b3I8VD4gPSAoYTogVCwgYjogVCkgPT4gbnVtYmVyO1xuXG4vKipcbiAqIEluZGV4T2JzZXJ2ZXIgaXMgYSB1c2VyLXByb3ZpZGVkIGNhbGxiYWNrIHRoYXQgaW5mb3JtcyB0aGUgY2FsbGVyIHdoZW4gYW5cbiAqIGVsZW1lbnQgaW4gdGhlIHByaW9yaXR5IHF1ZXVlJ3MgYmluYXJ5IG1pbi1oZWFwIGhhcyBiZWVuIHJlbG9jYXRlZC5cbiAqIEBwYXJhbSB0IFRoZSBlbGVtZW50IHRoYXQgd2FzIHJlbG9jYXRlZC5cbiAqIEBwYXJhbSBuZXdJbmRleCBUaGUgbmV3IGxvY2F0aW9uIGluIHRoZSBiaW5hcnkgbWluLWhlYXAgb2YgdGhlIGVsZW1lbnQuXG4gKi9cbmV4cG9ydCB0eXBlIEluZGV4T2JzZXJ2ZXI8VD4gPSAodDogVCwgbmV3SW5kZXg6IG51bWJlcikgPT4gdm9pZDtcblxuLyoqXG4gKiBBIHByaW9yaXR5IHF1ZXVlLCBpbXBsZW1lbnRlZCBpbiB0ZXJtcyBvZiBhIGJpbmFyeSBtaW4taGVhcC4gTG93ZXIgcHJpb3JpdHlcbiAqIG51bWJlcnMgYXJlIGNvbnNpZGVyZWQgaGlnaGVyIHByaW9yaXR5LlxuICogZW5xdWV1ZSwgZGVxdWV1ZSwgYW5kIHVwZGF0ZSBhcmUgYWxsIE8obG9nIE4pIHdpdGggcmVzcGVjdCB0byB0aGUgbnVtYmVyIG9mXG4gKiBlbGVtZW50cyBpbiB0aGUgcXVldWUuXG4gKi9cbmV4cG9ydCBjbGFzcyBQcmlvcml0eVF1ZXVlPFQ+IHtcbiAgcHJpdmF0ZSBoZWFwOiBUW10gPSBbXTtcblxuICAvKipcbiAgICogQHBhcmFtIGNvbXBhcmF0b3IgQSBmdW5jdGlvbiB0aGF0IGNvbXBhcmVzIHR3byBxdWV1ZSBlbGVtZW50cy5cbiAgICogQHBhcmFtIGluZGV4T2JzZXJ2ZXIgQW4gb3B0aW9uYWwgY2FsbGJhY2sgcmFpc2VkIHdoZW4gdGhlIHByaW9yaXR5IHF1ZXVlXG4gICAqIGNoYW5nZXMgdGhlIG9yZGVyIG9mIGVsZW1lbnRzIGluIGl0cyBtaW4taGVhcC4gVXNlZnVsIGZvciB0cmFja2luZyB0aGVcbiAgICogcG9zaXRpb25zIG9mIGVsZW1lbnRzIHRoYXQgbmVlZCB1cGRhdGluZy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBjb21wYXJhdG9yOiBDb21wYXJhdG9yPFQ+LFxuICAgICAgcHJpdmF0ZSBpbmRleE9ic2VydmVyPzogSW5kZXhPYnNlcnZlcjxUPikge31cblxuICAvKipcbiAgICogQWRkIGFuIGVsZW1lbnQgdG8gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcGFyYW0gdCBUaGUgZWxlbWVudCB0byBlbnF1ZXVlLlxuICAgKi9cbiAgZW5xdWV1ZSh0OiBUKSB7XG4gICAgdGhpcy5oZWFwLnB1c2godCk7XG4gICAgdGhpcy5vbkluZGV4Q2hhbmdlZCh0LCB0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gICAgdGhpcy5zaWZ0VXAodGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZSBhbiBlbGVtZW50IGZyb20gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcmV0dXJuIFRoZSBlbGVtZW50IGluIHRoZSBwcmlvcml0eSBxdWV1ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5XG4gICAqIChsb3dlc3QgbnVtZXJpYyBwcmlvcml0eSB2YWx1ZSkuXG4gICAqL1xuICBkZXF1ZXVlKCk6IFQge1xuICAgIGlmICh0aGlzLmVtcHR5KCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignZGVxdWV1ZSBjYWxsZWQgb24gZW1wdHkgcHJpb3JpdHkgcXVldWUuJyk7XG4gICAgfVxuICAgIGNvbnN0IHQgPSB0aGlzLmhlYXBbMF07XG4gICAgdGhpcy5zd2FwKDAsIHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICB0aGlzLmhlYXAucG9wKCk7XG4gICAgdGhpcy5zaWZ0RG93bigwKTtcbiAgICByZXR1cm4gdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIGFuIGVsZW1lbnQgYXQgdGhlIHNwZWNpZmllZCBpbmRleC4gVGhpcyBjYW4gYmUgYSBmdWxsIGVsZW1lbnRcbiAgICogcmVwbGFjZW1lbnQsIG9yIGl0IGNhbiBiZSBhbiBpbi1wbGFjZSB1cGRhdGUuIFRoZSBwcmlvcml0eSBpcyBhc3N1bWVkIHRvIGJlXG4gICAqIGNoYW5nZWQsIGFuZCB0aGUgaW50ZXJuYWwgc3RvcmFnZSBpcyB1cGRhdGVkLiBUaGlzIGZ1bmN0aW9uIGlzIG9ubHkgdXNlZnVsXG4gICAqIGlmIHRoZSBzdG9yYWdlIGluZGV4IG9mIHRoZSB1cGRhdGVkIGVsZW1lbnQgaXMga25vd247IGNvbnN0cnVjdCB0aGVcbiAgICogUHJpb3JpdHlRdWV1ZSB3aXRoIGFuIEluZGV4T2JzZXJ2ZXIgdG8gdHJhY2sgZWxlbWVudCBsb2NhdGlvbnMuXG4gICAqIEBwYXJhbSBuZXdUIFRoZSBuZXcgZWxlbWVudCB0byByZXBsYWNlIGluIHRoZSBwcmlvcml0eSBxdWV1ZS5cbiAgICogQHBhcmFtIGluZGV4IFRoZSBpbmRleCB0byBpbnNlcnQgdGhlIG5ldyBlbGVtZW50IGludG8uXG4gICAqL1xuICB1cGRhdGUobmV3VDogVCwgaW5kZXg6IG51bWJlcikge1xuICAgIC8qIElmIHRoZSBlbGVtZW50IGlzIGF0IHRoZSB2ZXJ5IGVuZCBvZiB0aGUgaGVhcCwgbm8gc2lmdGluZyBpcyBuZWNlc3NhcnksXG4gICAgICogaXQgY2FuIGJlIHNhZmVseSByZW1vdmVkLiAqL1xuICAgIGNvbnN0IGxhc3QgPSAoaW5kZXggPT09IHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICBpZiAoIWxhc3QpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIH1cbiAgICB0aGlzLmhlYXAucG9wKCk7XG4gICAgaWYgKCFsYXN0KSB7XG4gICAgICAvKiBUaGUgZWxlbWVudCBhdCAnaW5kZXgnIGhhcyBiZWVuIHJlbW92ZWQsIGFuZCByZXBsYWNlZCB3aXRoIHdoYXRldmVyIHdhc1xuICAgICAgICogYXQgdGhlIGVuZCBvZiB0aGUgaGVhcC4gU2luY2UgdGhhdCBlbGVtZW50IG1pZ2h0IGhhdmUgY29tZSBmcm9tIGFcbiAgICAgICAqIGRpZmZlcmVudCBzdWJ0cmVlIChhbmQgbm90IGJlIGEgZGlyZWN0IGRlc2NlbmRhbnQgb2YgdGhlIG5vZGUgYXRcbiAgICAgICAqICdpbmRleCcpLCB3ZSBtaWdodCBuZWVkIHRvIHNpZnQgdGhpcyBuZXcgdmFsdWUgdXAgaW5zdGVhZCBvZiBkb3duLiBUZXN0XG4gICAgICAgKiBib3RoIGRpcmVjdGlvbnMsIGFuZCBzaWZ0IHRvIHdoZXJldmVyIHRoZSBub2RlIG5lZWRzIHRvIGdvLlxuICAgICAgICovXG4gICAgICBpZiAodGhpcy5zaWZ0VXBJbmRleChpbmRleCkgIT09IC0xKSB7XG4gICAgICAgIHRoaXMuc2lmdFVwKGluZGV4KTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KSAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5zaWZ0RG93bihpbmRleCk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuZW5xdWV1ZShuZXdUKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmVkaWNhdGUgZm9yIHRlc3Rpbmcgd2hldGhlciB0aGUgUHJpb3JpdHlRdWV1ZSBpcyBlbXB0eS5cbiAgICogQHJldHVybiBUcnVlIGlmIHRoZSBQcmlvcml0eVF1ZXVlIGlzIGVtcHR5LCBvdGhlcndpc2UgRmFsc2UuXG4gICAqL1xuICBlbXB0eSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5oZWFwLmxlbmd0aCA9PT0gMDtcbiAgfVxuXG4gIHByaXZhdGUgb25JbmRleENoYW5nZWQodDogVCwgbmV3SW5kZXg6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmluZGV4T2JzZXJ2ZXIpIHtcbiAgICAgIHRoaXMuaW5kZXhPYnNlcnZlcih0LCBuZXdJbmRleCk7XG4gICAgfVxuICB9XG5cbiAgLypcbiAgICogU3RhbmRhcmQgemVyby1pbmRleGVkIGJpbmFyeSBoZWFwIGFycmF5IGxheW91dDpcbiAgICogICBQYXJlbnQoTikgPSBGbG9vcigoTiAtIDEpIC8gMilcbiAgICogICBMZWZ0Q2hpbGQoTikgPSAoTiAqIDIpICsgMVxuICAgKiAgIFJpZ2h0Q2hpbGQoTikgPSAoTiAqIDIpICsgMlxuICAgKi9cblxuICBwcml2YXRlIGdldFBhcmVudEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmIChpbmRleCA9PT0gMCkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICByZXR1cm4gTWF0aC5mbG9vcigoaW5kZXggLSAxKSAvIDIpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRMZWZ0Q2hpbGRJbmRleChpbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBjYW5kaWRhdGUgPSBpbmRleCAqIDIgKyAxO1xuICAgIHJldHVybiBjYW5kaWRhdGUgPCB0aGlzLmhlYXAubGVuZ3RoID8gY2FuZGlkYXRlIDogLTE7XG4gIH1cblxuICBwcml2YXRlIGdldFJpZ2h0Q2hpbGRJbmRleChpbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICBjb25zdCBjYW5kaWRhdGUgPSBpbmRleCAqIDIgKyAyO1xuICAgIHJldHVybiBjYW5kaWRhdGUgPCB0aGlzLmhlYXAubGVuZ3RoID8gY2FuZGlkYXRlIDogLTE7XG4gIH1cblxuICBwcml2YXRlIHNpZnRVcEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IHBhcmVudEluZGV4ID0gdGhpcy5nZXRQYXJlbnRJbmRleChpbmRleCk7XG4gICAgaWYgKHBhcmVudEluZGV4ID09PSAtMSkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICBpZiAodGhpcy5jb21wYXJlKHBhcmVudEluZGV4LCBpbmRleCkgPiAwKSB7XG4gICAgICByZXR1cm4gcGFyZW50SW5kZXg7XG4gICAgfVxuICAgIHJldHVybiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgc2lmdFVwKGluZGV4OiBudW1iZXIpIHtcbiAgICBsZXQgc2lmdEluZGV4ID0gdGhpcy5zaWZ0VXBJbmRleChpbmRleCk7XG4gICAgd2hpbGUgKHNpZnRJbmRleCAhPT0gLTEpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgc2lmdEluZGV4KTtcbiAgICAgIGluZGV4ID0gc2lmdEluZGV4O1xuICAgICAgc2lmdEluZGV4ID0gdGhpcy5zaWZ0VXBJbmRleChpbmRleCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0RG93bkluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGlmIChpbmRleCA+PSB0aGlzLmhlYXAubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGxldCBsYXJnZXN0Q2hpbGRJbmRleCA9IGluZGV4O1xuICAgIGNvbnN0IGxlZnRDaGlsZEluZGV4ID0gdGhpcy5nZXRMZWZ0Q2hpbGRJbmRleChpbmRleCk7XG4gICAgaWYgKChsZWZ0Q2hpbGRJbmRleCAhPT0gLTEpICYmXG4gICAgICAgICh0aGlzLmNvbXBhcmUobGVmdENoaWxkSW5kZXgsIGxhcmdlc3RDaGlsZEluZGV4KSA8IDApKSB7XG4gICAgICBsYXJnZXN0Q2hpbGRJbmRleCA9IGxlZnRDaGlsZEluZGV4O1xuICAgIH1cbiAgICBjb25zdCByaWdodENoaWxkSW5kZXggPSB0aGlzLmdldFJpZ2h0Q2hpbGRJbmRleChpbmRleCk7XG4gICAgaWYgKChyaWdodENoaWxkSW5kZXggIT09IC0xKSAmJlxuICAgICAgICAodGhpcy5jb21wYXJlKHJpZ2h0Q2hpbGRJbmRleCwgbGFyZ2VzdENoaWxkSW5kZXgpIDwgMCkpIHtcbiAgICAgIGxhcmdlc3RDaGlsZEluZGV4ID0gcmlnaHRDaGlsZEluZGV4O1xuICAgIH1cbiAgICByZXR1cm4gKGxhcmdlc3RDaGlsZEluZGV4ID09PSBpbmRleCkgPyAtMSA6IGxhcmdlc3RDaGlsZEluZGV4O1xuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0RG93bihpbmRleDogbnVtYmVyKSB7XG4gICAgbGV0IHNpZnRJbmRleCA9IHRoaXMuc2lmdERvd25JbmRleChpbmRleCk7XG4gICAgd2hpbGUgKHNpZnRJbmRleCAhPT0gLTEpIHtcbiAgICAgIHRoaXMuc3dhcChpbmRleCwgc2lmdEluZGV4KTtcbiAgICAgIGluZGV4ID0gc2lmdEluZGV4O1xuICAgICAgc2lmdEluZGV4ID0gdGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGNvbXBhcmUoYUluZGV4OiBudW1iZXIsIGJJbmRleDogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5jb21wYXJhdG9yKHRoaXMuaGVhcFthSW5kZXhdLCB0aGlzLmhlYXBbYkluZGV4XSk7XG4gIH1cblxuICBwcml2YXRlIHN3YXAoYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgICBjb25zdCB0ZW1wID0gdGhpcy5oZWFwW2FdO1xuICAgIHRoaXMuaGVhcFthXSA9IHRoaXMuaGVhcFtiXTtcbiAgICB0aGlzLmhlYXBbYl0gPSB0ZW1wO1xuICAgIHRoaXMub25JbmRleENoYW5nZWQodGhpcy5oZWFwW2FdLCBhKTtcbiAgICB0aGlzLm9uSW5kZXhDaGFuZ2VkKHRoaXMuaGVhcFtiXSwgYik7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHcmFwaCwgTm9kZSwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgb3BlcmF0aW9uX2VtaXR0ZXIgZnJvbSAnLi9vcGVyYXRpb25fZW1pdHRlcic7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCAqIGFzIHNlc3Npb25fdXRpbCBmcm9tICcuL3Nlc3Npb25fdXRpbCc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIEZlZWRFbnRyeSBhc3NvY2lhdGVzIGEgdGVuc29yIHdpdGggdXNlci1wcm92aWRlZCBOREFycmF5IGRhdGEuXG4gKi9cbmV4cG9ydCB0eXBlIEZlZWRFbnRyeSA9IHtcbiAgdGVuc29yOiBUZW5zb3IsXG4gIGRhdGE6IE5EQXJyYXl8SW5wdXRQcm92aWRlclxufTtcblxuLyoqXG4gKiBBIEZlZWREaWN0aW9uYXJ5IGhvbGRzIGEgbWFwIGZyb20gdGVuc29ycyB0byB1c2VyLXByb3ZpZGVkIE5EQXJyYXlzLiBGZWVkXG4gKiBkaWN0aW9uYXJpZXMgcmVwcmVzZW50IHRoZSAnZW50cnkgcG9pbnRzJyBvZiBldmFsdWF0aW9uLCBzaW5jZSBncmFwaCBub2Rlc1xuICogdGhhdCBhcmUgcmVwbGFjZWQgYnkgZmVlZHMgZG9uJ3QgbmVlZCB0byBoYXZlIHRoZWlyIGlucHV0IG5vZGVzIGV2YWx1YXRlZC5cbiAqIEZlZWQgZGljdGlvbmFyaWVzIHVzdWFsbHkgcHJvdmlkZSBOREFycmF5IGRhdGEgZm9yIFBsYWNlaG9sZGVyIG5vZGVzLCBidXQgYW55XG4gKiBub2RlIGluIHRoZSBncmFwaCBjYW4gYmUgcmVwbGFjZWQgYnkgYSBmZWVkIGRpY3Rpb25hcnkgZW50cnkuXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRmVlZERpY3Rpb25hcnkge1xuICBkaWN0OiB7W3RlbnNvcklEOiBudW1iZXJdOiBGZWVkRW50cnl9ID0ge307XG5cbiAgLyoqXG4gICAqIE9wdGlvbmFsbHkgY29uc3RydWN0IGEgRmVlZERpY3Rpb25hcnkgZnJvbSBhbiBhcnJheSBvZiBlbnRyaWVzLlxuICAgKiBAcGFyYW0gZmVlZEVudHJpZXMgT3B0aW9uYWwgYXJyYXkgb2YgRmVlZEVudHJ5IG9iamVjdHMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihmZWVkRW50cmllcz86IEZlZWRFbnRyeVtdKSB7XG4gICAgaWYgKGZlZWRFbnRyaWVzKSB7XG4gICAgICBmZWVkRW50cmllcy5mb3JFYWNoKGVudHJ5ID0+IHRoaXMuZGljdFtlbnRyeS50ZW5zb3IuaWRdID0gZW50cnkpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZW51bSBDb3N0UmVkdWN0aW9uIHtcbiAgTk9ORSxcbiAgU1VNLFxuICBNRUFOXG59XG5cbi8qKlxuICogQSBTZXNzaW9uIG1haW50YWlucyB0aGUgcnVudGltZSBzdGF0ZSByZXF1aXJlZCB0byBlZmZpY2llbnRseSBldmFsdWF0ZSBub2Rlcy5cbiAqIE9uIHRoZWlyIG93biwgZ3JhcGggb2JqZWN0cyBhcmUgdmVyeSBsaWdodHdlaWdodCBsb2dpY2FsIHRvcG9sb2dpZXM7IHRoZXlcbiAqIGhhdmUgbm8gcmVsYXRpb25zaGlwIHdpdGggdGhlIEdQVS4gU2Vzc2lvbnMgZW5jYXBzdWxhdGUgdGhlIGV2YWx1YXRpb24gb2ZcbiAqIG5vZGVzLCB0aGUgbWFuYWdlbWVudCBvZiBHUFUgcmVzb3VyY2VzLCB0aGUgY2FjaGluZyBvZiBldmFsdWF0aW9uIHBhdGhzLCBhbmRcbiAqIGFueXRoaW5nIGVsc2UgcmVxdWlyZWQgdG8gZXZhbHVhdGUgb3IgdHJhaW4gYSBuZXR3b3JrLlxuICovXG5leHBvcnQgY2xhc3MgU2Vzc2lvbiB7XG4gIC8qKlxuICAgKiBAcGFyYW0gZ3JhcGggVGhlIGdyYXBoIHRvIGFzc29jaWF0ZSB3aXRoIHRoaXMgU2Vzc2lvbi5cbiAgICogQHBhcmFtIG1hdGggVGhlIE5EQXJyYXlNYXRoIGludGVyZmFjZSB0aGF0IHRoaXMgU2Vzc2lvbiBzaG91bGQgdXNlLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBncmFwaDogR3JhcGgsIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGgpIHt9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2UgYWxsIHN5c3RlbSByZXNvdXJjZXMgYXNzb2NpYXRlZCB3aXRoIHRoaXMgU2Vzc2lvbi5cbiAgICovXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5hY3RpdmF0aW9uQXJyYXlNYXAuZGlzcG9zZSgpO1xuICAgIE9iamVjdC5rZXlzKHRoaXMucnVudGltZUNhY2hlKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBydW50aW1lID0gdGhpcy5ydW50aW1lQ2FjaGVba2V5XTtcbiAgICAgIGlmIChydW50aW1lLm9wZXJhdGlvbnMpIHtcbiAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZGlzcG9zZSgpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnJ1bnRpbWVDYWNoZSA9IHt9O1xuICAgIGlmICh0aGlzLmJhdGNoU2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmJhdGNoU2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHRoaXMub25lU2NhbGFyLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZSBhIGxpc3Qgb2YgdGVuc29ycywgdXNpbmcgdGhlIHByb3ZpZGVkIGZlZWQgZW50cmllcyB0byBwcm92aWRlXG4gICAqIHVwc3RyZWFtIE5EQXJyYXkgaW5wdXQuXG4gICAqIFdoZW4gdXNpbmcgYSBgTkRBcnJheU1hdGhgIG9iamVjdCBpbiBzYWZlIG1vZGUgdGhpcyBtdXN0IGJlIHVzZWQgaW4gYVxuICAgKiBtYXRoLnNjb3BlKCkuXG4gICAqIEBwYXJhbSB0ZW5zb3JzIFRoZSBsaXN0IG9mIHRlbnNvcnMgdG8gZXZhbHVhdGUuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBMaXN0IG9mIGBGZWVkRW50cnlgIHRvIHJlYWQgd2hlbiByZXBsYWNpbmcgZ3JhcGhcbiAgICogdGVuc29ycyB3aXRoIE5EQXJyYXlzLlxuICAgKiBAcmV0dXJuIFRoZSBjb21wdXRlZCB2YWx1ZXMgb2YgdGhlIHRlbnNvcnMuXG4gICAqL1xuICBldmFsQWxsKHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10pOiBOREFycmF5W10ge1xuICAgIHJldHVybiB0aGlzLm1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgY29uc3QgZmVlZCA9IG5ldyBGZWVkRGljdGlvbmFyeShmZWVkRW50cmllcyk7XG4gICAgICBjb25zdCBydW50aW1lID0gdGhpcy5nZXRPckNyZWF0ZVJ1bnRpbWUodGVuc29ycywgZmVlZCk7XG5cbiAgICAgIGNvbnN0IGFjdGl2YXRpb25zID0gdGhpcy5hY3RpdmF0aW9uQXJyYXlNYXA7XG5cbiAgICAgIHNlc3Npb25fdXRpbC5kaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbk91dHB1dHMoXG4gICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VUcmFuc2llbnRPcGVyYXRpb25BcnJheXMoXG4gICAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLCB0aGlzLmFjdGl2YXRpb25BcnJheU1hcCwgdGhpcy5ncmFkaWVudEFycmF5TWFwKTtcblxuICAgICAgc2Vzc2lvbl91dGlsLmFkZFBlcnNpc3RlbnRBcnJheXNUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcbiAgICAgIHNlc3Npb25fdXRpbC5sb2FkSW5wdXRzRnJvbUZlZWREaWN0aW9uYXJ5VG9UZW5zb3JBcnJheU1hcChcbiAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgcnVudGltZS5vcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZmVlZEZvcndhcmQodGhpcy5tYXRoLCBhY3RpdmF0aW9ucykpO1xuXG4gICAgICBjb25zdCByZXN1bHRzID0gdGVuc29ycy5tYXAoeCA9PiBhY3RpdmF0aW9ucy5nZXQoeCkpO1xuICAgICAgdGVuc29ycy5mb3JFYWNoKHggPT4gYWN0aXZhdGlvbnMuZGVsZXRlKHgpKTtcblxuICAgICAgc2Vzc2lvbl91dGlsLnJlbGVhc2VGZWVkRGljdGlvbmFyeUlucHV0c0Zyb21UZW5zb3JBcnJheU1hcChcbiAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgcmV0dXJuIHJlc3VsdHM7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGUgYSB0ZW5zb3IsIHVzaW5nIHRoZSBwcm92aWRlZCBmZWVkIGVudHJpZXMgdG8gcHJvdmlkZVxuICAgKiB1cHN0cmVhbSBOREFycmF5IGlucHV0LlxuICAgKlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3IgdG8gZXZhbHVhdGUuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBMaXN0IG9mIGBGZWVkRW50cnlgIHRvIHJlYWQgd2hlbiByZXBsYWNpbmcgZ3JhcGhcbiAgICogdGVuc29ycyB3aXRoIE5EQXJyYXlzLlxuICAgKiBAcmV0dXJuIFRoZSBjb21wdXRlZCB2YWx1ZSBvZiB0aGUgdGVuc29yLlxuICAgKi9cbiAgZXZhbCh0ZW5zb3I6IFRlbnNvciwgZmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdKTogTkRBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZXZhbEFsbChbdGVuc29yXSwgZmVlZEVudHJpZXMpWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWlucyBhIGJhdGNoLlxuICAgKiBSZXR1cm5zIGEgcmVkdWNlZCBjb3N0IGlmIHRoZSBjb3N0UmVkdWN0aW9uIHBhcmFtZXRlciBpcyBzZXQuXG4gICAqIFdoZW4gdXNpbmcgYSBgTkRBcnJheU1hdGhgIG9iamVjdCBpbiBzYWZlIG1vZGUgdGhpcyBtdXN0IGJlIHVzZWQgaW4gYVxuICAgKiBtYXRoLnNjb3BlKCkuXG4gICAqIEBwYXJhbSBjb3N0VGVuc29yIEEgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgY29zdCB0byBvcHRpbWl6ZS4gU2hvdWxkIGJlIGFcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0gZmVlZEVudHJpZXMgRmVlZCBlbnRyaWVzIGZvciB0aGlzIHRyYWluIHJ1bi4gUHJvdmlkZXMgaW5wdXRzLlxuICAgKiBAcGFyYW0gYmF0Y2hTaXplIEJhdGNoIHNpemUgZm9yIHRoaXMgdHJhaW4gbG9vcC5cbiAgICogQHBhcmFtIG9wdGltaXplciBBbiBvcHRpbWl6ZXIgdG8gcGVyZm9ybSB3ZWlnaHQgdXBkYXRlcy5cbiAgICogQHBhcmFtIGNvc3RSZWR1Y3Rpb24gQW4gb3B0aW9uIHRvIGFsbG93IHRoZSB1c2VyIHRvIGdldCBhIHN1bW1lZCwgYXZlcmFnZWQsXG4gICAqIG9yIG5vIGNvc3QgYmFjay5cbiAgICogQHJldHVybiBUaGUgcmVkdWNlZCBjb3N0LCBpZiBjb3N0IHJlZHVjdGlvbiBpcyBub3QgTk9ORS4gVGhlIHVzZXIgaXNcbiAgICogcmVzcG9uc2libGUgZm9yIGRpc3Bvc2luZyB0aGUgY29zdCBOREFycmF5IGJldHdlZW4gdHJhaW4gbG9vcHMuXG4gICAqL1xuICB0cmFpbihcbiAgICAgIGNvc3RUZW5zb3I6IFRlbnNvciwgZmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLCBiYXRjaFNpemU6IG51bWJlcixcbiAgICAgIG9wdGltaXplcjogT3B0aW1pemVyLCBjb3N0UmVkdWN0aW9uID0gQ29zdFJlZHVjdGlvbi5OT05FKTogU2NhbGFyIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc1NjYWxhclNoYXBlKGNvc3RUZW5zb3Iuc2hhcGUpLFxuICAgICAgICAnQ29zdCB0ZW5zb3IgZm9yIHRyYWluaW5nIG11c3QgYmUgYSBzY2FsYXIgdmFsdWUuJyk7XG5cbiAgICBpZiAodGhpcy5wcmV2QmF0Y2hTaXplICE9PSBiYXRjaFNpemUpIHtcbiAgICAgIHRoaXMucHJldkJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICAgIHRoaXMuYmF0Y2hTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhiYXRjaFNpemUpO1xuICAgIH1cblxuICAgIGNvbnN0IGZlZWQgPSBuZXcgRmVlZERpY3Rpb25hcnkoZmVlZEVudHJpZXMpO1xuICAgIHNlc3Npb25fdXRpbC50aHJvd0lmRmVlZERpY3Rpb25hcnlDb250YWluc05EQXJyYXlzKGZlZWQpO1xuXG4gICAgY29uc3QgcnVudGltZSA9IHRoaXMuZ2V0T3JDcmVhdGVSdW50aW1lKFtjb3N0VGVuc29yXSwgZmVlZCk7XG4gICAgY29uc3QgaW5mZXJlbmNlT3BlcmF0aW9ucyA9IHJ1bnRpbWUub3BlcmF0aW9ucztcbiAgICBjb25zdCBiYWNrUHJvcE9wZXJhdGlvbnMgPSBydW50aW1lLm9wZXJhdGlvbnMuc2xpY2UoKS5yZXZlcnNlKCk7XG4gICAgY29uc3QgYWN0aXZhdGlvbnMgPSB0aGlzLmFjdGl2YXRpb25BcnJheU1hcDtcbiAgICBjb25zdCBncmFkaWVudHMgPSB0aGlzLmdyYWRpZW50QXJyYXlNYXA7XG4gICAgZ3JhZGllbnRzLnNldChjb3N0VGVuc29yLCB0aGlzLm9uZVNjYWxhcik7XG5cbiAgICBzZXNzaW9uX3V0aWwuYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcblxuICAgIG9wdGltaXplci5iZWZvcmVCYXRjaChcbiAgICAgICAgdGhpcy5tYXRoLCBiYXRjaFNpemUsIHJ1bnRpbWUsIGFjdGl2YXRpb25zLCBncmFkaWVudHMpO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoa2VlcCwgdHJhY2spID0+IHtcbiAgICAgIGxldCBjb3N0ID0gdHJhY2soU2NhbGFyLm5ldygwKSk7XG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYmF0Y2hTaXplOyArK2kpIHtcbiAgICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGFjdGl2YXRpb25zKTtcbiAgICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uSW5wdXRHcmFkaWVudHMoXG4gICAgICAgICAgICBydW50aW1lLm5vZGVzLCBncmFkaWVudHMpO1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZVRyYW5zaWVudE9wZXJhdGlvbkFycmF5cyhcbiAgICAgICAgICAgIHJ1bnRpbWUub3BlcmF0aW9ucywgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICAgICAgc2Vzc2lvbl91dGlsLmxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgICAgZmVlZCwgYWN0aXZhdGlvbnMsIHRoaXMubWF0aCk7XG5cbiAgICAgICAgaW5mZXJlbmNlT3BlcmF0aW9ucy5mb3JFYWNoKFxuICAgICAgICAgICAgb3AgPT4gb3AuZmVlZEZvcndhcmQodGhpcy5tYXRoLCBhY3RpdmF0aW9ucykpO1xuICAgICAgICBiYWNrUHJvcE9wZXJhdGlvbnMuZm9yRWFjaChcbiAgICAgICAgICAgIG9wID0+IG9wLmJhY2tQcm9wKHRoaXMubWF0aCwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cykpO1xuXG4gICAgICAgIG9wdGltaXplci5hZnRlckV4YW1wbGUodGhpcy5tYXRoLCBydW50aW1lLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgICBzZXNzaW9uX3V0aWwucmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgICAgICAgICAgZmVlZCwgYWN0aXZhdGlvbnMsIHRoaXMubWF0aCk7XG5cbiAgICAgICAgY29zdCA9IHRoaXMudXBkYXRlQ29zdEZvckV4YW1wbGUoXG4gICAgICAgICAgICBjb3N0LCBhY3RpdmF0aW9ucy5nZXQoY29zdFRlbnNvciksIGNvc3RSZWR1Y3Rpb24pO1xuICAgICAgfVxuXG4gICAgICBvcHRpbWl6ZXIuYWZ0ZXJCYXRjaChcbiAgICAgICAgICB0aGlzLm1hdGgsIGJhdGNoU2l6ZSwgcnVudGltZSwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICAgIHJldHVybiB0aGlzLnVwZGF0ZUNvc3RGb3JCYXRjaChjb3N0LCBjb3N0UmVkdWN0aW9uKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlQ29zdEZvckV4YW1wbGUoXG4gICAgICB0b3RhbENvc3Q6IFNjYWxhciwgY3VyckNvc3Q6IFNjYWxhcixcbiAgICAgIGNvc3RSZWR1Y3Rpb246IENvc3RSZWR1Y3Rpb24pOiBTY2FsYXIge1xuICAgIGlmIChjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLk1FQU4gfHxcbiAgICAgICAgY29zdFJlZHVjdGlvbiA9PT0gQ29zdFJlZHVjdGlvbi5TVU0pIHtcbiAgICAgIHJldHVybiB0aGlzLm1hdGguYWRkKHRvdGFsQ29zdCwgY3VyckNvc3QpO1xuICAgIH1cbiAgICByZXR1cm4gdG90YWxDb3N0O1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVDb3N0Rm9yQmF0Y2godG90YWxDb3N0OiBTY2FsYXIsIGNvc3RSZWR1Y3Rpb246IENvc3RSZWR1Y3Rpb24pOlxuICAgICAgU2NhbGFyIHtcbiAgICBpZiAoY29zdFJlZHVjdGlvbiA9PT0gQ29zdFJlZHVjdGlvbi5NRUFOKSB7XG4gICAgICByZXR1cm4gdGhpcy5tYXRoLmRpdmlkZSh0b3RhbENvc3QsIHRoaXMuYmF0Y2hTaXplU2NhbGFyKTtcbiAgICB9XG4gICAgcmV0dXJuIHRvdGFsQ29zdDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0T3JDcmVhdGVSdW50aW1lKHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkOiBGZWVkRGljdGlvbmFyeSk6XG4gICAgICBTZXNzaW9uUnVudGltZSB7XG4gICAgY29uc3Qga2V5ID0gdGhpcy5tYWtlUnVudGltZUNhY2hlS2V5KHRlbnNvcnMsIGZlZWQpO1xuICAgIGxldCBydW50aW1lID0gdGhpcy5ydW50aW1lQ2FjaGVba2V5XTtcbiAgICBpZiAocnVudGltZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBsZXQgbm9kZXMgPVxuICAgICAgICAgIHNlc3Npb25fdXRpbC5nZXRPcmRlcmVkRXZhbHVhdGlvblNldEZyb21FdmFsVGVuc29yKHRlbnNvcnMsIGZlZWQpO1xuICAgICAgLy8gSW4gaW5mZXJlbmNlIG1vZGUgc3BsaXQgbm9kZXMgYXJlIG5vdCBuZWVkZWQsIGJ1dCB0aGVpciBjb3N0IGlzXG4gICAgICAvLyBuZWdsaWdpYmxlLCBhbmQgYWx3YXlzIGFkZGluZyB0aGVtIGluIGFsbG93cyBmb3IgY2FjaGluZyBvZiAxIHJ1bnRpbWVcbiAgICAgIC8vIGZvciBib3RoIHRyYWluL2V2YWwuXG4gICAgICBub2RlcyA9IHNlc3Npb25fdXRpbC5hZGRTcGxpdE5vZGVzKG5vZGVzKTtcbiAgICAgIHNlc3Npb25fdXRpbC5yZW1vdmVGZWVkRGljdGlvbmFyeU5vZGVzRnJvbUV2YWx1YXRpb25TZXQoZmVlZCwgbm9kZXMpO1xuICAgICAgc2Vzc2lvbl91dGlsLnRocm93RXJyb3JJZkV2YWx1YXRpb25TZXRDb250YWluc1BsYWNlaG9sZGVyTm9kZXMobm9kZXMpO1xuICAgICAgY29uc3Qgb3BlcmF0aW9ucyA9IG9wZXJhdGlvbl9lbWl0dGVyLmVtaXRGcm9tR3JhcGhOb2Rlcyhub2Rlcyk7XG4gICAgICBydW50aW1lID0ge25vZGVzLCBvcGVyYXRpb25zfTtcbiAgICAgIHRoaXMucnVudGltZUNhY2hlW2tleV0gPSBydW50aW1lO1xuICAgIH1cblxuICAgIHJldHVybiBydW50aW1lO1xuICB9XG5cbiAgcHJpdmF0ZSBtYWtlUnVudGltZUNhY2hlS2V5KHRlbnNvcnM6IFRlbnNvcltdLCBmZWVkOiBGZWVkRGljdGlvbmFyeSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRlbnNvcnMubWFwKHggPT4geC5pZCkuc29ydCgpLmpvaW4oJ18nKSArICdfXycgK1xuICAgICAgICBPYmplY3Qua2V5cyhmZWVkLmRpY3QpLnNvcnQoKS5qb2luKCdfJyk7XG4gIH1cblxuICAvKiogTWFwcyBlYWNoIG91dHB1dCB0ZW5zb3Igb2YgdGhlIGdyYXBoIHRvIGl0cyBhY3RpdmF0aW9uIHZhbHVlLiAqL1xuICBhY3RpdmF0aW9uQXJyYXlNYXAgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgLyoqIE1hcHMgZWFjaCB0ZW5zb3Igb2YgdGhlIGdyYXBoIHRvIGl0cyBkZXJpdmF0aXZlIHdydCB0aGUgY29zdCBmdW5jdGlvbi4gKi9cbiAgZ3JhZGllbnRBcnJheU1hcCA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICBwcml2YXRlIHJ1bnRpbWVDYWNoZToge1trZXk6IHN0cmluZ106IFNlc3Npb25SdW50aW1lfSA9IHt9O1xuICAvKiogQmF0Y2ggc2l6ZSBvZiB0aGUgcHJldmlvdXMgdHJhaW4oKSBjYWxsLiAqL1xuICBwcml2YXRlIHByZXZCYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBiYXRjaFNpemVTY2FsYXI6IFNjYWxhcjtcbiAgcHJpdmF0ZSBvbmVTY2FsYXIgPSBTY2FsYXIubmV3KDEpO1xufVxuXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IHR5cGUgU2Vzc2lvblJ1bnRpbWUgPSB7XG4gIG5vZGVzOiBOb2RlW107IG9wZXJhdGlvbnM6IE9wZXJhdGlvbltdO1xufTtcbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtDb25zdGFudE5vZGUsIE5vZGUsIFBsYWNlaG9sZGVyTm9kZSwgU3BsaXROb2RlLCBUZW5zb3IsIFZhcmlhYmxlTm9kZX0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge0lucHV0UHJvdmlkZXJ9IGZyb20gJy4vaW5wdXRfcHJvdmlkZXInO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcHMvb3AnO1xuaW1wb3J0IHtGZWVkRGljdGlvbmFyeX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBhcnJheSBvZiBncmFwaCBub2RlcyB0aGF0IHN0b3AgdHJhdmVyc2FsLCBiYXNlZCBvbiB0aGUgY29udGVudHNcbiAqIG9mIHRoZSBwcm92aWRlZCBGZWVkRGljdGlvbmFyeS4gVGhpcyBpcyBhIHNpbXBsZSAxOjEgZXh0cmFjdGlvbiBvZiBub2RlcyBmcm9tXG4gKiB0aGUgRmVlZERpY3Rpb25hcnkuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGZlZWREaWN0aW9uYXJ5IFRoZSBGZWVkRGljdGlvbmFyeSB0byBzY2FuIGZvciB0ZXJtaW5hdGlvbiBub2Rlcy5cbiAqIEByZXR1cm4gYW4gYXJyYXkgb2YgTm9kZXMgd2hpY2ggaGFsdCB0cmF2ZXJzYWwgd2hlbiB2aXNpdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGVybWluYXRpbmdOb2Rlc0Zyb21GZWVkRGljdGlvbmFyeShcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpOiBOb2RlW10ge1xuICByZXR1cm4gT2JqZWN0LmtleXMoZmVlZERpY3Rpb25hcnkuZGljdClcbiAgICAgIC5tYXAodGVuc29ySUQgPT4gZmVlZERpY3Rpb25hcnkuZGljdFsrdGVuc29ySURdLnRlbnNvci5ub2RlKTtcbn1cblxuLyoqXG4gKiBHaXZlbiBhIHRlbnNvciBhbmQgYSBmZWVkIGRpY3Rpb25hcnksIGNvbXB1dGVzIHRoZSBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvXG4gKiBiZSBldmFsdWF0ZWQgdG8gcGVyZm9ybSBpbmZlcmVuY2UuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWxUZW5zb3JzIFRoZSBsaXN0IG9mIHRlbnNvcnMgdG8gZXZlbnR1YWxseSBiZSBldmFsdWF0ZWQuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIHBvcHVsYXRlZCBmZWVkIGRpY3Rpb25hcnkuXG4gKiBAcmV0dXJuIFRoZSBzZXQgb2Ygbm9kZXMgdG8gZXZhbHVhdGUsIGluIGV2YWx1YXRpb24gb3JkZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRlcmVkRXZhbHVhdGlvblNldEZyb21FdmFsVGVuc29yKFxuICAgIGV2YWxUZW5zb3JzOiBUZW5zb3JbXSwgZmVlZERpY3Rpb25hcnk6IEZlZWREaWN0aW9uYXJ5KTogTm9kZVtdIHtcbiAgY29uc3QgdGVybWluYXRpbmdOb2RlcyA9XG4gICAgICBnZXRUZXJtaW5hdGluZ05vZGVzRnJvbUZlZWREaWN0aW9uYXJ5KGZlZWREaWN0aW9uYXJ5KTtcbiAgY29uc3QgZXZhbE5vZGVzID0gZXZhbFRlbnNvcnMubWFwKHggPT4geC5ub2RlKTtcbiAgY29uc3QgdW5vcmRlcmVkRXZhbHVhdGlvblNldCA9XG4gICAgICBncmFwaF91dGlsLmdldFVub3JkZXJlZEV2YWx1YXRpb25TZXQoZXZhbE5vZGVzLCB0ZXJtaW5hdGluZ05vZGVzKTtcbiAgY29uc3Qgb3JkZXJlZEV2YWx1YXRpb25TZXQgPVxuICAgICAgZ3JhcGhfdXRpbC5nZXRPcmRlcmVkRXZhbHVhdGlvblNldCh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0KTtcbiAgcmV0dXJuIG9yZGVyZWRFdmFsdWF0aW9uU2V0O1xufVxuXG4vKipcbiAqIFRyYXZlcnNlcyB0aGUgcHJvdmlkZWQgbm9kZSBhcnJheSBhbmQgYWRkcyBhbGwgcGVyc2lzdGVudCBub2RlIE5EQXJyYXlzIHRvXG4gKiB0aGUgcHJvdmlkZWQgVGVuc29yQXJyYXlNYXAuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWx1YXRpb25TZXQgVGhlIGFycmF5IG9mIG5vZGVzIHRvIHNjYW4uXG4gKiBAcGFyYW0gdGVuc29yQXJyYXlNYXAgVGhlIG1hcCB0aGF0IHJlY2VpdmVzIHRoZSBOREFycmF5cyBmcm9tIHBlcnNpc3RlbnRcbiAqIG5vZGVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdLCB0ZW5zb3JBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgVmFyaWFibGVOb2RlIHx8IG5vZGUgaW5zdGFuY2VvZiBDb25zdGFudE5vZGUpIHtcbiAgICAgIHRlbnNvckFycmF5TWFwLnNldChub2RlLm91dHB1dCwgbm9kZS5kYXRhKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFZhcmlhYmxlTm9kZXNGcm9tRXZhbHVhdGlvblNldChldmFsdWF0aW9uU2V0OiBOb2RlW10pOlxuICAgIFZhcmlhYmxlTm9kZVtdIHtcbiAgY29uc3Qgbm9kZXM6IFZhcmlhYmxlTm9kZVtdID0gW107XG4gIGV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHtcbiAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFZhcmlhYmxlTm9kZSkge1xuICAgICAgbm9kZXMucHVzaChub2RlKTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gbm9kZXM7XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gdGhyb3dJZkZlZWREaWN0aW9uYXJ5Q29udGFpbnNOREFycmF5cyhcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpIHtcbiAgT2JqZWN0LmtleXMoZmVlZERpY3Rpb25hcnkuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgaWYgKGZlZWREaWN0aW9uYXJ5LmRpY3RbK3RlbnNvcklEXS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICd0cmFpbmluZyByZXF1aXJlcyBGZWVkRGljdGlvbmFyeSBlbnRyaWVzIHRvIGJlIElucHV0UHJvdmlkZXJzJyArXG4gICAgICAgICAgJ2FuZCBub3QgTkRBcnJheXMuJyk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkSW5wdXRzRnJvbUZlZWREaWN0aW9uYXJ5VG9UZW5zb3JBcnJheU1hcChcbiAgICBiYXRjaEZlZWQ6IEZlZWREaWN0aW9uYXJ5LCBhY3RpdmF0aW9uczogVGVuc29yQXJyYXlNYXAsIG1hdGg6IE5EQXJyYXlNYXRoKSB7XG4gIE9iamVjdC5rZXlzKGJhdGNoRmVlZC5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICBjb25zdCBmZWVkRW50cnkgPSBiYXRjaEZlZWQuZGljdFsrdGVuc29ySURdO1xuXG4gICAgbGV0IGRhdGE6IE5EQXJyYXk7XG4gICAgaWYgKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkge1xuICAgICAgZGF0YSA9IGZlZWRFbnRyeS5kYXRhIGFzIE5EQXJyYXk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHByb3ZpZGVyID0gZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcjtcbiAgICAgIGRhdGEgPSBwcm92aWRlci5nZXROZXh0Q29weShtYXRoKTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbChmZWVkRW50cnkudGVuc29yLnNoYXBlLCBkYXRhLnNoYXBlKSxcbiAgICAgICAgYEVycm9yIGxvYWRpbmcgRmVlZEVudHJ5OiBmZWVkaW5nIE5EQXJyYXkgb2Ygc2hhcGUgJHtkYXRhLnNoYXBlfSBgICtcbiAgICAgICAgICAgIGBkb2VzIG5vdCBtYXRjaCBUZW5zb3IgKGlkOiAke2ZlZWRFbnRyeS50ZW5zb3IuaWR9KSBzaGFwZTogYCArXG4gICAgICAgICAgICBgJHtmZWVkRW50cnkudGVuc29yLnNoYXBlfS5gKTtcbiAgICBhY3RpdmF0aW9ucy5zZXQoZmVlZEVudHJ5LnRlbnNvciwgZGF0YSk7XG4gIH0pO1xufVxuXG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgIGJhdGNoRmVlZDogRmVlZERpY3Rpb25hcnksIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCwgbWF0aDogTkRBcnJheU1hdGgpIHtcbiAgT2JqZWN0LmtleXMoYmF0Y2hGZWVkLmRpY3QpLmZvckVhY2godGVuc29ySUQgPT4ge1xuICAgIGNvbnN0IGZlZWRFbnRyeSA9IGJhdGNoRmVlZC5kaWN0Wyt0ZW5zb3JJRF07XG5cbiAgICBpZiAoIShmZWVkRW50cnkuZGF0YSBpbnN0YW5jZW9mIE5EQXJyYXkpKSB7XG4gICAgICBjb25zdCBwcm92aWRlciA9IGZlZWRFbnRyeS5kYXRhIGFzIElucHV0UHJvdmlkZXI7XG5cbiAgICAgIGNvbnN0IGZlZWRFbnRyeUFycmF5ID0gYWN0aXZhdGlvbnMuZ2V0KGZlZWRFbnRyeS50ZW5zb3IpO1xuICAgICAgcHJvdmlkZXIuZGlzcG9zZUNvcHkobWF0aCwgZmVlZEVudHJ5QXJyYXkpO1xuICAgIH1cblxuICAgIGFjdGl2YXRpb25zLmRlbGV0ZShmZWVkRW50cnkudGVuc29yKTtcbiAgfSk7XG59XG5cbi8qKlxuICogUmVtb3ZlcyBhbGwgbm9kZXMgZnJvbSB0aGUgcHJvdmlkZWQgTm9kZSBhcnJheSB3aG9zZSBvdXRwdXQgdGVuc29ycyBleGlzdCBpblxuICogdGhlIHByb3ZpZGVkIGZlZWQgZGljdGlvbmFyeS4gQWZ0ZXIgY2FsbGluZyB0aGlzLCB0aGUgTm9kZSBhcnJheSBzaG91bGRcbiAqIGNvbnRhaW4gemVybyBQbGFjZWhvbGRlciBub2Rlcywgb3IgdGhlIHVzZXIgaGFzIGZhaWxlZCB0byBwcm92aWRlIGEgZmVlZCBmb3JcbiAqIGEgUGxhY2Vob2xkZXIgbm9kZS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIEZlZWREaWN0aW9uYXJ5IHRvIHByb2Nlc3MuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gcmVtb3ZlIGlucHV0IG5vZGVzIGZyb20uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVGZWVkRGljdGlvbmFyeU5vZGVzRnJvbUV2YWx1YXRpb25TZXQoXG4gICAgZmVlZERpY3Rpb25hcnk6IEZlZWREaWN0aW9uYXJ5LCBldmFsdWF0aW9uU2V0OiBOb2RlW10pIHtcbiAgbGV0IGkgPSAwO1xuICB3aGlsZSAoaSA8IGV2YWx1YXRpb25TZXQubGVuZ3RoKSB7XG4gICAgY29uc3Qgbm9kZSA9IGV2YWx1YXRpb25TZXRbaV07XG4gICAgaWYgKGZlZWREaWN0aW9uYXJ5LmRpY3Rbbm9kZS5vdXRwdXQuaWRdICE9IG51bGwpIHtcbiAgICAgIGV2YWx1YXRpb25TZXQuc3BsaWNlKGksIDEpO1xuICAgIH0gZWxzZSB7XG4gICAgICArK2k7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogRGlzcG9zZXMgYW55IE5EQXJyYXlzIG9uIHRoZSB0ZW5zb3JBcnJheU1hcCBmcm9tIG9wZXJhdGlvbiBvdXRwdXRzIGFuZCBzZXRzXG4gKiB0aGUgdmFsdWUgdG8gbnVsbC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgc2V0IG9mIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSB0ZW5zb3JBcnJheU1hcCBUaGUgbWFwIHRvIGRpc3Bvc2UgYW5kIGluaXRpYWxpemUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbk91dHB1dHMoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdLCB0ZW5zb3JBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmICghZ3JhcGhfdXRpbC5pc0lucHV0Tm9kZShub2RlKSkge1xuICAgICAgaWYgKCFncmFwaF91dGlsLmlzUGFzc3Rocm91Z2hOb2RlKG5vZGUsIHRlbnNvckFycmF5TWFwKSkge1xuICAgICAgICB0ZW5zb3JBcnJheU1hcC5kaXNwb3NlQXJyYXkobm9kZS5vdXRwdXQpO1xuICAgICAgfVxuICAgICAgdGVuc29yQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBudWxsKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIERpc3Bvc2VzIGFueSBOREFycmF5cyBvbiB0aGUgdGVuc29yQXJyYXlNYXAgZnJvbSBkZXJpdmF0aXZlcyBvZiBvcGVyYXRpb25cbiAqIGlucHV0cyBhbmQgc2V0cyB0aGUgdmFsdWUgdG8gbnVsbC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgc2V0IG9mIG5vZGVzIHRvIGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBncmFkaWVudHMgVGhlIGdyYWRpZW50IG1hcCB0byBkaXNwb3NlIGFuZCBpbml0aWFsaXplLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25JbnB1dEdyYWRpZW50cyhcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIGdyYWRpZW50czogVGVuc29yQXJyYXlNYXApIHtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKS5mb3JFYWNoKGlucHV0TmFtZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dCA9IG5vZGUuaW5wdXRzW2lucHV0TmFtZV07XG4gICAgICBpZiAoZ3JhZGllbnRzLmdldChpbnB1dCwgdHJ1ZSkgIT09IGdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQsIHRydWUpKSB7XG4gICAgICAgIGdyYWRpZW50cy5kaXNwb3NlQXJyYXkoaW5wdXQpO1xuICAgICAgfVxuICAgICAgZ3JhZGllbnRzLnNldChpbnB1dCwgbnVsbCk7XG4gICAgfSk7XG4gIH0pO1xufVxuXG5cbi8qKlxuICogQ2FsbHMgdW5kZXJseWluZyBvcGVyYXRpb24gZGlzcG9zZVRyYW5zaWVudEFycmF5cyBtZXRob2RzIHdoaWNoIGNsZWFuIHVwIGFueVxuICogTkRBcnJheXMgd2hpY2ggb3BlcmF0aW9ucyBtYXkgaGF2ZSBjcmVhdGVkIGR1cmluZyBhIHJ1bi5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gb3BlcmF0aW9uTm9kZXMgVGhlIGFycmF5IG9mIE5vZGVzIHRvIHRyYXZlcnNlLlxuICogQHBhcmFtIG91dHB1dFRlbnNvciBUaGUgdGVuc29yIGJlaW5nIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBtYXAgVGhlIFRlbnNvckFycmF5TWFwIHRvIG9wZXJhdGUgb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlVHJhbnNpZW50T3BlcmF0aW9uQXJyYXlzKFxuICAgIG9wZXJhdGlvbnM6IE9wZXJhdGlvbltdLCBhY3RpdmF0aW9uczogVGVuc29yQXJyYXlNYXAsXG4gICAgZ3JhZGllbnRzOiBUZW5zb3JBcnJheU1hcCkge1xuICBvcGVyYXRpb25zLmZvckVhY2gob3AgPT4gb3AuZGlzcG9zZVRyYW5zaWVudEFycmF5cyhhY3RpdmF0aW9ucywgZ3JhZGllbnRzKSk7XG59XG5cbi8qKlxuICogSXRlcmF0ZXMgdGhlIHByb3ZpZGVkIE5vZGUgYXJyYXkgYW5kIHRocm93cyBhbiBleGNlcHRpb24gaWYgdGhlcmUgYXJlIGFueVxuICogUGxhY2Vob2xkZXIgbm9kZXMgcHJlc2VudC4gQ2FsbCBhZnRlciB0aGUgZXZhbHVhdGlvbiBzZXQgaGFzIGJlZW4gcHJ1bmVkIHdpdGhcbiAqIHRoZSBhY2NvbXBhbnlpbmcgRmVlZERpY3Rpb25hcnkgdG8gZW5zdXJlIHRoYXQgYWxsIGlucHV0cyBoYXZlIGJlZW4gcmVzb2x2ZWQuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIGV2YWx1YXRpb25TZXQgVGhlIGFycmF5IG9mIG5vZGVzIHRvIHNjYW4uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0aHJvd0Vycm9ySWZFdmFsdWF0aW9uU2V0Q29udGFpbnNQbGFjZWhvbGRlck5vZGVzKFxuICAgIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKG5vZGUgaW5zdGFuY2VvZiBQbGFjZWhvbGRlck5vZGUpIHtcbiAgICAgIGNvbnN0IHNoYXBlID0gJ1snICsgbm9kZS5vdXRwdXQuc2hhcGUuam9pbignLCAnKSArICddJztcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnUGxhY2Vob2xkZXIgbm9kZSBcIicgKyBub2RlLm5hbWUgKyAnXCIgJyArIHNoYXBlICtcbiAgICAgICAgICAnIG5vdCBwcmVzZW50IGluIGZlZWQgZGljdGlvbmFyeS4nKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEluamVjdHMgc3BsaXRzIG5vZGVzIGFmdGVyIGV2ZXJ5IG5vZGUgdGhhdCBoYXMgbXVsdGlwbGUgY29uc3VtZXJzLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBub2RlcyBUaGUgbm9kZSBsaXN0IGluIGV2YWx1YXRpb24gb3JkZXIuXG4gKiBAcmV0dXJuIFRoZSBub2RlIGxpc3Qgd2l0aCBzcGxpdCBub2RlcyBpbmplY3RlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFkZFNwbGl0Tm9kZXMobm9kZXM6IE5vZGVbXSk6IE5vZGVbXSB7XG4gIGNvbnN0IG5vZGVJZFRvTnVtQ29uc3VtZXJzOiBudW1iZXJbXSA9IFtdO1xuICBjb25zdCBub2RlSWRUb1NwbGl0Tm9kZToge1tub2RlSWQ6IG51bWJlcl06IFNwbGl0Tm9kZX0gPSB7fTtcblxuICAvLyBGaW5kIG5vZGVzIHRoYXQgaGF2ZSBtdWx0aXBsZSBjb25zdW1lcnMuXG4gIG5vZGVzLmZvckVhY2gobm9kZSA9PiB7XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKTtcbiAgICBrZXlzLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VGVuc29yID0gbm9kZS5pbnB1dHNba2V5XTtcbiAgICAgIGNvbnN0IGlucHV0ID0gaW5wdXRUZW5zb3Iubm9kZTtcbiAgICAgIGlmIChub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPT0gbnVsbCkge1xuICAgICAgICBub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPSAwO1xuICAgICAgfVxuICAgICAgbm9kZUlkVG9OdW1Db25zdW1lcnNbaW5wdXQuaWRdKys7XG4gICAgICBpZiAobm9kZUlkVG9OdW1Db25zdW1lcnNbaW5wdXQuaWRdID4gMSAmJlxuICAgICAgICAgIG5vZGVJZFRvU3BsaXROb2RlW2lucHV0LmlkXSA9PSBudWxsKSB7XG4gICAgICAgIG5vZGVJZFRvU3BsaXROb2RlW2lucHV0LmlkXSA9IG5ldyBTcGxpdE5vZGUoaW5wdXQuZ3JhcGgsIGlucHV0VGVuc29yKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG5cbiAgLy8gSW5qZWN0IGEgc3BsaXQgbm9kZSBhZnRlciBlYWNoIG5vZGUgdGhhdCBoYXMgbXVsdGlwbGUgY29uc3VtZXJzIGFuZFxuICAvLyByZXdpcmUgdGhlIGlucHV0cyBvZiB0aGUgY29uc3VtZXJzIHRvIGNvbnN1bWUgdGhlIG91dHB1dCB0ZW5zb3JzIG9mIHRoZVxuICAvLyBzcGxpdCBub2RlIGluc3RlYWQgb2YgdGhlIG9yaWdpbmFsIG5vZGUuIEVhY2ggY29uc3VtZXIgY29uc3VtZXMgYVxuICAvLyBkaWZmZXJlbnQgb3V0cHV0IHRlbnNvciBzbyB0aGF0IGRlcml2YXRpdmVzIGFyZSBub3Qgb3ZlcndyaXR0ZW4uXG4gIC8vIHgtLT55ICBiZWNvbWVzIHgtLT5zLS0+eSAgIHdoZXJlIHkgY29uc3VtZXMgdGhlIDFzdCBvdXRwdXQgdGVuc29yIG9mIHNcbiAgLy8gfC0tPnogICAgICAgICAgICAgIHwtLT56ICAgICBhbmQgeiBjb25zdW1lcyB0aGUgMm5kIG91dHB1dCB0ZW5zb3Igb2Ygc1xuICBjb25zdCBuZXdOb2RlczogTm9kZVtdID0gW107XG4gIG5vZGVzLmZvckVhY2gobm9kZSA9PiB7XG4gICAgbmV3Tm9kZXMucHVzaChub2RlKTtcbiAgICBpZiAobm9kZS5pZCBpbiBub2RlSWRUb1NwbGl0Tm9kZSkge1xuICAgICAgY29uc3Qgc3BsaXROb2RlID0gbm9kZUlkVG9TcGxpdE5vZGVbbm9kZS5pZF07XG4gICAgICBuZXdOb2Rlcy5wdXNoKHNwbGl0Tm9kZSk7XG4gICAgfVxuICAgIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhub2RlLmlucHV0cyk7XG4gICAga2V5cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFRlbnNvciA9IG5vZGUuaW5wdXRzW2tleV07XG4gICAgICBjb25zdCBpbnB1dElkID0gaW5wdXRUZW5zb3Iubm9kZS5pZDtcbiAgICAgIGlmIChpbnB1dElkIGluIG5vZGVJZFRvU3BsaXROb2RlKSB7XG4gICAgICAgIG5vZGUuaW5wdXRzW2tleV0gPSBub2RlSWRUb1NwbGl0Tm9kZVtpbnB1dElkXS5nZXROZXdPdXRwdXRUZW5zb3IoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG4gIHJldHVybiBuZXdOb2Rlcztcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtOb2RlLCBUZW5zb3IsIFZhcmlhYmxlTm9kZX0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCB7U2Vzc2lvblJ1bnRpbWV9IGZyb20gJy4vc2Vzc2lvbic7XG5pbXBvcnQgKiBhcyBzZXNzaW9uX3V0aWwgZnJvbSAnLi9zZXNzaW9uX3V0aWwnO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuZXhwb3J0IGNsYXNzIFNHRE9wdGltaXplciBleHRlbmRzIE9wdGltaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbGVhcm5pbmdSYXRlOiBudW1iZXIsIHNwZWNpZmllZFZhcmlhYmxlTGlzdD86IE5vZGVbXSkge1xuICAgIHN1cGVyKHNwZWNpZmllZFZhcmlhYmxlTGlzdCk7XG4gIH1cblxuICBiZWZvcmVCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRoaXMudmFyaWFibGVOb2RlcyA9IHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcyA9PSBudWxsID9cbiAgICAgICAgc2Vzc2lvbl91dGlsLmdldFZhcmlhYmxlTm9kZXNGcm9tRXZhbHVhdGlvblNldChydW50aW1lLm5vZGVzKSA6XG4gICAgICAgIHRoaXMuc3BlY2lmaWVkVmFyaWFibGVOb2RlcztcbiAgICBpZiAoYmF0Y2hTaXplICE9PSB0aGlzLnByZXZCYXRjaFNpemUpIHtcbiAgICAgIHRoaXMucHJldkJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICAgIHRoaXMuYyA9IFNjYWxhci5uZXcoLXRoaXMubGVhcm5pbmdSYXRlIC8gYmF0Y2hTaXplKTtcbiAgICB9XG4gICAgdGhpcy52YXJpYWJsZU5vZGVzLmZvckVhY2goXG4gICAgICAgIG5vZGUgPT4gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5zZXQoXG4gICAgICAgICAgICBub2RlLm91dHB1dCwgTkRBcnJheS56ZXJvcyhub2RlLm91dHB1dC5zaGFwZSkpKTtcbiAgfVxuXG4gIGFmdGVyRXhhbXBsZShcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgdGhpcy52YXJpYWJsZU5vZGVzIS5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICBjb25zdCBncmFkaWVudCA9IGdyYWRpZW50QXJyYXlNYXAuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgY29uc3QgYWNjdW11bGF0ZWRHcmFkaWVudCA9IHRoaXMudmFyaWFibGVHcmFkaWVudHMuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cy5zZXQoXG4gICAgICAgICAgICBub2RlLm91dHB1dCwga2VlcChtYXRoLmFkZChncmFkaWVudCwgYWNjdW11bGF0ZWRHcmFkaWVudCkpKTtcbiAgICAgICAgYWNjdW11bGF0ZWRHcmFkaWVudC5kaXNwb3NlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFmdGVyQmF0Y2goXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgYmF0Y2hTaXplOiBudW1iZXIsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICB0aGlzLnZhcmlhYmxlTm9kZXMhLmZvckVhY2gobm9kZSA9PiB7XG4gICAgICAgIGNvbnN0IG9sZFZhcmlhYmxlID0gYWN0aXZhdGlvbkFycmF5TWFwLmdldChub2RlLm91dHB1dCk7XG4gICAgICAgIGNvbnN0IGdyYWRpZW50ID0gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICBjb25zdCB2YXJpYWJsZSA9XG4gICAgICAgICAgICBtYXRoLnNjYWxlZEFycmF5QWRkKHRoaXMuYyEsIGdyYWRpZW50LCB0aGlzLm9uZSEsIG9sZFZhcmlhYmxlKTtcbiAgICAgICAgYWN0aXZhdGlvbkFycmF5TWFwLnNldChub2RlLm91dHB1dCwga2VlcCh2YXJpYWJsZSkpO1xuICAgICAgICBub2RlLmRhdGEgPSB2YXJpYWJsZTtcblxuICAgICAgICBvbGRWYXJpYWJsZS5kaXNwb3NlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRoaXMudmFyaWFibGVHcmFkaWVudHMuZGlzcG9zZSgpO1xuICAgIHRoaXMudmFyaWFibGVHcmFkaWVudHMgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuYyAhPSBudWxsKSB7XG4gICAgICB0aGlzLmMuZGlzcG9zZSgpO1xuICAgIH1cbiAgICB0aGlzLm9uZS5kaXNwb3NlKCk7XG4gIH1cblxuICBzZXRMZWFybmluZ1JhdGUobGVhcm5pbmdSYXRlOiBudW1iZXIpIHtcbiAgICB0aGlzLmxlYXJuaW5nUmF0ZSA9IGxlYXJuaW5nUmF0ZTtcbiAgfVxuXG4gIHByaXZhdGUgdmFyaWFibGVHcmFkaWVudHMgPSBuZXcgVGVuc29yQXJyYXlNYXAoKTtcbiAgcHJpdmF0ZSBwcmV2QmF0Y2hTaXplOiBudW1iZXI7XG4gIHByaXZhdGUgb25lID0gU2NhbGFyLm5ldygxKTtcbiAgcHJpdmF0ZSBjOiBTY2FsYXI7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIFRlbnNvckFycmF5TWFwIGlzIGFuIGludGVybmFsIG1hcCBmcm9tIFRlbnNvciBJRHMgdG8gTkRBcnJheXMuIFNpbmNlIE5EQXJyYXlzXG4gKiBjYW4gYmUgYmFja2VkIGJ5IFdlYkdMIHRleHR1cmVzLCB0aGUgVGVuc29yQXJyYXlNYXAgaXMgb25seSB1c2VkIGluc2lkZSBvZiBhXG4gKiBTZXNzaW9uLlxuICovXG5leHBvcnQgY2xhc3MgVGVuc29yQXJyYXlNYXAge1xuICAvKipcbiAgICogQWRkIG9yIHJlcGxhY2UgYW4gZW50cnkgaW4gdGhlIG1hcC5cbiAgICogQHBhcmFtIHRlbnNvciBUaGUgdGVuc29yIGtleS5cbiAgICogQHBhcmFtIGFycmF5IFRoZSBOREFycmF5IHZhbHVlLCBjYW4gYmUgbnVsbC5cbiAgICovXG4gIHNldCh0ZW5zb3I6IFRlbnNvciwgYXJyYXk6IE5EQXJyYXl8bnVsbCkge1xuICAgIHRoaXMuZGljdFt0ZW5zb3IuaWRdID0gYXJyYXk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgTkRBcnJheSBhc3NvY2lhdGVkIHdpdGggdGhlIHByb3ZpZGVkIHRlbnNvci4gV2lsbCB0aHJvdyBhblxuICAgKiBleGNlcHRpb24gaWYgdGhlIHRlbnNvciBpcyBub3QgYSBrZXkgaW4gdGhlIG1hcCwgb3IgaWYgdGhlIGFzc29jaWF0ZWRcbiAgICogTkRBcnJheSBpcyBudWxsLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcGFyYW0gc2tpcENoZWNrcyBGYWxzZSBieSBkZWZhdWx0LiBJZiB0cnVlIHdpbGwgc2tpcCBhbGwgY2hlY2tzLlxuICAgKiBAcmV0dXJuIFRoZSBOREFycmF5IGFzc29jaWF0ZWQgd2l0aCB0aGUgdGVuc29yLlxuICAgKi9cbiAgZ2V0KHRlbnNvcjogVGVuc29yLCBza2lwQ2hlY2tzID0gZmFsc2UpOiBOREFycmF5IHtcbiAgICBpZiAoIXNraXBDaGVja3MgJiYgdGhpcy5kaWN0W3RlbnNvci5pZF0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgbm90IGluIGFycmF5IG1hcC4nKTtcbiAgICB9XG4gICAgY29uc3QgbmRhID0gdGhpcy5kaWN0W3RlbnNvci5pZF07XG4gICAgaWYgKCFza2lwQ2hlY2tzICYmIG5kYSA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgaGFzIG51bGwgYXJyYXkuJyk7XG4gICAgfVxuICAgIHJldHVybiBuZGEhO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSB0ZW5zb3IvTkRBcnJheSBwYWlyIGZyb20gdGhlIG1hcC5cbiAgICogQHBhcmFtIHRlbnNvciBUaGUgdGVuc29yIGtleS5cbiAgICovXG4gIGRlbGV0ZSh0ZW5zb3I6IFRlbnNvcikge1xuICAgIGRlbGV0ZSB0aGlzLmRpY3RbdGVuc29yLmlkXTtcbiAgfVxuXG4gIGRpc3Bvc2VBcnJheSh0ZW5zb3I6IFRlbnNvcikge1xuICAgIGlmICh0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG5kYSA9IHRoaXMuZGljdFt0ZW5zb3IuaWRdO1xuICAgIGlmIChuZGEgPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbmRhLmRpc3Bvc2UoKTtcbiAgICB0aGlzLmRpY3RbdGVuc29yLmlkXSA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybiBUaGUgbnVtYmVyIG9mIHRlbnNvci9OREFycmF5IHBhaXJzIGluIHRoZSBtYXAuXG4gICAqL1xuICBzaXplKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHRoaXMuZGljdCkubGVuZ3RoO1xuICB9XG5cbiAgLyoqXG4gICAqIEl0ZXJhdGUgb3ZlciBhbGwgY29udGFpbmVkIE5EQXJyYXkgdmFsdWVzIGFuZCBkaXNwb3NlIHRoZW0uXG4gICAqL1xuICBkaXNwb3NlKCkge1xuICAgIE9iamVjdC5rZXlzKHRoaXMuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgICBjb25zdCBuZGEgPSB0aGlzLmRpY3RbK3RlbnNvcklEXTtcbiAgICAgIGlmIChuZGEpIHtcbiAgICAgICAgbmRhLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLmRpY3QgPSB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUZXN0cyB0byBzZWUgaWYgYSB0ZW5zb3IgaGFzIGEgbnVsbCBhc3NvY2lhdGVkIHdpdGggaXQuIFRocm93c1xuICAgKiBpZiB0aGUgdGVuc29yIGlzIG5vdCBhIGtleSBpbiB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcmV0dXJuIFRydWUgaWYgdGhlIGFzc29jaWF0ZWQgTkRBcnJheSBpcyBudWxsLCBlbHNlIEZhbHNlLlxuICAgKi9cbiAgaGFzTnVsbEFycmF5KHRlbnNvcjogVGVuc29yKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndGVuc29yICcgKyB0ZW5zb3IuaWQgKyAnIG5vdCBpbiBhcnJheSBtYXAuJyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgZGljdDoge1t0ZW5zb3JJRDogbnVtYmVyXTogTkRBcnJheSB8IG51bGx9ID0ge307XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCB0eXBlIFZlY3RvciA9IG51bWJlcltdIHwgRmxvYXQ2NEFycmF5IHwgRmxvYXQzMkFycmF5IHwgSW50MzJBcnJheSB8XG4gICAgSW50OEFycmF5IHwgSW50MTZBcnJheTtcblxuLyoqIFNodWZmbGVzIHRoZSBhcnJheSB1c2luZyBGaXNoZXItWWF0ZXMgYWxnb3JpdGhtLiAqL1xuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuZXhwb3J0IGZ1bmN0aW9uIHNodWZmbGUoYXJyYXk6IGFueVtdfFVpbnQzMkFycmF5fEludDMyQXJyYXl8XG4gICAgICAgICAgICAgICAgICAgICAgICBGbG9hdDMyQXJyYXkpOiB2b2lkIHtcbiAgbGV0IGNvdW50ZXIgPSBhcnJheS5sZW5ndGg7XG4gIGxldCB0ZW1wID0gMDtcbiAgbGV0IGluZGV4ID0gMDtcbiAgLy8gV2hpbGUgdGhlcmUgYXJlIGVsZW1lbnRzIGluIHRoZSBhcnJheVxuICB3aGlsZSAoY291bnRlciA+IDApIHtcbiAgICAvLyBQaWNrIGEgcmFuZG9tIGluZGV4XG4gICAgaW5kZXggPSAoTWF0aC5yYW5kb20oKSAqIGNvdW50ZXIpIHwgMDtcbiAgICAvLyBEZWNyZWFzZSBjb3VudGVyIGJ5IDFcbiAgICBjb3VudGVyLS07XG4gICAgLy8gQW5kIHN3YXAgdGhlIGxhc3QgZWxlbWVudCB3aXRoIGl0XG4gICAgdGVtcCA9IGFycmF5W2NvdW50ZXJdO1xuICAgIGFycmF5W2NvdW50ZXJdID0gYXJyYXlbaW5kZXhdO1xuICAgIGFycmF5W2luZGV4XSA9IHRlbXA7XG4gIH1cbn1cblxuLyoqIENsYW1wcyBhIHZhbHVlIHRvIGEgc3BlY2lmaWVkIHJhbmdlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCB4OiBudW1iZXIsIG1heDogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE1hdGgubWF4KG1pbiwgTWF0aC5taW4oeCwgbWF4KSk7XG59XG5cbi8qKiBSZXR1cm5zIGEgc2FtcGxlIGZyb20gYSB1bmlmb3JtIFthLCBiXSBkaXN0cmlidXRpb24uICovXG5leHBvcnQgZnVuY3Rpb24gcmFuZFVuaWZvcm0oYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgcmV0dXJuIE1hdGgucmFuZG9tKCkgKiAoYiAtIGEpICsgYTtcbn1cblxuLyoqXG4gKiBTYW1wbGVzIGZyb20gYSBnYXVzc2lhbiBkaXN0cmlidXRpb24uXG4gKlxuICogQHBhcmFtIG1lYW4gVGhlIG1lYW4uIERlZmF1bHQgaXMgMC5cbiAqIEBwYXJhbSBzdGREZXYgVGhlIHN0YW5kYXJkIGRldmlhdGlvbi4gRGVmYXVsdCBpcyAxLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmFuZEdhdXNzKG1lYW4gPSAwLCBzdGREZXYgPSAxLCB0cnVuY2F0ZWQgPSBmYWxzZSk6IG51bWJlciB7XG4gIGxldCB2MTogbnVtYmVyLCB2MjogbnVtYmVyLCBzOiBudW1iZXI7XG4gIGRvIHtcbiAgICB2MSA9IDIgKiBNYXRoLnJhbmRvbSgpIC0gMTtcbiAgICB2MiA9IDIgKiBNYXRoLnJhbmRvbSgpIC0gMTtcbiAgICBzID0gdjEgKiB2MSArIHYyICogdjI7XG4gIH0gd2hpbGUgKHMgPiAxKTtcblxuICBjb25zdCByZXN1bHQgPSBNYXRoLnNxcnQoLTIgKiBNYXRoLmxvZyhzKSAvIHMpICogdjE7XG4gIGlmICh0cnVuY2F0ZWQgJiYgcmVzdWx0ID4gMikge1xuICAgIHJldHVybiByYW5kR2F1c3MobWVhbiwgc3RkRGV2LCB0cnVlKTtcbiAgfVxuICByZXR1cm4gbWVhbiArIHN0ZERldiAqIHJlc3VsdDtcbn1cblxuLyoqIFJldHVybnMgc3F1YXJlZCBldWNsZWRpYW4gZGlzdGFuY2UgYmV0d2VlbiB0d28gdmVjdG9ycy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXN0U3F1YXJlZChhOiBWZWN0b3IsIGI6IFZlY3Rvcik6IG51bWJlciB7XG4gIGxldCByZXN1bHQgPSAwO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGEubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBkaWZmID0gYVtpXSAtIGJbaV07XG4gICAgcmVzdWx0ICs9IGRpZmYgKiBkaWZmO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnQoZXhwcjogYm9vbGVhbiwgbXNnOiBzdHJpbmcpIHtcbiAgaWYgKCFleHByKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydFNoYXBlc01hdGNoKFxuICAgIHNoYXBlQTogbnVtYmVyW10sIHNoYXBlQjogbnVtYmVyW10sIGVycm9yTWVzc2FnZVByZWZpeCA9ICcnKTogdm9pZCB7XG4gIGFzc2VydChcbiAgICAgIGFycmF5c0VxdWFsKHNoYXBlQSwgc2hhcGVCKSxcbiAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArIGBTaGFwZXMgJHtzaGFwZUF9IGFuZCAke3NoYXBlQn0gbXVzdCBtYXRjaGApO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gZmxhdHRlbihhcnI6IGFueVtdLCByZXQ/OiBudW1iZXJbXSk6IG51bWJlcltdIHtcbiAgcmV0ID0gKHJldCA9PT0gdW5kZWZpbmVkID8gW10gOiByZXQpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyci5sZW5ndGg7ICsraSkge1xuICAgIGlmIChBcnJheS5pc0FycmF5KGFycltpXSkpIHtcbiAgICAgIGZsYXR0ZW4oYXJyW2ldLCByZXQpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXQucHVzaChhcnJbaV0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmV0O1xufVxuXG5leHBvcnQgdHlwZSBBcnJheURhdGEgPSBudW1iZXJ8bnVtYmVyW118bnVtYmVyW11bXXxudW1iZXJbXVtdW118bnVtYmVyW11bXVtdW107XG5cbmV4cG9ydCBmdW5jdGlvbiBpbmZlclNoYXBlKGFycjogQXJyYXlEYXRhKTogbnVtYmVyW10ge1xuICBjb25zdCBzaGFwZTogbnVtYmVyW10gPSBbXTtcbiAgd2hpbGUgKGFyciBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgc2hhcGUucHVzaChhcnIubGVuZ3RoKTtcbiAgICBhcnIgPSBhcnJbMF07XG4gIH1cbiAgcmV0dXJuIHNoYXBlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2l6ZUZyb21TaGFwZShzaGFwZTogbnVtYmVyW10pOiBudW1iZXIge1xuICBpZiAoc2hhcGUubGVuZ3RoID09PSAwKSB7XG4gICAgLy8gU2NhbGFyLlxuICAgIHJldHVybiAxO1xuICB9XG4gIGxldCBzaXplID0gc2hhcGVbMF07XG4gIGZvciAobGV0IGkgPSAxOyBpIDwgc2hhcGUubGVuZ3RoOyBpKyspIHtcbiAgICBzaXplICo9IHNoYXBlW2ldO1xuICB9XG4gIHJldHVybiBzaXplO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNTY2FsYXJTaGFwZShzaGFwZTogbnVtYmVyW10pOiBib29sZWFuIHtcbiAgcmV0dXJuIHNoYXBlLmxlbmd0aCA9PT0gMDtcbn1cblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuZXhwb3J0IGZ1bmN0aW9uIGFycmF5c0VxdWFsKG4xOiBhbnlbXXxGbG9hdDMyQXJyYXksIG4yOiBhbnlbXXxGbG9hdDMyQXJyYXkpIHtcbiAgaWYgKG4xLmxlbmd0aCAhPT0gbjIubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjEubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAobjFbaV0gIT09IG4yW2ldKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG4gIHJldHVybiB0cnVlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNJbnQoYTogbnVtYmVyKTogYm9vbGVhbiB7XG4gIHJldHVybiBhICUgMSA9PT0gMDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRhbmgoeDogbnVtYmVyKTogbnVtYmVyIHtcbiAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICBpZiAoKE1hdGggYXMgYW55KS50YW5oICE9IG51bGwpIHtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgcmV0dXJuIChNYXRoIGFzIGFueSkudGFuaCh4KTtcbiAgfVxuICBpZiAoeCA9PT0gSW5maW5pdHkpIHtcbiAgICByZXR1cm4gMTtcbiAgfSBlbHNlIGlmICh4ID09PSAtSW5maW5pdHkpIHtcbiAgICByZXR1cm4gLTE7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgZTJ4ID0gTWF0aC5leHAoMiAqIHgpO1xuICAgIHJldHVybiAoZTJ4IC0gMSkgLyAoZTJ4ICsgMSk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpemVUb1NxdWFyaXNoU2hhcGUoc2l6ZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGZvciAobGV0IGEgPSBNYXRoLmZsb29yKE1hdGguc3FydChzaXplKSk7IGEgPiAxOyAtLWEpIHtcbiAgICBpZiAoc2l6ZSAlIGEgPT09IDApIHtcbiAgICAgIHJldHVybiBbYSwgc2l6ZSAvIGFdO1xuICAgIH1cbiAgfVxuICByZXR1cm4gWzEsIHNpemVdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU2h1ZmZsZWRJbmRpY2VzKG46IG51bWJlcik6IFVpbnQzMkFycmF5IHtcbiAgY29uc3Qgc2h1ZmZsZWRJbmRpY2VzID0gbmV3IFVpbnQzMkFycmF5KG4pO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IG47ICsraSkge1xuICAgIHNodWZmbGVkSW5kaWNlc1tpXSA9IGk7XG4gIH1cbiAgc2h1ZmZsZShzaHVmZmxlZEluZGljZXMpO1xuICByZXR1cm4gc2h1ZmZsZWRJbmRpY2VzO1xufVxuIl19 diff --git a/demos/model-builder/cifar10-conv.json b/demos/model-builder/cifar10-conv.json new file mode 100644 index 0000000000..152d1ac230 --- /dev/null +++ b/demos/model-builder/cifar10-conv.json @@ -0,0 +1 @@ +[{"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":16},{"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},{"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":20},{"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},{"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":20},{"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},{"layerName":"Flatten"},{"layerName":"Fully connected","hiddenUnits":10}] \ No newline at end of file diff --git a/demos/model-builder/layer_builder.ts b/demos/model-builder/layer_builder.ts new file mode 100644 index 0000000000..743865356d --- /dev/null +++ b/demos/model-builder/layer_builder.ts @@ -0,0 +1,370 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Array1D, Array2D, Array4D, conv_util, Graph, Initializer, NDArray, NDArrayInitializer, Tensor, util, VarianceScalingInitializer, ZerosInitializer} from '../deeplearnjs'; + +/** + * Classes that specify operation parameters, how they affect output shape, + * and methods for building the operations themselves. Any new ops to be added + * to the model builder UI should be added here. + */ + +export type LayerName = 'Fully connected' | 'ReLU' | 'Convolution' | + 'Max pool' | 'Reshape' | 'Flatten'; + +/** + * Creates a layer builder object. + * + * @param layerName The name of the layer to build. + * @param layerBuilderJson An optional LayerBuilder JSON object. This doesn't + * have the prototype methods on them as it comes from serialization. This + * method creates the object with the necessary prototype methods. + */ +export function getLayerBuilder( + layerName: LayerName, layerBuilderJson?: LayerBuilder): LayerBuilder { + let layerBuilder: LayerBuilder; + switch (layerName) { + case 'Fully connected': + layerBuilder = new FullyConnectedLayerBuilder(); + break; + case 'ReLU': + layerBuilder = new ReLULayerBuilder(); + break; + case 'Convolution': + layerBuilder = new Convolution2DLayerBuilder(); + break; + case 'Max pool': + layerBuilder = new MaxPoolLayerBuilder(); + break; + case 'Reshape': + layerBuilder = new ReshapeLayerBuilder(); + break; + case 'Flatten': + layerBuilder = new FlattenLayerBuilder(); + break; + default: + throw new Error('Layer builder for ' + layerName + ' not found.'); + } + + // For layer builders passed as serialized objects, we create the objects and + // set the fields. + if (layerBuilderJson != null) { + for (const prop in layerBuilderJson) { + if (layerBuilderJson.hasOwnProperty(prop)) { + // tslint:disable-next-line:no-any + (layerBuilder as any)[prop] = (layerBuilderJson as any)[prop]; + } + } + } + return layerBuilder; +} + +export interface LayerParam { + label: string; + initialValue(inputShape: number[]): number|string; + type: 'number'|'text'; + min?: number; + max?: number; + setValue(value: number|string): void; + getValue(): number|string; +} + +export type LayerWeightsDict = { + [name: string]: number[] +}; + +export interface LayerBuilder { + layerName: LayerName; + getLayerParams(): LayerParam[]; + getOutputShape(inputShape: number[]): number[]; + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights?: LayerWeightsDict|null): Tensor; + // Return null if no errors, otherwise return an array of errors. + validate(inputShape: number[]): string[]|null; +} + +export class FullyConnectedLayerBuilder implements LayerBuilder { + layerName: LayerName = 'Fully connected'; + hiddenUnits: number; + + getLayerParams(): LayerParam[] { + return [{ + label: 'Hidden units', + initialValue: (inputShape: number[]) => 10, + type: 'number', + min: 1, + max: 1000, + setValue: (value: number) => this.hiddenUnits = value, + getValue: () => this.hiddenUnits + }]; + } + + getOutputShape(inputShape: number[]): number[] { + return [this.hiddenUnits]; + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + const inputSize = util.sizeFromShape(inputShape); + const wShape: [number, number] = [this.hiddenUnits, inputSize]; + + let weightsInitializer: Initializer; + let biasInitializer: Initializer; + if (weights != null) { + weightsInitializer = + new NDArrayInitializer(Array2D.new(wShape, weights['W'])); + biasInitializer = new NDArrayInitializer(Array1D.new(weights['b'])); + } else { + weightsInitializer = new VarianceScalingInitializer(); + biasInitializer = new ZerosInitializer(); + } + + const useBias = true; + return g.layers.dense( + 'fc1', network, this.hiddenUnits, null, useBias, weightsInitializer, + biasInitializer); + } + + validate(inputShape: number[]) { + if (inputShape.length !== 1) { + return ['Input shape must be a Array1D.']; + } + return null; + } +} + +export class ReLULayerBuilder implements LayerBuilder { + layerName: LayerName = 'ReLU'; + getLayerParams(): LayerParam[] { + return []; + } + + getOutputShape(inputShape: number[]): number[] { + return inputShape; + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + return g.relu(network); + } + + validate(inputShape: number[]): string[]|null { + return null; + } +} + +export class Convolution2DLayerBuilder implements LayerBuilder { + layerName: LayerName = 'Convolution'; + fieldSize: number; + stride: number; + zeroPad: number; + outputDepth: number; + + getLayerParams(): LayerParam[] { + return [ + { + label: 'Field size', + initialValue: (inputShape: number[]) => 3, + type: 'number', + min: 1, + max: 100, + setValue: (value: number) => this.fieldSize = value, + getValue: () => this.fieldSize + }, + { + label: 'Stride', + initialValue: (inputShape: number[]) => 1, + type: 'number', + min: 1, + max: 100, + setValue: (value: number) => this.stride = value, + getValue: () => this.stride + }, + { + label: 'Zero pad', + initialValue: (inputShape: number[]) => 0, + type: 'number', + min: 0, + max: 100, + setValue: (value: number) => this.zeroPad = value, + getValue: () => this.zeroPad + }, + { + label: 'Output depth', + initialValue: (inputShape: number[]) => + this.outputDepth != null ? this.outputDepth : 1, + type: 'number', + min: 1, + max: 1000, + setValue: (value: number) => this.outputDepth = value, + getValue: () => this.outputDepth + } + ]; + } + + getOutputShape(inputShape: number[]): number[] { + return conv_util.computeOutputShape3D( + inputShape as [number, number, number], this.fieldSize, + this.outputDepth, this.stride, this.zeroPad); + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + const inputShape3d = inputShape as [number, number, number]; + const wShape: [number, number, number, number] = + [this.fieldSize, this.fieldSize, inputShape[2], this.outputDepth]; + let w: Array4D; + let b: Array1D; + if (weights != null) { + w = Array4D.new(wShape, weights['W']); + b = Array1D.new(weights['b']); + } else { + w = NDArray.randTruncatedNormal(wShape, 0, 0.1); + b = Array1D.zeros([this.outputDepth]); + } + const wTensor = g.variable('conv2d-' + index + '-w', w); + const bTensor = g.variable('conv2d-' + index + '-b', b); + return g.conv2d( + network, wTensor, bTensor, this.fieldSize, this.outputDepth, + this.stride, this.zeroPad); + } + + validate(inputShape: number[]) { + if (inputShape.length !== 3) { + return ['Input shape must be a Array3D.']; + } + return null; + } +} + +export class MaxPoolLayerBuilder implements LayerBuilder { + layerName: LayerName = 'Max pool'; + fieldSize: number; + stride: number; + zeroPad: number; + + getLayerParams(): LayerParam[] { + return [ + { + label: 'Field size', + initialValue: (inputShape: number[]) => 3, + type: 'number', + min: 1, + max: 100, + setValue: (value: number) => this.fieldSize = value, + getValue: () => this.fieldSize + }, + { + label: 'Stride', + initialValue: (inputShape: number[]) => 1, + type: 'number', + min: 1, + max: 100, + setValue: (value: number) => this.stride = value, + getValue: () => this.stride + }, + { + label: 'Zero pad', + initialValue: (inputShape: number[]) => 0, + type: 'number', + min: 0, + max: 100, + setValue: (value: number) => this.zeroPad = value, + getValue: () => this.zeroPad + } + ]; + } + + getOutputShape(inputShape: number[]): number[] { + return conv_util.computeOutputShape3D( + inputShape as [number, number, number], this.fieldSize, inputShape[2], + this.stride, this.zeroPad); + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + return g.maxPool(network, this.fieldSize, this.stride, this.zeroPad); + } + + validate(inputShape: number[]) { + if (inputShape.length !== 3) { + return ['Input shape must be a Array3D.']; + } + return null; + } +} + +export class ReshapeLayerBuilder implements LayerBuilder { + layerName: LayerName = 'Reshape'; + outputShape: number[]; + getLayerParams() { + return [{ + label: 'Shape (comma separated)', + initialValue: (inputShape: number[]) => inputShape.join(', '), + type: 'text' as 'text', + setValue: (value: string) => this.outputShape = + value.split(',').map((value) => +value), + getValue: () => this.outputShape.join(', ') + }]; + } + + getOutputShape(inputShape: number[]): number[] { + return this.outputShape; + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + return g.reshape(network, this.outputShape); + } + + validate(inputShape: number[]) { + const inputSize = util.sizeFromShape(inputShape); + const outputSize = util.sizeFromShape(this.outputShape); + if (inputSize !== outputSize) { + return [ + `Input size (${inputSize}) must match output size (${outputSize}).` + ]; + } + return null; + } +} + +export class FlattenLayerBuilder implements LayerBuilder { + layerName: LayerName = 'Flatten'; + + getLayerParams(): LayerParam[] { + return []; + } + + getOutputShape(inputShape: number[]): number[] { + return [util.sizeFromShape(inputShape)]; + } + + addLayer( + g: Graph, network: Tensor, inputShape: number[], index: number, + weights: LayerWeightsDict|null): Tensor { + return g.reshape(network, this.getOutputShape(inputShape)); + } + + validate(inputShape: number[]): string[]|null { + return null; + } +} diff --git a/demos/model-builder/mnist-conv.json b/demos/model-builder/mnist-conv.json new file mode 100644 index 0000000000..49d826f60d --- /dev/null +++ b/demos/model-builder/mnist-conv.json @@ -0,0 +1 @@ +[{"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":8},{"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},{"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":16},{"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},{"layerName":"Flatten"},{"layerName":"Fully connected","hiddenUnits":10}] diff --git a/demos/model-builder/mnist-fully-connected.json b/demos/model-builder/mnist-fully-connected.json new file mode 100644 index 0000000000..8dab4bccc6 --- /dev/null +++ b/demos/model-builder/mnist-fully-connected.json @@ -0,0 +1 @@ +[{"layerName":"Flatten"},{"layerName":"Fully connected","hiddenUnits":128},{"layerName":"ReLU"},{"layerName":"Fully connected","hiddenUnits":32},{"layerName":"ReLU"},{"layerName":"Fully connected","hiddenUnits":10}] diff --git a/demos/model-builder/model-builder-datasets-config.json b/demos/model-builder/model-builder-datasets-config.json new file mode 100644 index 0000000000..e4901d604d --- /dev/null +++ b/demos/model-builder/model-builder-datasets-config.json @@ -0,0 +1,43 @@ +{ + "MNIST": { + "data": [{ + "name": "images", + "path": "https://storage.googleapis.com/learnjs-data/model-builder/mnist_images.png", + "dataType": "png", + "shape": [28, 28, 1] + }, { + "name": "labels", + "path": "https://storage.googleapis.com/learnjs-data/model-builder/mnist_labels_uint8", + "dataType": "uint8", + "shape": [10] + }], + "modelConfigs": { + "Fully connected": { + "name": "Fully connected", + "path": "mnist-fully-connected.json" + }, + "Convolutional": { + "path": "mnist-conv.json" + } + } + }, + "CIFAR 10": { + "data": [{ + "name": "images", + "path": "https://storage.googleapis.com/learnjs-data/model-builder/cifar10_images.png", + "dataType": "png", + "shape": [32, 32, 3] + }, { + "name": "labels", + "path": "https://storage.googleapis.com/learnjs-data/model-builder/cifar10_labels_uint8", + "dataType": "uint8", + "shape": [10] + }], + "labelClassNames": ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"], + "modelConfigs": { + "Convolutional": { + "path": "cifar10-conv.json" + } + } + } +} diff --git a/demos/model-builder/model-builder-demo.html b/demos/model-builder/model-builder-demo.html new file mode 100644 index 0000000000..9417369509 --- /dev/null +++ b/demos/model-builder/model-builder-demo.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + LearnJS Demo Page + + + + + + + + + + diff --git a/demos/model-builder/model-builder.html b/demos/model-builder/model-builder.html new file mode 100644 index 0000000000..9dd025560f --- /dev/null +++ b/demos/model-builder/model-builder.html @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/model-builder/model-builder.ts b/demos/model-builder/model-builder.ts new file mode 100644 index 0000000000..b913842052 --- /dev/null +++ b/demos/model-builder/model-builder.ts @@ -0,0 +1,839 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// tslint:disable-next-line:no-unused-variable +import '../ndarray-image-visualizer'; +import '../ndarray-logits-visualizer'; +import './model-layer'; +import '../demo-header'; +import '../demo-footer'; + +import {Array1D, Array3D, DataStats, FeedEntry, Graph, GraphRunner, GraphRunnerEventObserver, InCPUMemoryShuffledInputProviderBuilder, InMemoryDataset, MetricReduction, NDArray, NDArrayMath, NDArrayMathCPU, NDArrayMathGPU, Optimizer, Scalar, Session, SGDOptimizer, Tensor, util} from '../deeplearnjs'; +import {NDArrayImageVisualizer} from '../ndarray-image-visualizer'; +import {NDArrayLogitsVisualizer} from '../ndarray-logits-visualizer'; +import {PolymerElement, PolymerHTMLElement} from '../polymer-spec'; +import * as xhr_dataset from '../xhr-dataset'; +import {XhrDataset, XhrDatasetConfig} from '../xhr-dataset'; + +import {LayerBuilder, LayerWeightsDict} from './layer_builder'; +import {ModelLayer} from './model-layer'; +import * as model_builder_util from './model_builder_util'; +import {Normalization} from './tensorflow'; + +const DATASETS_CONFIG_JSON = 'model-builder-datasets-config.json'; + +// TODO(nsthorat): Make these parameters in the UI. +const BATCH_SIZE = 64; +const LEARNING_RATE = 0.1; +/** How often to evaluate the model against test data. */ +const EVAL_INTERVAL_MS = 1500; +/** How often to compute the cost. Downloading the cost stalls the GPU. */ +const COST_INTERVAL_MS = 500; +/** How many inference examples to show when evaluating accuracy. */ +const INFERENCE_EXAMPLE_COUNT = 15; +const INFERENCE_IMAGE_SIZE_PX = 100; +/** + * How often to show inference examples. This should be less often than + * EVAL_INTERVAL_MS as we only show inference examples during an eval. + */ +const INFERENCE_EXAMPLE_INTERVAL_MS = 3000; + +// Smoothing factor for the examples/s standalone text statistic. +const EXAMPLE_SEC_STAT_SMOOTHING_FACTOR = .7; + +const TRAIN_TEST_RATIO = 5 / 6; + +const IMAGE_DATA_INDEX = 0; +const LABEL_DATA_INDEX = 1; + +// tslint:disable-next-line:variable-name +export let ModelBuilderPolymer = PolymerElement({ + is: 'model-builder', + properties: { + inputShapeDisplay: String, + isValid: Boolean, + inferencesPerSec: Number, + inferenceDuration: Number, + examplesTrained: Number, + examplesPerSec: Number, + totalTimeSec: String, + applicationState: Number, + modelInitialized: Boolean, + showTrainStats: Boolean, + datasetDownloaded: Boolean, + datasetNames: Array, + selectedDatasetName: String, + modelNames: Array, + selectedModelName: String, + selectedNormalizationOption: + {type: Number, value: Normalization.NORMALIZATION_NEGATIVE_ONE_TO_ONE}, + // Stats + showDatasetStats: Boolean, + statsInputMin: Number, + statsInputMax: Number, + statsInputShapeDisplay: String, + statsLabelShapeDisplay: String, + statsExampleCount: Number, + } +}); + +export enum ApplicationState { + IDLE = 1, + TRAINING = 2 +} + +export class ModelBuilder extends ModelBuilderPolymer { + // Polymer properties. + private isValid: boolean; + private totalTimeSec: string; + private applicationState: ApplicationState; + private modelInitialized: boolean; + private showTrainStats: boolean; + private selectedNormalizationOption: number; + + // Datasets and models. + private graphRunner: GraphRunner; + private graph: Graph; + private session: Session; + private optimizer: Optimizer; + private xTensor: Tensor; + private labelTensor: Tensor; + private costTensor: Tensor; + private accuracyTensor: Tensor; + private predictionTensor: Tensor; + + private datasetDownloaded: boolean; + private datasetNames: string[]; + private selectedDatasetName: string; + private modelNames: string[]; + private selectedModelName: string; + private loadedWeights: LayerWeightsDict[]|null; + private dataSets: {[datasetName: string]: InMemoryDataset}; + private dataSet: InMemoryDataset; + private xhrDatasetConfigs: {[datasetName: string]: XhrDatasetConfig}; + private datasetStats: DataStats[]; + + // Stats. + private showDatasetStats: boolean; + private statsInputRange: string; + private statsInputShapeDisplay: string; + private statsLabelShapeDisplay: string; + private statsExampleCount: number; + + // Charts. + private costChart: Chart; + private accuracyChart: Chart; + private examplesPerSecChart: Chart; + private costChartData: ChartPoint[]; + private accuracyChartData: ChartPoint[]; + private examplesPerSecChartData: ChartPoint[]; + + private trainButton: HTMLButtonElement; + + // Visualizers. + private inputNDArrayVisualizers: NDArrayImageVisualizer[]; + private outputNDArrayVisualizers: NDArrayLogitsVisualizer[]; + + private inputShape: number[]; + private labelShape: number[]; + private examplesPerSec: number; + private examplesTrained: number; + private inferencesPerSec: number; + private inferenceDuration: number; + + private inputLayer: ModelLayer; + private hiddenLayers: ModelLayer[]; + + private layersContainer: HTMLDivElement; + + private math: NDArrayMath; + // Keep one instance of each NDArrayMath so we don't create a user-initiated + // number of NDArrayMathGPU's. + private mathGPU: NDArrayMathGPU; + private mathCPU: NDArrayMathCPU; + + ready() { + this.mathGPU = new NDArrayMathGPU(); + this.mathCPU = new NDArrayMathCPU(); + this.math = this.mathGPU; + + const eventObserver: GraphRunnerEventObserver = { + batchesTrainedCallback: (batchesTrained: number) => + this.displayBatchesTrained(batchesTrained), + avgCostCallback: (avgCost: Scalar) => this.displayCost(avgCost), + metricCallback: (metric: Scalar) => this.displayAccuracy(metric), + inferenceExamplesCallback: + (inputFeeds: FeedEntry[][], inferenceOutputs: NDArray[]) => + this.displayInferenceExamplesOutput(inputFeeds, inferenceOutputs), + inferenceExamplesPerSecCallback: (examplesPerSec: number) => + this.displayInferenceExamplesPerSec(examplesPerSec), + trainExamplesPerSecCallback: (examplesPerSec: number) => + this.displayExamplesPerSec(examplesPerSec), + totalTimeCallback: (totalTimeSec: number) => this.totalTimeSec = + totalTimeSec.toFixed(1), + }; + this.graphRunner = new GraphRunner(this.math, this.session, eventObserver); + this.optimizer = new SGDOptimizer(LEARNING_RATE); + + // Set up datasets. + this.populateDatasets(); + + this.querySelector('#dataset-dropdown .dropdown-content')!.addEventListener( + // tslint:disable-next-line:no-any + 'iron-activate', (event: any) => { + // Update the dataset. + const datasetName = event.detail.selected; + this.updateSelectedDataset(datasetName); + + // TODO(nsthorat): Remember the last model used for each dataset. + this.removeAllLayers(); + }); + this.querySelector('#model-dropdown .dropdown-content')!.addEventListener( + // tslint:disable-next-line:no-any + 'iron-activate', (event: any) => { + // Update the model. + const modelName = event.detail.selected; + this.updateSelectedModel(modelName); + }); + + { + const normalizationDropdown = + this.querySelector('#normalization-dropdown .dropdown-content')!; + // tslint:disable-next-line:no-any + normalizationDropdown.addEventListener('iron-activate', (event: any) => { + const selectedNormalizationOption = event.detail.selected; + this.applyNormalization(selectedNormalizationOption); + this.setupDatasetStats(); + }); + } + + this.applicationState = ApplicationState.IDLE; + this.loadedWeights = null; + this.modelInitialized = false; + this.showTrainStats = false; + this.showDatasetStats = false; + + const addButton = this.querySelector('#add-layer')!; + addButton.addEventListener('click', () => this.addLayer()); + + const downloadModelButton = this.querySelector('#download-model')!; + downloadModelButton.addEventListener('click', () => this.downloadModel()); + const uploadModelButton = this.querySelector('#upload-model')!; + uploadModelButton.addEventListener('click', () => this.uploadModel()); + this.setupUploadModelButton(); + + const uploadWeightsButton = this.querySelector('#upload-weights')!; + uploadWeightsButton.addEventListener('click', () => this.uploadWeights()); + this.setupUploadWeightsButton(); + + const stopButton = this.querySelector('#stop')!; + stopButton.addEventListener('click', () => { + this.applicationState = ApplicationState.IDLE; + this.graphRunner.stopTraining(); + }); + + this.trainButton = this.querySelector('#train') as HTMLButtonElement; + this.trainButton.addEventListener('click', () => { + this.createModel(); + this.startTraining(); + }); + + this.querySelector( + '#environment-toggle')!.addEventListener('change', (event) => { + // tslint:disable-next-line:no-any + this.math = (event.target as any).active ? this.mathGPU : this.mathCPU; + this.graphRunner.setMath(this.math); + }); + + this.hiddenLayers = []; + this.examplesPerSec = 0; + this.inferencesPerSec = 0; + } + + isTraining(applicationState: ApplicationState): boolean { + return applicationState === ApplicationState.TRAINING; + } + + isIdle(applicationState: ApplicationState): boolean { + return applicationState === ApplicationState.IDLE; + } + + private getTestData(): NDArray[][] { + const data = this.dataSet.getData(); + if (data == null) { + return null; + } + const [images, labels] = this.dataSet.getData() as [NDArray[], NDArray[]]; + + const start = Math.floor(TRAIN_TEST_RATIO * images.length); + + return [images.slice(start), labels.slice(start)]; + } + + private getTrainingData(): NDArray[][] { + const [images, labels] = this.dataSet.getData() as [NDArray[], NDArray[]]; + + const end = Math.floor(TRAIN_TEST_RATIO * images.length); + + return [images.slice(0, end), labels.slice(0, end)]; + } + + private startInference() { + const testData = this.getTestData(); + if (testData == null) { + // Dataset not ready yet. + return; + } + if (this.isValid && (testData != null)) { + const inferenceShuffledInputProviderGenerator = + new InCPUMemoryShuffledInputProviderBuilder(testData); + const [inferenceInputProvider, inferenceLabelProvider] = + inferenceShuffledInputProviderGenerator.getInputProviders(); + + const inferenceFeeds = [ + {tensor: this.xTensor, data: inferenceInputProvider}, + {tensor: this.labelTensor, data: inferenceLabelProvider} + ]; + + this.graphRunner.infer( + this.predictionTensor, inferenceFeeds, INFERENCE_EXAMPLE_INTERVAL_MS, + INFERENCE_EXAMPLE_COUNT); + } + } + + private startTraining() { + const trainingData = this.getTrainingData(); + const testData = this.getTestData(); + + if (this.isValid && (trainingData != null) && (testData != null)) { + this.recreateCharts(); + this.graphRunner.resetStatistics(); + + const trainingShuffledInputProviderGenerator = + new InCPUMemoryShuffledInputProviderBuilder(trainingData); + const [trainInputProvider, trainLabelProvider] = + trainingShuffledInputProviderGenerator.getInputProviders(); + + const trainFeeds = [ + {tensor: this.xTensor, data: trainInputProvider}, + {tensor: this.labelTensor, data: trainLabelProvider} + ]; + + const accuracyShuffledInputProviderGenerator = + new InCPUMemoryShuffledInputProviderBuilder(testData); + const [accuracyInputProvider, accuracyLabelProvider] = + accuracyShuffledInputProviderGenerator.getInputProviders(); + + const accuracyFeeds = [ + {tensor: this.xTensor, data: accuracyInputProvider}, + {tensor: this.labelTensor, data: accuracyLabelProvider} + ]; + + this.graphRunner.train( + this.costTensor, trainFeeds, BATCH_SIZE, this.optimizer, + undefined /** numBatches */, this.accuracyTensor, accuracyFeeds, + BATCH_SIZE, MetricReduction.MEAN, EVAL_INTERVAL_MS, COST_INTERVAL_MS); + + this.showTrainStats = true; + this.applicationState = ApplicationState.TRAINING; + } + } + + private createModel() { + if (this.session != null) { + this.session.dispose(); + } + + this.modelInitialized = false; + if (this.isValid === false) { + return; + } + + this.graph = new Graph(); + const g = this.graph; + this.xTensor = g.placeholder('input', this.inputShape); + this.labelTensor = g.placeholder('label', this.labelShape); + + let network = this.xTensor; + + for (let i = 0; i < this.hiddenLayers.length; i++) { + let weights: LayerWeightsDict|null = null; + if (this.loadedWeights != null) { + weights = this.loadedWeights[i]; + } + network = this.hiddenLayers[i].addLayer(g, network, i, weights); + } + this.predictionTensor = network; + this.costTensor = + g.softmaxCrossEntropyCost(this.predictionTensor, this.labelTensor); + this.accuracyTensor = + g.argmaxEquals(this.predictionTensor, this.labelTensor); + + this.loadedWeights = null; + + this.session = new Session(g, this.math); + this.graphRunner.setSession(this.session); + + this.startInference(); + + this.modelInitialized = true; + } + + private populateDatasets() { + this.dataSets = {}; + xhr_dataset.getXhrDatasetConfig(DATASETS_CONFIG_JSON) + .then( + xhrDatasetConfigs => { + for (const datasetName in xhrDatasetConfigs) { + if (xhrDatasetConfigs.hasOwnProperty(datasetName)) { + this.dataSets[datasetName] = + new XhrDataset(xhrDatasetConfigs[datasetName]); + } + } + this.datasetNames = Object.keys(this.dataSets); + this.selectedDatasetName = this.datasetNames[0]; + this.xhrDatasetConfigs = xhrDatasetConfigs; + this.updateSelectedDataset(this.datasetNames[0]); + }, + error => { + throw new Error('Dataset config could not be loaded: ' + error); + }); + } + + private updateSelectedDataset(datasetName: string) { + this.graphRunner.stopTraining(); + this.graphRunner.stopInferring(); + + if (this.dataSet != null) { + this.dataSet.dispose(); + } + + this.selectedDatasetName = datasetName; + this.dataSet = this.dataSets[datasetName]; + this.datasetDownloaded = false; + this.showDatasetStats = false; + + this.dataSet.fetchData().then(() => { + this.datasetDownloaded = true; + this.applyNormalization(this.selectedNormalizationOption); + this.setupDatasetStats(); + if (this.isValid) { + this.createModel(); + this.startInference(); + } + }); + // Get prebuilt models. + this.populateModelDropdown(); + + this.inputShape = this.dataSet.getDataShape(IMAGE_DATA_INDEX); + this.labelShape = this.dataSet.getDataShape(LABEL_DATA_INDEX); + + this.layersContainer = + this.querySelector('#hidden-layers') as HTMLDivElement; + + this.inputLayer = this.querySelector('#input-layer') as ModelLayer; + this.inputLayer.outputShapeDisplay = + model_builder_util.getDisplayShape(this.inputShape); + + const labelShapeDisplay = + model_builder_util.getDisplayShape(this.labelShape); + const costLayer = this.querySelector('#cost-layer') as ModelLayer; + costLayer.inputShapeDisplay = labelShapeDisplay; + costLayer.outputShapeDisplay = labelShapeDisplay; + + const outputLayer = this.querySelector('#output-layer') as ModelLayer; + outputLayer.inputShapeDisplay = labelShapeDisplay; + + // Setup the inference example container. + // TODO(nsthorat): Generalize this. + const inferenceContainer = + this.querySelector('#inference-container') as HTMLElement; + inferenceContainer.innerHTML = ''; + this.inputNDArrayVisualizers = []; + this.outputNDArrayVisualizers = []; + for (let i = 0; i < INFERENCE_EXAMPLE_COUNT; i++) { + const inferenceExampleElement = document.createElement('div'); + inferenceExampleElement.className = 'inference-example'; + + // Set up the input visualizer. + const ndarrayImageVisualizer = + document.createElement('ndarray-image-visualizer') as + NDArrayImageVisualizer; + ndarrayImageVisualizer.setShape(this.inputShape); + ndarrayImageVisualizer.setSize( + INFERENCE_IMAGE_SIZE_PX, INFERENCE_IMAGE_SIZE_PX); + this.inputNDArrayVisualizers.push(ndarrayImageVisualizer); + inferenceExampleElement.appendChild(ndarrayImageVisualizer); + + // Set up the output ndarray visualizer. + const ndarrayLogitsVisualizer = + document.createElement('ndarray-logits-visualizer') as + NDArrayLogitsVisualizer; + ndarrayLogitsVisualizer.initialize( + INFERENCE_IMAGE_SIZE_PX, INFERENCE_IMAGE_SIZE_PX); + this.outputNDArrayVisualizers.push(ndarrayLogitsVisualizer); + inferenceExampleElement.appendChild(ndarrayLogitsVisualizer); + + inferenceContainer.appendChild(inferenceExampleElement); + } + } + + private populateModelDropdown() { + const modelNames = ['Custom']; + + const modelConfigs = + this.xhrDatasetConfigs[this.selectedDatasetName].modelConfigs; + for (const modelName in modelConfigs) { + if (modelConfigs.hasOwnProperty(modelName)) { + modelNames.push(modelName); + } + } + + this.modelNames = modelNames; + this.selectedModelName = modelNames[modelNames.length - 1]; + this.updateSelectedModel(this.selectedModelName); + } + + private updateSelectedModel(modelName: string) { + this.removeAllLayers(); + if (modelName === 'Custom') { + // TODO(nsthorat): Remember the custom layers. + return; + } + + this.loadModelFromPath(this.xhrDatasetConfigs[this.selectedDatasetName] + .modelConfigs[modelName] + .path); + } + + private loadModelFromPath(modelPath: string) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', modelPath); + + xhr.onload = () => { + this.loadModelFromJson(xhr.responseText); + }; + xhr.onerror = (error) => { + throw new Error( + 'Model could not be fetched from ' + modelPath + ': ' + error); + }; + xhr.send(); + } + + private setupDatasetStats() { + this.datasetStats = this.dataSet.getStats(); + this.statsExampleCount = this.datasetStats[IMAGE_DATA_INDEX].exampleCount; + this.statsInputRange = '[' + this.datasetStats[IMAGE_DATA_INDEX].inputMin + + ', ' + this.datasetStats[IMAGE_DATA_INDEX].inputMax + ']'; + this.statsInputShapeDisplay = model_builder_util.getDisplayShape( + this.datasetStats[IMAGE_DATA_INDEX].shape); + this.statsLabelShapeDisplay = model_builder_util.getDisplayShape( + this.datasetStats[LABEL_DATA_INDEX].shape); + this.showDatasetStats = true; + } + + private applyNormalization(selectedNormalizationOption: number) { + switch (selectedNormalizationOption) { + case Normalization.NORMALIZATION_NEGATIVE_ONE_TO_ONE: { + this.dataSet.normalizeWithinBounds(IMAGE_DATA_INDEX, -1, 1); + break; + } + case Normalization.NORMALIZATION_ZERO_TO_ONE: { + this.dataSet.normalizeWithinBounds(IMAGE_DATA_INDEX, 0, 1); + break; + } + case Normalization.NORMALIZATION_NONE: { + this.dataSet.removeNormalization(IMAGE_DATA_INDEX); + break; + } + default: { throw new Error('Normalization option must be 0, 1, or 2'); } + } + this.setupDatasetStats(); + } + + private recreateCharts() { + this.costChartData = []; + if (this.costChart != null) { + this.costChart.destroy(); + } + this.costChart = + this.createChart('cost-chart', 'Cost', this.costChartData, 0); + + if (this.accuracyChart != null) { + this.accuracyChart.destroy(); + } + this.accuracyChartData = []; + this.accuracyChart = this.createChart( + 'accuracy-chart', 'Accuracy', this.accuracyChartData, 0, 100); + + if (this.examplesPerSecChart != null) { + this.examplesPerSecChart.destroy(); + } + this.examplesPerSecChartData = []; + this.examplesPerSecChart = this.createChart( + 'examplespersec-chart', 'Examples/sec', this.examplesPerSecChartData, + 0); + } + + private createChart( + canvasId: string, label: string, data: ChartData[], min?: number, + max?: number): Chart { + const context = (document.getElementById(canvasId) as HTMLCanvasElement) + .getContext('2d') as CanvasRenderingContext2D; + return new Chart(context, { + type: 'line', + data: { + datasets: [{ + data, + fill: false, + label, + pointRadius: 0, + borderColor: 'rgba(75,192,192,1)', + borderWidth: 1, + lineTension: 0, + pointHitRadius: 8 + }] + }, + options: { + animation: {duration: 0}, + responsive: false, + scales: { + xAxes: [{type: 'linear', position: 'bottom'}], + yAxes: [{ + ticks: { + max, + min, + } + }] + } + } + }); + } + + displayBatchesTrained(totalBatchesTrained: number) { + this.examplesTrained = BATCH_SIZE * totalBatchesTrained; + } + + displayCost(avgCost: Scalar) { + this.costChartData.push( + {x: this.graphRunner.getTotalBatchesTrained(), y: avgCost.get()}); + this.costChart.update(); + } + + displayAccuracy(accuracy: Scalar) { + this.accuracyChartData.push({ + x: this.graphRunner.getTotalBatchesTrained(), + y: accuracy.get() * 100 + }); + this.accuracyChart.update(); + } + + displayInferenceExamplesPerSec(examplesPerSec: number) { + this.inferencesPerSec = + this.smoothExamplesPerSec(this.inferencesPerSec, examplesPerSec); + this.inferenceDuration = Number((1000 / examplesPerSec).toPrecision(3)); + } + + displayExamplesPerSec(examplesPerSec: number) { + this.examplesPerSecChartData.push( + {x: this.graphRunner.getTotalBatchesTrained(), y: examplesPerSec}); + this.examplesPerSecChart.update(); + this.examplesPerSec = + this.smoothExamplesPerSec(this.examplesPerSec, examplesPerSec); + } + + private smoothExamplesPerSec( + lastExamplesPerSec: number, nextExamplesPerSec: number): number { + return Number((EXAMPLE_SEC_STAT_SMOOTHING_FACTOR * lastExamplesPerSec + + (1 - EXAMPLE_SEC_STAT_SMOOTHING_FACTOR) * nextExamplesPerSec) + .toPrecision(3)); + } + + displayInferenceExamplesOutput( + inputFeeds: FeedEntry[][], inferenceOutputs: NDArray[]) { + let images: Array3D[] = []; + const logits: Array1D[] = []; + const labels: Array1D[] = []; + for (let i = 0; i < inputFeeds.length; i++) { + images.push(inputFeeds[i][IMAGE_DATA_INDEX].data as Array3D); + labels.push(inputFeeds[i][LABEL_DATA_INDEX].data as Array1D); + logits.push(inferenceOutputs[i] as Array1D); + } + + images = + this.dataSet.unnormalizeExamples(images, IMAGE_DATA_INDEX) as Array3D[]; + + // Draw the images. + for (let i = 0; i < inputFeeds.length; i++) { + this.inputNDArrayVisualizers[i].saveImageDataFromNDArray(images[i]); + } + + // Draw the logits. + for (let i = 0; i < inputFeeds.length; i++) { + const softmaxLogits = this.math.softmax(logits[i]); + + this.outputNDArrayVisualizers[i].drawLogits( + softmaxLogits, labels[i], + this.xhrDatasetConfigs[this.selectedDatasetName].labelClassNames); + this.inputNDArrayVisualizers[i].draw(); + + softmaxLogits.dispose(); + } + } + + addLayer(): ModelLayer { + const modelLayer = document.createElement('model-layer') as ModelLayer; + modelLayer.className = 'layer'; + this.layersContainer.appendChild(modelLayer); + + const lastHiddenLayer = this.hiddenLayers[this.hiddenLayers.length - 1]; + const lastOutputShape = lastHiddenLayer != null ? + lastHiddenLayer.getOutputShape() : + this.inputShape; + this.hiddenLayers.push(modelLayer); + modelLayer.initialize(this, lastOutputShape); + return modelLayer; + } + + removeLayer(modelLayer: ModelLayer) { + this.layersContainer.removeChild(modelLayer); + this.hiddenLayers.splice(this.hiddenLayers.indexOf(modelLayer), 1); + this.layerParamChanged(); + } + + private removeAllLayers() { + for (let i = 0; i < this.hiddenLayers.length; i++) { + this.layersContainer.removeChild(this.hiddenLayers[i]); + } + this.hiddenLayers = []; + this.layerParamChanged(); + } + + private validateModel() { + let valid = true; + for (let i = 0; i < this.hiddenLayers.length; ++i) { + valid = valid && this.hiddenLayers[i].isValid(); + } + if (this.hiddenLayers.length > 0) { + const lastLayer = this.hiddenLayers[this.hiddenLayers.length - 1]; + valid = valid && + util.arraysEqual(this.labelShape, lastLayer.getOutputShape()); + } + this.isValid = valid && (this.hiddenLayers.length > 0); + } + + layerParamChanged() { + // Go through each of the model layers and propagate shapes. + let lastOutputShape = this.inputShape; + for (let i = 0; i < this.hiddenLayers.length; i++) { + lastOutputShape = this.hiddenLayers[i].setInputShape(lastOutputShape); + } + this.validateModel(); + + if (this.isValid) { + this.createModel(); + this.startInference(); + } + } + + private downloadModel() { + const modelJson = this.getModelAsJson(); + const blob = new Blob([modelJson], {type: 'text/json'}); + const textFile = window.URL.createObjectURL(blob); + + // Force a download. + const a = document.createElement('a'); + document.body.appendChild(a); + a.style.display = 'none'; + a.href = textFile; + // tslint:disable-next-line:no-any + (a as any).download = this.selectedDatasetName + '_model'; + a.click(); + + document.body.removeChild(a); + window.URL.revokeObjectURL(textFile); + } + + private uploadModel() { + (this.querySelector('#model-file') as HTMLInputElement).click(); + } + + private setupUploadModelButton() { + // Show and setup the load view button. + const fileInput = this.querySelector('#model-file') as HTMLInputElement; + fileInput.addEventListener('change', event => { + const file = fileInput.files![0]; + // Clear out the value of the file chooser. This ensures that if the user + // selects the same file, we'll re-read it. + fileInput.value = ''; + const fileReader = new FileReader(); + fileReader.onload = (evt) => { + this.removeAllLayers(); + const modelJson: string = fileReader.result; + this.loadModelFromJson(modelJson); + }; + fileReader.readAsText(file); + }); + } + + private getModelAsJson(): string { + const layerBuilders: LayerBuilder[] = []; + for (let i = 0; i < this.hiddenLayers.length; i++) { + layerBuilders.push(this.hiddenLayers[i].layerBuilder); + } + return JSON.stringify(layerBuilders); + } + + private loadModelFromJson(modelJson: string) { + let lastOutputShape = this.inputShape; + + const layerBuilders = JSON.parse(modelJson) as LayerBuilder[]; + for (let i = 0; i < layerBuilders.length; i++) { + const modelLayer = this.addLayer(); + modelLayer.loadParamsFromLayerBuilder(lastOutputShape, layerBuilders[i]); + lastOutputShape = this.hiddenLayers[i].setInputShape(lastOutputShape); + } + this.validateModel(); + } + + private uploadWeights() { + (this.querySelector('#weights-file') as HTMLInputElement).click(); + } + + private setupUploadWeightsButton() { + // Show and setup the load view button. + const fileInput = this.querySelector('#weights-file') as HTMLInputElement; + fileInput.addEventListener('change', event => { + const file = fileInput.files![0]; + // Clear out the value of the file chooser. This ensures that if the user + // selects the same file, we'll re-read it. + fileInput.value = ''; + const fileReader = new FileReader(); + fileReader.onload = (evt) => { + const weightsJson: string = fileReader.result; + this.loadWeightsFromJson(weightsJson); + this.createModel(); + this.startInference(); + }; + fileReader.readAsText(file); + }); + } + + private loadWeightsFromJson(weightsJson: string) { + this.loadedWeights = JSON.parse(weightsJson) as LayerWeightsDict[]; + } +} + +document.registerElement(ModelBuilder.prototype.is, ModelBuilder); diff --git a/demos/model-builder/model-layer.html b/demos/model-builder/model-layer.html new file mode 100644 index 0000000000..34c8fceb57 --- /dev/null +++ b/demos/model-builder/model-layer.html @@ -0,0 +1,135 @@ + + + + + + + + + + + diff --git a/demos/model-builder/model-layer.ts b/demos/model-builder/model-layer.ts new file mode 100644 index 0000000000..6080c84518 --- /dev/null +++ b/demos/model-builder/model-layer.ts @@ -0,0 +1,190 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Graph, Tensor} from '../deeplearnjs'; +// tslint:disable-next-line:no-unused-variable +import {PolymerElement, PolymerHTMLElement} from '../polymer-spec'; + +import * as layer_builder from './layer_builder'; +import {LayerBuilder, LayerName, LayerWeightsDict} from './layer_builder'; +import {ModelBuilder} from './model-builder'; +import * as model_builder_util from './model_builder_util'; + +// tslint:disable-next-line:variable-name +export let ModelLayerPolymer = PolymerElement({ + is: 'model-layer', + properties: { + layerName: String, + inputShapeDisplay: String, + outputShapeDisplay: String, + isStatic: {type: Boolean, value: false}, + layerNames: Array, + selectedLayerName: String, + hasError: {type: Boolean, value: false}, + errorMessages: Array, + } +}); + +export class ModelLayer extends ModelLayerPolymer { + // Polymer properties. + inputShapeDisplay: string; + outputShapeDisplay: string; + private layerNames: LayerName[]; + private selectedLayerName: LayerName; + private hasError: boolean; + private errorMessages: string[]; + + private modelBuilder: ModelBuilder; + layerBuilder: LayerBuilder; + private inputShape: number[]; + private outputShape: number[]; + + private paramContainer: HTMLDivElement; + + initialize(modelBuilder: ModelBuilder, inputShape: number[]) { + this.modelBuilder = modelBuilder; + this.paramContainer = + this.querySelector('.param-container') as HTMLDivElement; + this.layerNames = [ + 'Fully connected', 'ReLU', 'Convolution', 'Max pool', 'Reshape', 'Flatten' + ]; + this.inputShape = inputShape; + this.buildParamsUI('Fully connected', this.inputShape); + + this.querySelector('.dropdown-content')!.addEventListener( + // tslint:disable-next-line:no-any + 'iron-activate', (event: any) => { + this.buildParamsUI( + event.detail.selected as LayerName, this.inputShape); + }); + + this.querySelector('#remove-layer')!.addEventListener('click', (event) => { + modelBuilder.removeLayer(this); + }); + } + + setInputShape(shape: number[]): number[] { + this.inputShape = shape; + this.inputShapeDisplay = + model_builder_util.getDisplayShape(this.inputShape); + + const errors: string[] = []; + const validationErrors = this.layerBuilder.validate(this.inputShape); + if (validationErrors != null) { + for (let i = 0; i < validationErrors.length; i++) { + errors.push('Error: ' + validationErrors[i]); + } + } + + try { + this.outputShape = this.layerBuilder.getOutputShape(this.inputShape); + } catch (e) { + errors.push(e); + } + this.outputShapeDisplay = + model_builder_util.getDisplayShape(this.outputShape); + + if (errors.length > 0) { + this.hasError = true; + this.errorMessages = errors; + } else { + this.hasError = false; + this.errorMessages = []; + } + + return this.outputShape; + } + + isValid(): boolean { + return !this.hasError; + } + + getOutputShape(): number[] { + return this.outputShape; + } + + addLayer( + g: Graph, network: Tensor, index: number, + weights: LayerWeightsDict|null): Tensor { + return this.layerBuilder.addLayer( + g, network, this.inputShape, index, weights); + } + + /** + * Build parameters for the UI for a given op type. This is called when the + * op is added, and when the op type changes. + */ + buildParamsUI( + layerName: LayerName, inputShape: number[], + layerBuilderJson?: LayerBuilder) { + this.selectedLayerName = layerName; + + this.layerBuilder = + layer_builder.getLayerBuilder(layerName, layerBuilderJson); + + // Clear any existing parameters. + this.paramContainer.innerHTML = ''; + + // Add all the parameters to the UI. + const layerParams = this.layerBuilder.getLayerParams(); + for (let i = 0; i < layerParams.length; i++) { + const initialValue = layerBuilderJson != null ? + layerParams[i].getValue() : + layerParams[i].initialValue(inputShape); + this.addParamField( + layerParams[i].label, initialValue, layerParams[i].setValue, + layerParams[i].type, layerParams[i].min, layerParams[i].max); + } + this.modelBuilder.layerParamChanged(); + } + + loadParamsFromLayerBuilder( + inputShape: number[], layerBuilderJson: LayerBuilder) { + this.buildParamsUI( + layerBuilderJson.layerName, inputShape, layerBuilderJson); + } + + private addParamField( + label: string, initialValue: number|string, + setValue: (value: number|string) => void, type: 'number'|'text', + min?: number, max?: number) { + const input = document.createElement('paper-input'); + input.setAttribute('always-float-label', 'true'); + input.setAttribute('label', label); + input.setAttribute('value', '' + initialValue); + input.setAttribute('type', type); + if (type === 'number') { + input.setAttribute('min', '' + min); + input.setAttribute('max', '' + max); + } + input.className = 'param-input'; + this.paramContainer.appendChild(input); + + // Update the parent when this changes. + input.addEventListener('input', (event) => { + if (type === 'number') { + // tslint:disable-next-line:no-any + setValue((event.target as any).valueAsNumber as number); + } else { + // tslint:disable-next-line:no-any + setValue((event.target as any).value as string); + } + this.modelBuilder.layerParamChanged(); + }); + setValue(initialValue); + } +} + +document.registerElement(ModelLayer.prototype.is, ModelLayer); diff --git a/demos/model-builder/model_builder_util.ts b/demos/model-builder/model_builder_util.ts new file mode 100644 index 0000000000..d2598c4031 --- /dev/null +++ b/demos/model-builder/model_builder_util.ts @@ -0,0 +1,18 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export function getDisplayShape(shape: number[]) { + return '[' + shape + ']'; +} \ No newline at end of file diff --git a/demos/model-builder/tensorflow.ts b/demos/model-builder/tensorflow.ts new file mode 100644 index 0000000000..11a02bb8d2 --- /dev/null +++ b/demos/model-builder/tensorflow.ts @@ -0,0 +1,200 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Convolution2DLayerBuilder, LayerBuilder, MaxPoolLayerBuilder} from './layer_builder'; + +export enum Normalization { + NORMALIZATION_NEGATIVE_ONE_TO_ONE, + NORMALIZATION_ZERO_TO_ONE, + NORMALIZATION_NONE +} + +export function generatePython( + datasetName: string, normalizationStrategy: number, inputShape: number[], + modelLayers: LayerBuilder[]): string { + const loadData = generateLoadData(datasetName, normalizationStrategy); + const buildModel = generateBuildModel(inputShape, modelLayers); + const captureWeights = generateCaptureWeights(modelLayers); + return [loadData, buildModel, captureWeights].join('\n\n'); +} + +function generateLoadData( + datasetName: string, normalizationStrategy: number): string { + let loadFunction: string; + switch (datasetName) { + case 'CIFAR 10': { + loadFunction = 'cifar10'; + break; + } + case 'MNIST': { + loadFunction = 'mnist'; + break; + } + default: { + throw new Error('datasetName must be \'CIFAR 10\' or \'MNIST\''); + } + } + + let normString: string; + switch (normalizationStrategy) { + case Normalization.NORMALIZATION_NEGATIVE_ONE_TO_ONE: { + normString = 'NORMALIZATION_NEGATIVE_ONE_TO_ONE'; + break; + } + case Normalization.NORMALIZATION_ZERO_TO_ONE: { + normString = 'NORMALIZATION_ZERO_TO_ONE'; + break; + } + case Normalization.NORMALIZATION_NONE: { + normString = 'NORMALIZATION_NONE'; + break; + } + default: { throw new Error('invalid normalizationStrategy value'); } + } + + return `def load_data(): + return learnjs_colab.load_${loadFunction}(learnjs_colab.${normString}) +`; +} + +function generateBuildModelLayer( + layerIndex: number, inputShape: number[], layer: LayerBuilder): string { + let src = ''; + const W = 'W_' + layerIndex; + const b = 'b_' + layerIndex; + const outputShape = layer.getOutputShape(inputShape); + switch (layer.layerName) { + case 'Fully connected': { + const shape = [inputShape[0], outputShape].join(', '); + + src = ` ${W} = tf.Variable(tf.truncated_normal([${shape}], + stddev = 1.0 / math.sqrt(${outputShape[0]}))) + ${b} = tf.Variable(tf.truncated_normal([${outputShape[0]}], stddev = 0.1)) + layers.append({ 'x': layers[-1]['y'], + 'W': ${W}, + 'b': ${b}, + 'y': tf.add(tf.matmul(layers[-1]['y'], ${W}), ${b}) })`; + break; + } + + case 'ReLU': { + src = ` layers.append({ 'x': layers[-1]['y'], + 'y': tf.nn.relu(layers[-1]['y']) })`; + break; + } + + case 'Convolution': { + const conv = layer as Convolution2DLayerBuilder; + const f = conv.fieldSize; + const d1 = inputShape[inputShape.length - 1]; + const d2 = outputShape[outputShape.length - 1]; + const wShape = '[' + f + ', ' + f + ', ' + d1 + ', ' + d2 + ']'; + const stride = '[1, ' + conv.stride + ', ' + conv.stride + ', 1]'; + src = ` ${W} = tf.Variable(tf.truncated_normal(${wShape}, stddev = 0.1)) + ${b} = tf.Variable(tf.truncated_normal([${d2}], stddev = 0.1)) + layers.append({ 'x': layers[-1]['y'], + 'W': ${W}, + 'b': ${b}, + 'y': tf.add(tf.nn.conv2d(layers[-1]['y'], + ${W}, + strides = ${stride}, + padding = 'SAME'), ${b}) })`; + break; + } + + case 'Max pool': { + const mp = layer as MaxPoolLayerBuilder; + const field = '[1, ' + mp.fieldSize + ', ' + mp.fieldSize + ', 1]'; + const stride = '[1, ' + mp.stride + ', ' + mp.stride + ', 1]'; + src = ` layers.append({ 'x': layers[-1]['y'], + 'y': tf.nn.max_pool(layers[-1]['y'], + ${field}, + ${stride}, + padding = 'SAME') })`; + break; + } + + case 'Reshape': { + break; + } + + case 'Flatten': { + src = ` layers.append({ 'x': layers[-1]['y'], + 'y': tf.reshape(layers[-1]['y'], [-1, ${outputShape[0]}]) })`; + break; + } + + default: { + throw new Error('unknown layer type \'' + layer.layerName + '\''); + } + } + + return src; +} + +function generateBuildModel( + inputShape: number[], modelLayers: LayerBuilder[]): string { + const inputShapeStr = inputShape.join(', '); + const sources: string[] = []; + + sources.push(`def build_model(): + layers = [] + + layers.append({ 'y': tf.placeholder(tf.float32, [None, ${inputShapeStr}]), + 'y_label': tf.placeholder(tf.float32, [None, 10]) })`); + + for (let i = 0; i < modelLayers.length; ++i) { + sources.push(generateBuildModelLayer(i + 1, inputShape, modelLayers[i])); + inputShape = modelLayers[i].getOutputShape(inputShape); + } + + sources.push(' return layers\n'); + return sources.join('\n\n'); +} + +function generateCaptureWeights(modelLayers: LayerBuilder[]): string { + const sources: string[] = []; + sources.push(`def capture_weights(): + weights = []`); + + for (let i = 0; i < modelLayers.length; ++i) { + const layer = modelLayers[i]; + const index = i + 1; + let src = ''; + const W = '\'W\': model[' + index + '][\'W\']'; + const b = '\'b\': model[' + index + '][\'b\']'; + switch (layer.layerName) { + case 'Fully connected': { + src = ` weights.append({ ${W}.eval().flatten().tolist(), + ${b}.eval().flatten().tolist() })`; + break; + } + + case 'Convolution': { + src = ` weights.append({ ${W}.eval().transpose().flatten().tolist(), + ${b}.eval().flatten().tolist() })`; + break; + } + + default: { src = ' weights.append({})'; } + } + + src += ' # ' + layer.layerName; + sources.push(src); + } + + sources.push(' return weights'); + return sources.join('\n'); +} diff --git a/demos/models/imagenet_classes.ts b/demos/models/imagenet_classes.ts new file mode 100644 index 0000000000..b9c88a399e --- /dev/null +++ b/demos/models/imagenet_classes.ts @@ -0,0 +1,1034 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export const IMAGENET_CLASSES: {[key: number]: string} = { + 0: 'tench, Tinca tinca', + 1: 'goldfish, Carassius auratus', + 2: 'great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias', + 3: 'tiger shark, Galeocerdo cuvieri', + 4: 'hammerhead, hammerhead shark', + 5: 'electric ray, crampfish, numbfish, torpedo', + 6: 'stingray', + 7: 'cock', + 8: 'hen', + 9: 'ostrich, Struthio camelus', + 10: 'brambling, Fringilla montifringilla', + 11: 'goldfinch, Carduelis carduelis', + 12: 'house finch, linnet, Carpodacus mexicanus', + 13: 'junco, snowbird', + 14: 'indigo bunting, indigo finch, indigo bird, Passerina cyanea', + 15: 'robin, American robin, Turdus migratorius', + 16: 'bulbul', + 17: 'jay', + 18: 'magpie', + 19: 'chickadee', + 20: 'water ouzel, dipper', + 21: 'kite', + 22: 'bald eagle, American eagle, Haliaeetus leucocephalus', + 23: 'vulture', + 24: 'great grey owl, great gray owl, Strix nebulosa', + 25: 'European fire salamander, Salamandra salamandra', + 26: 'common newt, Triturus vulgaris', + 27: 'eft', + 28: 'spotted salamander, Ambystoma maculatum', + 29: 'axolotl, mud puppy, Ambystoma mexicanum', + 30: 'bullfrog, Rana catesbeiana', + 31: 'tree frog, tree-frog', + 32: 'tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui', + 33: 'loggerhead, loggerhead turtle, Caretta caretta', + 34: 'leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea', + 35: 'mud turtle', + 36: 'terrapin', + 37: 'box turtle, box tortoise', + 38: 'banded gecko', + 39: 'common iguana, iguana, Iguana iguana', + 40: 'American chameleon, anole, Anolis carolinensis', + 41: 'whiptail, whiptail lizard', + 42: 'agama', + 43: 'frilled lizard, Chlamydosaurus kingi', + 44: 'alligator lizard', + 45: 'Gila monster, Heloderma suspectum', + 46: 'green lizard, Lacerta viridis', + 47: 'African chameleon, Chamaeleo chamaeleon', + 48: 'Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis', + 49: 'African crocodile, Nile crocodile, Crocodylus niloticus', + 50: 'American alligator, Alligator mississipiensis', + 51: 'triceratops', + 52: 'thunder snake, worm snake, Carphophis amoenus', + 53: 'ringneck snake, ring-necked snake, ring snake', + 54: 'hognose snake, puff adder, sand viper', + 55: 'green snake, grass snake', + 56: 'king snake, kingsnake', + 57: 'garter snake, grass snake', + 58: 'water snake', + 59: 'vine snake', + 60: 'night snake, Hypsiglena torquata', + 61: 'boa constrictor, Constrictor constrictor', + 62: 'rock python, rock snake, Python sebae', + 63: 'Indian cobra, Naja naja', + 64: 'green mamba', + 65: 'sea snake', + 66: 'horned viper, cerastes, sand viper, horned asp, Cerastes cornutus', + 67: 'diamondback, diamondback rattlesnake, Crotalus adamanteus', + 68: 'sidewinder, horned rattlesnake, Crotalus cerastes', + 69: 'trilobite', + 70: 'harvestman, daddy longlegs, Phalangium opilio', + 71: 'scorpion', + 72: 'black and gold garden spider, Argiope aurantia', + 73: 'barn spider, Araneus cavaticus', + 74: 'garden spider, Aranea diademata', + 75: 'black widow, Latrodectus mactans', + 76: 'tarantula', + 77: 'wolf spider, hunting spider', + 78: 'tick', + 79: 'centipede', + 80: 'black grouse', + 81: 'ptarmigan', + 82: 'ruffed grouse, partridge, Bonasa umbellus', + 83: 'prairie chicken, prairie grouse, prairie fowl', + 84: 'peacock', + 85: 'quail', + 86: 'partridge', + 87: 'African grey, African gray, Psittacus erithacus', + 88: 'macaw', + 89: 'sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita', + 90: 'lorikeet', + 91: 'coucal', + 92: 'bee eater', + 93: 'hornbill', + 94: 'hummingbird', + 95: 'jacamar', + 96: 'toucan', + 97: 'drake', + 98: 'red-breasted merganser, Mergus serrator', + 99: 'goose', + 100: 'black swan, Cygnus atratus', + 101: 'tusker', + 102: 'echidna, spiny anteater, anteater', + 103: + 'platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus', + 104: 'wallaby, brush kangaroo', + 105: 'koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus', + 106: 'wombat', + 107: 'jelly fish', + 108: 'sea anemone, anemone', + 109: 'brain coral', + 110: 'flatworm, platyhelminth', + 111: 'nematode, nematode worm, roundworm', + 112: 'conch', + 113: 'snail', + 114: 'slug', + 115: 'sea slug, nudibranch', + 116: 'chiton, coat-of-mail shell, sea cradle, polyplacophore', + 117: 'chambered nautilus, pearly nautilus, nautilus', + 118: 'Dungeness crab, Cancer magister', + 119: 'rock crab, Cancer irroratus', + 120: 'fiddler crab', + 121: + 'king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica', + 122: 'American lobster, Northern lobster, Maine lobster, Homarus americanus', + 123: + 'spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish', + 124: 'crayfish, crawfish, crawdad, crawdaddy', + 125: 'hermit crab', + 126: 'isopod', + 127: 'white stork, Ciconia ciconia', + 128: 'black stork, Ciconia nigra', + 129: 'spoonbill', + 130: 'flamingo', + 131: 'little blue heron, Egretta caerulea', + 132: 'American egret, great white heron, Egretta albus', + 133: 'bittern', + 134: 'crane', + 135: 'limpkin, Aramus pictus', + 136: 'European gallinule, Porphyrio porphyrio', + 137: 'American coot, marsh hen, mud hen, water hen, Fulica americana', + 138: 'bustard', + 139: 'ruddy turnstone, Arenaria interpres', + 140: 'red-backed sandpiper, dunlin, Erolia alpina', + 141: 'redshank, Tringa totanus', + 142: 'dowitcher', + 143: 'oystercatcher, oyster catcher', + 144: 'pelican', + 145: 'king penguin, Aptenodytes patagonica', + 146: 'albatross, mollymawk', + 147: + 'grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus', + 148: 'killer whale, killer, orca, grampus, sea wolf, Orcinus orca', + 149: 'dugong, Dugong dugon', + 150: 'sea lion', + 151: 'Chihuahua', + 152: 'Japanese spaniel', + 153: 'Maltese dog, Maltese terrier, Maltese', + 154: 'Pekinese, Pekingese, Peke', + 155: 'Shih-Tzu', + 156: 'Blenheim spaniel', + 157: 'papillon', + 158: 'toy terrier', + 159: 'Rhodesian ridgeback', + 160: 'Afghan hound, Afghan', + 161: 'basset, basset hound', + 162: 'beagle', + 163: 'bloodhound, sleuthhound', + 164: 'bluetick', + 165: 'black-and-tan coonhound', + 166: 'Walker hound, Walker foxhound', + 167: 'English foxhound', + 168: 'redbone', + 169: 'borzoi, Russian wolfhound', + 170: 'Irish wolfhound', + 171: 'Italian greyhound', + 172: 'whippet', + 173: 'Ibizan hound, Ibizan Podenco', + 174: 'Norwegian elkhound, elkhound', + 175: 'otterhound, otter hound', + 176: 'Saluki, gazelle hound', + 177: 'Scottish deerhound, deerhound', + 178: 'Weimaraner', + 179: 'Staffordshire bullterrier, Staffordshire bull terrier', + 180: + 'American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier', + 181: 'Bedlington terrier', + 182: 'Border terrier', + 183: 'Kerry blue terrier', + 184: 'Irish terrier', + 185: 'Norfolk terrier', + 186: 'Norwich terrier', + 187: 'Yorkshire terrier', + 188: 'wire-haired fox terrier', + 189: 'Lakeland terrier', + 190: 'Sealyham terrier, Sealyham', + 191: 'Airedale, Airedale terrier', + 192: 'cairn, cairn terrier', + 193: 'Australian terrier', + 194: 'Dandie Dinmont, Dandie Dinmont terrier', + 195: 'Boston bull, Boston terrier', + 196: 'miniature schnauzer', + 197: 'giant schnauzer', + 198: 'standard schnauzer', + 199: 'Scotch terrier, Scottish terrier, Scottie', + 200: 'Tibetan terrier, chrysanthemum dog', + 201: 'silky terrier, Sydney silky', + 202: 'soft-coated wheaten terrier', + 203: 'West Highland white terrier', + 204: 'Lhasa, Lhasa apso', + 205: 'flat-coated retriever', + 206: 'curly-coated retriever', + 207: 'golden retriever', + 208: 'Labrador retriever', + 209: 'Chesapeake Bay retriever', + 210: 'German short-haired pointer', + 211: 'vizsla, Hungarian pointer', + 212: 'English setter', + 213: 'Irish setter, red setter', + 214: 'Gordon setter', + 215: 'Brittany spaniel', + 216: 'clumber, clumber spaniel', + 217: 'English springer, English springer spaniel', + 218: 'Welsh springer spaniel', + 219: 'cocker spaniel, English cocker spaniel, cocker', + 220: 'Sussex spaniel', + 221: 'Irish water spaniel', + 222: 'kuvasz', + 223: 'schipperke', + 224: 'groenendael', + 225: 'malinois', + 226: 'briard', + 227: 'kelpie', + 228: 'komondor', + 229: 'Old English sheepdog, bobtail', + 230: 'Shetland sheepdog, Shetland sheep dog, Shetland', + 231: 'collie', + 232: 'Border collie', + 233: 'Bouvier des Flandres, Bouviers des Flandres', + 234: 'Rottweiler', + 235: 'German shepherd, German shepherd dog, German police dog, alsatian', + 236: 'Doberman, Doberman pinscher', + 237: 'miniature pinscher', + 238: 'Greater Swiss Mountain dog', + 239: 'Bernese mountain dog', + 240: 'Appenzeller', + 241: 'EntleBucher', + 242: 'boxer', + 243: 'bull mastiff', + 244: 'Tibetan mastiff', + 245: 'French bulldog', + 246: 'Great Dane', + 247: 'Saint Bernard, St Bernard', + 248: 'Eskimo dog, husky', + 249: 'malamute, malemute, Alaskan malamute', + 250: 'Siberian husky', + 251: 'dalmatian, coach dog, carriage dog', + 252: 'affenpinscher, monkey pinscher, monkey dog', + 253: 'basenji', + 254: 'pug, pug-dog', + 255: 'Leonberg', + 256: 'Newfoundland, Newfoundland dog', + 257: 'Great Pyrenees', + 258: 'Samoyed, Samoyede', + 259: 'Pomeranian', + 260: 'chow, chow chow', + 261: 'keeshond', + 262: 'Brabancon griffon', + 263: 'Pembroke, Pembroke Welsh corgi', + 264: 'Cardigan, Cardigan Welsh corgi', + 265: 'toy poodle', + 266: 'miniature poodle', + 267: 'standard poodle', + 268: 'Mexican hairless', + 269: 'timber wolf, grey wolf, gray wolf, Canis lupus', + 270: 'white wolf, Arctic wolf, Canis lupus tundrarum', + 271: 'red wolf, maned wolf, Canis rufus, Canis niger', + 272: 'coyote, prairie wolf, brush wolf, Canis latrans', + 273: 'dingo, warrigal, warragal, Canis dingo', + 274: 'dhole, Cuon alpinus', + 275: 'African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus', + 276: 'hyena, hyaena', + 277: 'red fox, Vulpes vulpes', + 278: 'kit fox, Vulpes macrotis', + 279: 'Arctic fox, white fox, Alopex lagopus', + 280: 'grey fox, gray fox, Urocyon cinereoargenteus', + 281: 'tabby, tabby cat', + 282: 'tiger cat', + 283: 'Persian cat', + 284: 'Siamese cat, Siamese', + 285: 'Egyptian cat', + 286: + 'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor', + 287: 'lynx, catamount', + 288: 'leopard, Panthera pardus', + 289: 'snow leopard, ounce, Panthera uncia', + 290: 'jaguar, panther, Panthera onca, Felis onca', + 291: 'lion, king of beasts, Panthera leo', + 292: 'tiger, Panthera tigris', + 293: 'cheetah, chetah, Acinonyx jubatus', + 294: 'brown bear, bruin, Ursus arctos', + 295: 'American black bear, black bear, Ursus americanus, Euarctos americanus', + 296: 'ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus', + 297: 'sloth bear, Melursus ursinus, Ursus ursinus', + 298: 'mongoose', + 299: 'meerkat, mierkat', + 300: 'tiger beetle', + 301: 'ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle', + 302: 'ground beetle, carabid beetle', + 303: 'long-horned beetle, longicorn, longicorn beetle', + 304: 'leaf beetle, chrysomelid', + 305: 'dung beetle', + 306: 'rhinoceros beetle', + 307: 'weevil', + 308: 'fly', + 309: 'bee', + 310: 'ant, emmet, pismire', + 311: 'grasshopper, hopper', + 312: 'cricket', + 313: 'walking stick, walkingstick, stick insect', + 314: 'cockroach, roach', + 315: 'mantis, mantid', + 316: 'cicada, cicala', + 317: 'leafhopper', + 318: 'lacewing, lacewing fly', + 319: + 'dragonfly, darning needle, devil\'s darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk', + 320: 'damselfly', + 321: 'admiral', + 322: 'ringlet, ringlet butterfly', + 323: 'monarch, monarch butterfly, milkweed butterfly, Danaus plexippus', + 324: 'cabbage butterfly', + 325: 'sulphur butterfly, sulfur butterfly', + 326: 'lycaenid, lycaenid butterfly', + 327: 'starfish, sea star', + 328: 'sea urchin', + 329: 'sea cucumber, holothurian', + 330: 'wood rabbit, cottontail, cottontail rabbit', + 331: 'hare', + 332: 'Angora, Angora rabbit', + 333: 'hamster', + 334: 'porcupine, hedgehog', + 335: 'fox squirrel, eastern fox squirrel, Sciurus niger', + 336: 'marmot', + 337: 'beaver', + 338: 'guinea pig, Cavia cobaya', + 339: 'sorrel', + 340: 'zebra', + 341: 'hog, pig, grunter, squealer, Sus scrofa', + 342: 'wild boar, boar, Sus scrofa', + 343: 'warthog', + 344: 'hippopotamus, hippo, river horse, Hippopotamus amphibius', + 345: 'ox', + 346: 'water buffalo, water ox, Asiatic buffalo, Bubalus bubalis', + 347: 'bison', + 348: 'ram, tup', + 349: + 'bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis', + 350: 'ibex, Capra ibex', + 351: 'hartebeest', + 352: 'impala, Aepyceros melampus', + 353: 'gazelle', + 354: 'Arabian camel, dromedary, Camelus dromedarius', + 355: 'llama', + 356: 'weasel', + 357: 'mink', + 358: 'polecat, fitch, foulmart, foumart, Mustela putorius', + 359: 'black-footed ferret, ferret, Mustela nigripes', + 360: 'otter', + 361: 'skunk, polecat, wood pussy', + 362: 'badger', + 363: 'armadillo', + 364: 'three-toed sloth, ai, Bradypus tridactylus', + 365: 'orangutan, orang, orangutang, Pongo pygmaeus', + 366: 'gorilla, Gorilla gorilla', + 367: 'chimpanzee, chimp, Pan troglodytes', + 368: 'gibbon, Hylobates lar', + 369: 'siamang, Hylobates syndactylus, Symphalangus syndactylus', + 370: 'guenon, guenon monkey', + 371: 'patas, hussar monkey, Erythrocebus patas', + 372: 'baboon', + 373: 'macaque', + 374: 'langur', + 375: 'colobus, colobus monkey', + 376: 'proboscis monkey, Nasalis larvatus', + 377: 'marmoset', + 378: 'capuchin, ringtail, Cebus capucinus', + 379: 'howler monkey, howler', + 380: 'titi, titi monkey', + 381: 'spider monkey, Ateles geoffroyi', + 382: 'squirrel monkey, Saimiri sciureus', + 383: 'Madagascar cat, ring-tailed lemur, Lemur catta', + 384: 'indri, indris, Indri indri, Indri brevicaudatus', + 385: 'Indian elephant, Elephas maximus', + 386: 'African elephant, Loxodonta africana', + 387: 'lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens', + 388: 'giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca', + 389: 'barracouta, snoek', + 390: 'eel', + 391: + 'coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch', + 392: 'rock beauty, Holocanthus tricolor', + 393: 'anemone fish', + 394: 'sturgeon', + 395: 'gar, garfish, garpike, billfish, Lepisosteus osseus', + 396: 'lionfish', + 397: 'puffer, pufferfish, blowfish, globefish', + 398: 'abacus', + 399: 'abaya', + 400: 'academic gown, academic robe, judge\'s robe', + 401: 'accordion, piano accordion, squeeze box', + 402: 'acoustic guitar', + 403: 'aircraft carrier, carrier, flattop, attack aircraft carrier', + 404: 'airliner', + 405: 'airship, dirigible', + 406: 'altar', + 407: 'ambulance', + 408: 'amphibian, amphibious vehicle', + 409: 'analog clock', + 410: 'apiary, bee house', + 411: 'apron', + 412: + 'ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin', + 413: 'assault rifle, assault gun', + 414: 'backpack, back pack, knapsack, packsack, rucksack, haversack', + 415: 'bakery, bakeshop, bakehouse', + 416: 'balance beam, beam', + 417: 'balloon', + 418: 'ballpoint, ballpoint pen, ballpen, Biro', + 419: 'Band Aid', + 420: 'banjo', + 421: 'bannister, banister, balustrade, balusters, handrail', + 422: 'barbell', + 423: 'barber chair', + 424: 'barbershop', + 425: 'barn', + 426: 'barometer', + 427: 'barrel, cask', + 428: 'barrow, garden cart, lawn cart, wheelbarrow', + 429: 'baseball', + 430: 'basketball', + 431: 'bassinet', + 432: 'bassoon', + 433: 'bathing cap, swimming cap', + 434: 'bath towel', + 435: 'bathtub, bathing tub, bath, tub', + 436: + 'beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon', + 437: 'beacon, lighthouse, beacon light, pharos', + 438: 'beaker', + 439: 'bearskin, busby, shako', + 440: 'beer bottle', + 441: 'beer glass', + 442: 'bell cote, bell cot', + 443: 'bib', + 444: 'bicycle-built-for-two, tandem bicycle, tandem', + 445: 'bikini, two-piece', + 446: 'binder, ring-binder', + 447: 'binoculars, field glasses, opera glasses', + 448: 'birdhouse', + 449: 'boathouse', + 450: 'bobsled, bobsleigh, bob', + 451: 'bolo tie, bolo, bola tie, bola', + 452: 'bonnet, poke bonnet', + 453: 'bookcase', + 454: 'bookshop, bookstore, bookstall', + 455: 'bottlecap', + 456: 'bow', + 457: 'bow tie, bow-tie, bowtie', + 458: 'brass, memorial tablet, plaque', + 459: 'brassiere, bra, bandeau', + 460: 'breakwater, groin, groyne, mole, bulwark, seawall, jetty', + 461: 'breastplate, aegis, egis', + 462: 'broom', + 463: 'bucket, pail', + 464: 'buckle', + 465: 'bulletproof vest', + 466: 'bullet train, bullet', + 467: 'butcher shop, meat market', + 468: 'cab, hack, taxi, taxicab', + 469: 'caldron, cauldron', + 470: 'candle, taper, wax light', + 471: 'cannon', + 472: 'canoe', + 473: 'can opener, tin opener', + 474: 'cardigan', + 475: 'car mirror', + 476: 'carousel, carrousel, merry-go-round, roundabout, whirligig', + 477: 'carpenter\'s kit, tool kit', + 478: 'carton', + 479: 'car wheel', + 480: + 'cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM', + 481: 'cassette', + 482: 'cassette player', + 483: 'castle', + 484: 'catamaran', + 485: 'CD player', + 486: 'cello, violoncello', + 487: 'cellular telephone, cellular phone, cellphone, cell, mobile phone', + 488: 'chain', + 489: 'chainlink fence', + 490: + 'chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour', + 491: 'chain saw, chainsaw', + 492: 'chest', + 493: 'chiffonier, commode', + 494: 'chime, bell, gong', + 495: 'china cabinet, china closet', + 496: 'Christmas stocking', + 497: 'church, church building', + 498: 'cinema, movie theater, movie theatre, movie house, picture palace', + 499: 'cleaver, meat cleaver, chopper', + 500: 'cliff dwelling', + 501: 'cloak', + 502: 'clog, geta, patten, sabot', + 503: 'cocktail shaker', + 504: 'coffee mug', + 505: 'coffeepot', + 506: 'coil, spiral, volute, whorl, helix', + 507: 'combination lock', + 508: 'computer keyboard, keypad', + 509: 'confectionery, confectionary, candy store', + 510: 'container ship, containership, container vessel', + 511: 'convertible', + 512: 'corkscrew, bottle screw', + 513: 'cornet, horn, trumpet, trump', + 514: 'cowboy boot', + 515: 'cowboy hat, ten-gallon hat', + 516: 'cradle', + 517: 'crane', + 518: 'crash helmet', + 519: 'crate', + 520: 'crib, cot', + 521: 'Crock Pot', + 522: 'croquet ball', + 523: 'crutch', + 524: 'cuirass', + 525: 'dam, dike, dyke', + 526: 'desk', + 527: 'desktop computer', + 528: 'dial telephone, dial phone', + 529: 'diaper, nappy, napkin', + 530: 'digital clock', + 531: 'digital watch', + 532: 'dining table, board', + 533: 'dishrag, dishcloth', + 534: 'dishwasher, dish washer, dishwashing machine', + 535: 'disk brake, disc brake', + 536: 'dock, dockage, docking facility', + 537: 'dogsled, dog sled, dog sleigh', + 538: 'dome', + 539: 'doormat, welcome mat', + 540: 'drilling platform, offshore rig', + 541: 'drum, membranophone, tympan', + 542: 'drumstick', + 543: 'dumbbell', + 544: 'Dutch oven', + 545: 'electric fan, blower', + 546: 'electric guitar', + 547: 'electric locomotive', + 548: 'entertainment center', + 549: 'envelope', + 550: 'espresso maker', + 551: 'face powder', + 552: 'feather boa, boa', + 553: 'file, file cabinet, filing cabinet', + 554: 'fireboat', + 555: 'fire engine, fire truck', + 556: 'fire screen, fireguard', + 557: 'flagpole, flagstaff', + 558: 'flute, transverse flute', + 559: 'folding chair', + 560: 'football helmet', + 561: 'forklift', + 562: 'fountain', + 563: 'fountain pen', + 564: 'four-poster', + 565: 'freight car', + 566: 'French horn, horn', + 567: 'frying pan, frypan, skillet', + 568: 'fur coat', + 569: 'garbage truck, dustcart', + 570: 'gasmask, respirator, gas helmet', + 571: 'gas pump, gasoline pump, petrol pump, island dispenser', + 572: 'goblet', + 573: 'go-kart', + 574: 'golf ball', + 575: 'golfcart, golf cart', + 576: 'gondola', + 577: 'gong, tam-tam', + 578: 'gown', + 579: 'grand piano, grand', + 580: 'greenhouse, nursery, glasshouse', + 581: 'grille, radiator grille', + 582: 'grocery store, grocery, food market, market', + 583: 'guillotine', + 584: 'hair slide', + 585: 'hair spray', + 586: 'half track', + 587: 'hammer', + 588: 'hamper', + 589: 'hand blower, blow dryer, blow drier, hair dryer, hair drier', + 590: 'hand-held computer, hand-held microcomputer', + 591: 'handkerchief, hankie, hanky, hankey', + 592: 'hard disc, hard disk, fixed disk', + 593: 'harmonica, mouth organ, harp, mouth harp', + 594: 'harp', + 595: 'harvester, reaper', + 596: 'hatchet', + 597: 'holster', + 598: 'home theater, home theatre', + 599: 'honeycomb', + 600: 'hook, claw', + 601: 'hoopskirt, crinoline', + 602: 'horizontal bar, high bar', + 603: 'horse cart, horse-cart', + 604: 'hourglass', + 605: 'iPod', + 606: 'iron, smoothing iron', + 607: 'jack-o\'-lantern', + 608: 'jean, blue jean, denim', + 609: 'jeep, landrover', + 610: 'jersey, T-shirt, tee shirt', + 611: 'jigsaw puzzle', + 612: 'jinrikisha, ricksha, rickshaw', + 613: 'joystick', + 614: 'kimono', + 615: 'knee pad', + 616: 'knot', + 617: 'lab coat, laboratory coat', + 618: 'ladle', + 619: 'lampshade, lamp shade', + 620: 'laptop, laptop computer', + 621: 'lawn mower, mower', + 622: 'lens cap, lens cover', + 623: 'letter opener, paper knife, paperknife', + 624: 'library', + 625: 'lifeboat', + 626: 'lighter, light, igniter, ignitor', + 627: 'limousine, limo', + 628: 'liner, ocean liner', + 629: 'lipstick, lip rouge', + 630: 'Loafer', + 631: 'lotion', + 632: 'loudspeaker, speaker, speaker unit, loudspeaker system, speaker system', + 633: 'loupe, jeweler\'s loupe', + 634: 'lumbermill, sawmill', + 635: 'magnetic compass', + 636: 'mailbag, postbag', + 637: 'mailbox, letter box', + 638: 'maillot', + 639: 'maillot, tank suit', + 640: 'manhole cover', + 641: 'maraca', + 642: 'marimba, xylophone', + 643: 'mask', + 644: 'matchstick', + 645: 'maypole', + 646: 'maze, labyrinth', + 647: 'measuring cup', + 648: 'medicine chest, medicine cabinet', + 649: 'megalith, megalithic structure', + 650: 'microphone, mike', + 651: 'microwave, microwave oven', + 652: 'military uniform', + 653: 'milk can', + 654: 'minibus', + 655: 'miniskirt, mini', + 656: 'minivan', + 657: 'missile', + 658: 'mitten', + 659: 'mixing bowl', + 660: 'mobile home, manufactured home', + 661: 'Model T', + 662: 'modem', + 663: 'monastery', + 664: 'monitor', + 665: 'moped', + 666: 'mortar', + 667: 'mortarboard', + 668: 'mosque', + 669: 'mosquito net', + 670: 'motor scooter, scooter', + 671: 'mountain bike, all-terrain bike, off-roader', + 672: 'mountain tent', + 673: 'mouse, computer mouse', + 674: 'mousetrap', + 675: 'moving van', + 676: 'muzzle', + 677: 'nail', + 678: 'neck brace', + 679: 'necklace', + 680: 'nipple', + 681: 'notebook, notebook computer', + 682: 'obelisk', + 683: 'oboe, hautboy, hautbois', + 684: 'ocarina, sweet potato', + 685: 'odometer, hodometer, mileometer, milometer', + 686: 'oil filter', + 687: 'organ, pipe organ', + 688: 'oscilloscope, scope, cathode-ray oscilloscope, CRO', + 689: 'overskirt', + 690: 'oxcart', + 691: 'oxygen mask', + 692: 'packet', + 693: 'paddle, boat paddle', + 694: 'paddlewheel, paddle wheel', + 695: 'padlock', + 696: 'paintbrush', + 697: 'pajama, pyjama, pj\'s, jammies', + 698: 'palace', + 699: 'panpipe, pandean pipe, syrinx', + 700: 'paper towel', + 701: 'parachute, chute', + 702: 'parallel bars, bars', + 703: 'park bench', + 704: 'parking meter', + 705: 'passenger car, coach, carriage', + 706: 'patio, terrace', + 707: 'pay-phone, pay-station', + 708: 'pedestal, plinth, footstall', + 709: 'pencil box, pencil case', + 710: 'pencil sharpener', + 711: 'perfume, essence', + 712: 'Petri dish', + 713: 'photocopier', + 714: 'pick, plectrum, plectron', + 715: 'pickelhaube', + 716: 'picket fence, paling', + 717: 'pickup, pickup truck', + 718: 'pier', + 719: 'piggy bank, penny bank', + 720: 'pill bottle', + 721: 'pillow', + 722: 'ping-pong ball', + 723: 'pinwheel', + 724: 'pirate, pirate ship', + 725: 'pitcher, ewer', + 726: 'plane, carpenter\'s plane, woodworking plane', + 727: 'planetarium', + 728: 'plastic bag', + 729: 'plate rack', + 730: 'plow, plough', + 731: 'plunger, plumber\'s helper', + 732: 'Polaroid camera, Polaroid Land camera', + 733: 'pole', + 734: + 'police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria', + 735: 'poncho', + 736: 'pool table, billiard table, snooker table', + 737: 'pop bottle, soda bottle', + 738: 'pot, flowerpot', + 739: 'potter\'s wheel', + 740: 'power drill', + 741: 'prayer rug, prayer mat', + 742: 'printer', + 743: 'prison, prison house', + 744: 'projectile, missile', + 745: 'projector', + 746: 'puck, hockey puck', + 747: 'punching bag, punch bag, punching ball, punchball', + 748: 'purse', + 749: 'quill, quill pen', + 750: 'quilt, comforter, comfort, puff', + 751: 'racer, race car, racing car', + 752: 'racket, racquet', + 753: 'radiator', + 754: 'radio, wireless', + 755: 'radio telescope, radio reflector', + 756: 'rain barrel', + 757: 'recreational vehicle, RV, R.V.', + 758: 'reel', + 759: 'reflex camera', + 760: 'refrigerator, icebox', + 761: 'remote control, remote', + 762: 'restaurant, eating house, eating place, eatery', + 763: 'revolver, six-gun, six-shooter', + 764: 'rifle', + 765: 'rocking chair, rocker', + 766: 'rotisserie', + 767: 'rubber eraser, rubber, pencil eraser', + 768: 'rugby ball', + 769: 'rule, ruler', + 770: 'running shoe', + 771: 'safe', + 772: 'safety pin', + 773: 'saltshaker, salt shaker', + 774: 'sandal', + 775: 'sarong', + 776: 'sax, saxophone', + 777: 'scabbard', + 778: 'scale, weighing machine', + 779: 'school bus', + 780: 'schooner', + 781: 'scoreboard', + 782: 'screen, CRT screen', + 783: 'screw', + 784: 'screwdriver', + 785: 'seat belt, seatbelt', + 786: 'sewing machine', + 787: 'shield, buckler', + 788: 'shoe shop, shoe-shop, shoe store', + 789: 'shoji', + 790: 'shopping basket', + 791: 'shopping cart', + 792: 'shovel', + 793: 'shower cap', + 794: 'shower curtain', + 795: 'ski', + 796: 'ski mask', + 797: 'sleeping bag', + 798: 'slide rule, slipstick', + 799: 'sliding door', + 800: 'slot, one-armed bandit', + 801: 'snorkel', + 802: 'snowmobile', + 803: 'snowplow, snowplough', + 804: 'soap dispenser', + 805: 'soccer ball', + 806: 'sock', + 807: 'solar dish, solar collector, solar furnace', + 808: 'sombrero', + 809: 'soup bowl', + 810: 'space bar', + 811: 'space heater', + 812: 'space shuttle', + 813: 'spatula', + 814: 'speedboat', + 815: 'spider web, spider\'s web', + 816: 'spindle', + 817: 'sports car, sport car', + 818: 'spotlight, spot', + 819: 'stage', + 820: 'steam locomotive', + 821: 'steel arch bridge', + 822: 'steel drum', + 823: 'stethoscope', + 824: 'stole', + 825: 'stone wall', + 826: 'stopwatch, stop watch', + 827: 'stove', + 828: 'strainer', + 829: 'streetcar, tram, tramcar, trolley, trolley car', + 830: 'stretcher', + 831: 'studio couch, day bed', + 832: 'stupa, tope', + 833: 'submarine, pigboat, sub, U-boat', + 834: 'suit, suit of clothes', + 835: 'sundial', + 836: 'sunglass', + 837: 'sunglasses, dark glasses, shades', + 838: 'sunscreen, sunblock, sun blocker', + 839: 'suspension bridge', + 840: 'swab, swob, mop', + 841: 'sweatshirt', + 842: 'swimming trunks, bathing trunks', + 843: 'swing', + 844: 'switch, electric switch, electrical switch', + 845: 'syringe', + 846: 'table lamp', + 847: 'tank, army tank, armored combat vehicle, armoured combat vehicle', + 848: 'tape player', + 849: 'teapot', + 850: 'teddy, teddy bear', + 851: 'television, television system', + 852: 'tennis ball', + 853: 'thatch, thatched roof', + 854: 'theater curtain, theatre curtain', + 855: 'thimble', + 856: 'thresher, thrasher, threshing machine', + 857: 'throne', + 858: 'tile roof', + 859: 'toaster', + 860: 'tobacco shop, tobacconist shop, tobacconist', + 861: 'toilet seat', + 862: 'torch', + 863: 'totem pole', + 864: 'tow truck, tow car, wrecker', + 865: 'toyshop', + 866: 'tractor', + 867: + 'trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi', + 868: 'tray', + 869: 'trench coat', + 870: 'tricycle, trike, velocipede', + 871: 'trimaran', + 872: 'tripod', + 873: 'triumphal arch', + 874: 'trolleybus, trolley coach, trackless trolley', + 875: 'trombone', + 876: 'tub, vat', + 877: 'turnstile', + 878: 'typewriter keyboard', + 879: 'umbrella', + 880: 'unicycle, monocycle', + 881: 'upright, upright piano', + 882: 'vacuum, vacuum cleaner', + 883: 'vase', + 884: 'vault', + 885: 'velvet', + 886: 'vending machine', + 887: 'vestment', + 888: 'viaduct', + 889: 'violin, fiddle', + 890: 'volleyball', + 891: 'waffle iron', + 892: 'wall clock', + 893: 'wallet, billfold, notecase, pocketbook', + 894: 'wardrobe, closet, press', + 895: 'warplane, military plane', + 896: 'washbasin, handbasin, washbowl, lavabo, wash-hand basin', + 897: 'washer, automatic washer, washing machine', + 898: 'water bottle', + 899: 'water jug', + 900: 'water tower', + 901: 'whiskey jug', + 902: 'whistle', + 903: 'wig', + 904: 'window screen', + 905: 'window shade', + 906: 'Windsor tie', + 907: 'wine bottle', + 908: 'wing', + 909: 'wok', + 910: 'wooden spoon', + 911: 'wool, woolen, woollen', + 912: 'worm fence, snake fence, snake-rail fence, Virginia fence', + 913: 'wreck', + 914: 'yawl', + 915: 'yurt', + 916: 'web site, website, internet site, site', + 917: 'comic book', + 918: 'crossword puzzle, crossword', + 919: 'street sign', + 920: 'traffic light, traffic signal, stoplight', + 921: 'book jacket, dust cover, dust jacket, dust wrapper', + 922: 'menu', + 923: 'plate', + 924: 'guacamole', + 925: 'consomme', + 926: 'hot pot, hotpot', + 927: 'trifle', + 928: 'ice cream, icecream', + 929: 'ice lolly, lolly, lollipop, popsicle', + 930: 'French loaf', + 931: 'bagel, beigel', + 932: 'pretzel', + 933: 'cheeseburger', + 934: 'hotdog, hot dog, red hot', + 935: 'mashed potato', + 936: 'head cabbage', + 937: 'broccoli', + 938: 'cauliflower', + 939: 'zucchini, courgette', + 940: 'spaghetti squash', + 941: 'acorn squash', + 942: 'butternut squash', + 943: 'cucumber, cuke', + 944: 'artichoke, globe artichoke', + 945: 'bell pepper', + 946: 'cardoon', + 947: 'mushroom', + 948: 'Granny Smith', + 949: 'strawberry', + 950: 'orange', + 951: 'lemon', + 952: 'fig', + 953: 'pineapple, ananas', + 954: 'banana', + 955: 'jackfruit, jak, jack', + 956: 'custard apple', + 957: 'pomegranate', + 958: 'hay', + 959: 'carbonara', + 960: 'chocolate sauce, chocolate syrup', + 961: 'dough', + 962: 'meat loaf, meatloaf', + 963: 'pizza, pizza pie', + 964: 'potpie', + 965: 'burrito', + 966: 'red wine', + 967: 'espresso', + 968: 'cup', + 969: 'eggnog', + 970: 'alp', + 971: 'bubble', + 972: 'cliff, drop, drop-off', + 973: 'coral reef', + 974: 'geyser', + 975: 'lakeside, lakeshore', + 976: 'promontory, headland, head, foreland', + 977: 'sandbar, sand bar', + 978: 'seashore, coast, seacoast, sea-coast', + 979: 'valley, vale', + 980: 'volcano', + 981: 'ballplayer, baseball player', + 982: 'groom, bridegroom', + 983: 'scuba diver', + 984: 'rapeseed', + 985: 'daisy', + 986: + 'yellow lady\'s slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum', + 987: 'corn', + 988: 'acorn', + 989: 'hip, rose hip, rosehip', + 990: 'buckeye, horse chestnut, conker', + 991: 'coral fungus', + 992: 'agaric', + 993: 'gyromitra', + 994: 'stinkhorn, carrion fungus', + 995: 'earthstar', + 996: + 'hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa', + 997: 'bolete', + 998: 'ear, spike, capitulum', + 999: 'toilet tissue, toilet paper, bathroom tissue' +}; diff --git a/demos/models/imagenet_util.ts b/demos/models/imagenet_util.ts new file mode 100644 index 0000000000..ec39bdb3f9 --- /dev/null +++ b/demos/models/imagenet_util.ts @@ -0,0 +1,160 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; +import * as webgl_util from '../../src/math/webgl/webgl_util'; + +/** + * Unpacks an RGB packed image texture into a 2D physical, 3D logical texture + * with the conventional ndarray format and performs the standard imagenet image + * preprocessing. + */ +export function getUnpackAndPreprocessInputShader( + gpgpu: GPGPUContext, inputShapeRC: [number, number]): WebGLProgram { + const fragmentShaderSource = ` + precision highp float; + uniform sampler2D source; + varying vec2 resultUV; + + const vec2 inputShapeCR = vec2(${inputShapeRC[1]}.0, ${inputShapeRC[0]}.0); + + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + vec2 outputCR = floor(gl_FragCoord.xy); + + vec2 sourceCR = vec2(floor(outputCR[0] / 3.0), outputCR[1]); + vec2 sourceUV = (sourceCR + halfCR) / inputShapeCR; + + vec4 sourceValue = texture2D(source, sourceUV) * 255.0; + + float channelValue = 0.0; + int channel = int(mod(outputCR[0], 3.0)); + + if (channel == 0) { + channelValue = sourceValue.r - 103.939; + } else if (channel == 1) { + channelValue = sourceValue.g - 116.779; + } else if (channel == 2) { + channelValue = sourceValue.b - 123.68; + } + + gl_FragColor = vec4(channelValue, 0, 0, 0); + }`; + return gpgpu.createProgram(fragmentShaderSource); +} + +export function preprocessInput( + gpgpu: GPGPUContext, preprocessInputShader: WebGLProgram, + sourceTex: WebGLTexture, resultTex: WebGLTexture, + shapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture(resultTex, shapeRowCol[0], shapeRowCol[1]); + gpgpu.setProgram(preprocessInputShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} + +/** + * Transposes the depth and the column dimensions of a 3D ndarray represented as + * a 2D texture into a square collage with each channel rendered as a normalized + * grayscale image. The normalization bounds are given as two sample2Ds, + * minValues and maxValues, which give min and max values per channel. These can + * be computed from a max and min pooling layer. + */ +export function getRenderGrayscaleChannelsCollageShader(gpgpu: GPGPUContext): + WebGLProgram { + const fragmentShaderSource = ` + precision highp float; + uniform sampler2D source; + uniform sampler2D minValues; + uniform sampler2D maxValues; + varying vec2 resultUV; + + uniform float imageSize; + uniform float channels; + uniform float imagesPerRow; + uniform vec2 inputShapeCR; + + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + vec2 outputCR = floor(gl_FragCoord.xy); + + float imageRow = floor(outputCR[1] / imageSize); + float imageCol = mod(outputCR[0], imageSize); + + float currentChannel = floor(outputCR[0] / imageSize) + + imageRow * imagesPerRow; + + // When the number of channels is not square, we render white to fill in + // the output texture. + if (currentChannel > channels) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + return; + } + + float sourceC = channels * imageCol + currentChannel; + float sourceR = mod(outputCR[1], imageSize); + + vec2 sourceUV = (vec2(sourceC, sourceR) + halfCR) / inputShapeCR; + + // Flip the vertical axis of the texture for display since we represent + // image textures as vertically flipped. + float sourceValue = texture2D( + source, vec2(sourceUV.s, 1.0 - sourceUV.t)).r; + + // Normalize the value by sampling the minValues and maxValues texture + // which contain min and max per channel. + vec2 minMaxValuesShapeCR = vec2(channels, 1); + vec2 minMaxValuesCR = vec2(currentChannel, 0); + vec2 minMaxValuesUV = (minMaxValuesCR + halfCR) / minMaxValuesShapeCR; + + float minValue = texture2D(minValues, minMaxValuesUV).r; + float maxValue = texture2D(maxValues, minMaxValuesUV).r; + + float normalizedValue = (sourceValue - minValue) / (maxValue - minValue); + + gl_FragColor = vec4( + normalizedValue, normalizedValue, normalizedValue, 1); + } + `; + return gpgpu.createProgram(fragmentShaderSource); +} + +export function renderGrayscaleChannelsCollage( + gpgpu: GPGPUContext, unpackChannelsShader: WebGLProgram, + sourceTex: WebGLTexture, minValuesTex: WebGLTexture, + maxValuesTex: WebGLTexture, inputShapeRC: [number, number], + imageSize: number, channels: number, textureSize: number, numRows: number) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + gpgpu.setProgram(unpackChannelsShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.setInputMatrixTexture(minValuesTex, 'minValues', 1); + gpgpu.setInputMatrixTexture(maxValuesTex, 'maxValues', 2); + + const imageSizeLoc = gpgpu.getUniformLocation('imageSize'); + gpgpu.gl.uniform1f(imageSizeLoc, imageSize); + + const channelsLoc = gpgpu.getUniformLocation('channels'); + gpgpu.gl.uniform1f(channelsLoc, channels); + + const imagesPerRowLoc = gpgpu.getUniformLocation('imagesPerRow'); + gpgpu.gl.uniform1f(imagesPerRowLoc, Math.floor(textureSize / imageSize)); + + const inputShapeCRLoc = gpgpu.getUniformLocation('inputShapeCR'); + gpgpu.gl.uniform2f(inputShapeCRLoc, inputShapeRC[1], inputShapeRC[0]); + + gpgpu.executeProgram(); +} diff --git a/demos/models/squeezenet.ts b/demos/models/squeezenet.ts new file mode 100644 index 0000000000..382fa5f069 --- /dev/null +++ b/demos/models/squeezenet.ts @@ -0,0 +1,191 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {CheckpointLoader} from '../../src/checkpoint_loader'; +import {NDArrayMathCPU} from '../../src/math/math_cpu'; +import {NDArrayMathGPU} from '../../src/math/math_gpu'; +import {Array1D, Array3D, Array4D, NDArray} from '../../src/math/ndarray'; +import {GPGPUContext} from '../../src/math/webgl/gpgpu_context'; + +import * as imagenet_classes from './imagenet_classes'; +import * as imagenet_util from './imagenet_util'; + +const IMAGE_SIZE = 227; +const NUM_CLASSES = 1000; +const GOOGLE_CLOUD_STORAGE_DIR = + 'https://storage.googleapis.com/learnjs-data/checkpoint_zoo/'; + +export class SqueezeNet { + private variables: {[varName: string]: NDArray}; + + private preprocessInputShader: WebGLShader; + + constructor(private gpgpu: GPGPUContext, private math: NDArrayMathGPU) { + this.preprocessInputShader = + imagenet_util.getUnpackAndPreprocessInputShader( + gpgpu, [IMAGE_SIZE, IMAGE_SIZE]); + } + + /** + * Loads necessary variables for SqueezeNet. Resolves the promise when the + * variables have all been loaded. + */ + loadVariables(): Promise { + return new Promise((resolve, reject) => { + const checkpointLoader = + new CheckpointLoader(GOOGLE_CLOUD_STORAGE_DIR + 'squeezenet1_1/'); + checkpointLoader.getAllVariables().then(variables => { + this.variables = variables; + resolve(); + }); + }); + } + + /** + * Preprocess an RGB color texture before inferring through squeezenet. + * @param rgbTexture The RGB color texture to process into an Array3D. + * @param imageDimensions The 2D dimensions of the image. + */ + preprocessColorTextureToArray3D(rgbTexture: WebGLTexture, imageDimensions: [ + number, number + ]): Array3D { + const preprocessResultShapeRC: [number, number] = + [imageDimensions[0], imageDimensions[0] * 3]; + + const preprocessResultTexture = + this.math.getTextureManager().acquireTexture(preprocessResultShapeRC); + + imagenet_util.preprocessInput( + this.gpgpu, this.preprocessInputShader, rgbTexture, + preprocessResultTexture, preprocessResultShapeRC); + return NDArray.make([imageDimensions[0], imageDimensions[0], 3], { + texture: preprocessResultTexture, + textureShapeRC: preprocessResultShapeRC + }); + } + + /** + * Infer through SqueezeNet, assumes variables have been loaded. This does + * standard ImageNet pre-processing before inferring through the model. This + * method returns named activations as well as pre-softmax logits. The user + * needs to clean up namedActivations after inferring. + * + * @param preprocessedInput preprocessed input Array. + * @return Named activations and the pre-softmax logits. + */ + infer(preprocessedInput: Array3D): + {namedActivations: {[activationName: string]: Array3D}, logits: Array1D} { + const namedActivations: {[key: string]: Array3D} = {}; + + const avgpool10 = this.math.scope((keep) => { + const conv1 = this.math.conv2d( + preprocessedInput, this.variables['conv1_W:0'] as Array4D, + this.variables['conv1_b:0'] as Array1D, 2, 0); + const conv1relu = keep(this.math.relu(conv1)); + namedActivations['conv_1'] = conv1relu; + + const pool1 = keep(this.math.maxPool(conv1relu, 3, 2, 0)); + namedActivations['maxpool_1'] = pool1; + + const fire2 = keep(this.fireModule(pool1, 2)); + namedActivations['fire2'] = fire2; + + const fire3 = keep(this.fireModule(fire2, 3)); + namedActivations['fire3'] = fire3; + + // Because we don't have uneven padding yet, manually pad the ndarray on + // the right. + const fire3Reshape2d = + fire3.as2D(fire3.shape[0], fire3.shape[1] * fire3.shape[2]); + const fire3Sliced2d = this.math.slice2D( + fire3Reshape2d, [0, 0], + [fire3.shape[0] - 1, (fire3.shape[1] - 1) * fire3.shape[2]]); + const fire3Sliced = fire3Sliced2d.as3D( + fire3.shape[0] - 1, fire3.shape[1] - 1, fire3.shape[2]); + const pool2 = keep(this.math.maxPool(fire3Sliced, 3, 2, 0)); + namedActivations['maxpool_2'] = pool2; + + const fire4 = keep(this.fireModule(pool2, 4)); + namedActivations['fire4'] = fire4; + + const fire5 = keep(this.fireModule(fire4, 5)); + namedActivations['fire5'] = fire5; + + const pool3 = keep(this.math.maxPool(fire5, 3, 2, 0)); + namedActivations['maxpool_3'] = pool3; + + const fire6 = keep(this.fireModule(pool3, 6)); + namedActivations['fire6'] = fire6; + + const fire7 = keep(this.fireModule(fire6, 7)); + namedActivations['fire7'] = fire7; + + const fire8 = keep(this.fireModule(fire7, 8)); + namedActivations['fire8'] = fire8; + + const fire9 = keep(this.fireModule(fire8, 9)); + namedActivations['fire9'] = fire9; + + const conv10 = keep(this.math.conv2d( + fire9, this.variables['conv10_W:0'] as Array4D, + this.variables['conv10_b:0'] as Array1D, 1, 0)); + namedActivations['conv10'] = conv10; + + return this.math.avgPool(conv10, conv10.shape[0], 1, 0).as1D(); + }); + + return {namedActivations, logits: avgpool10}; + } + + private fireModule(input: Array3D, fireId: number) { + const y1 = this.math.conv2d( + input, this.variables['fire' + fireId + '/squeeze1x1_W:0'] as Array4D, + this.variables['fire' + fireId + '/squeeze1x1_b:0'] as Array1D, 1, 0); + const y2 = this.math.relu(y1); + const left1 = this.math.conv2d( + y2, this.variables['fire' + fireId + '/expand1x1_W:0'] as Array4D, + this.variables['fire' + fireId + '/expand1x1_b:0'] as Array1D, 1, 0); + const left2 = this.math.relu(left1); + + const right1 = this.math.conv2d( + y2, this.variables['fire' + fireId + '/expand3x3_W:0'] as Array4D, + this.variables['fire' + fireId + '/expand3x3_b:0'] as Array1D, 1, 1); + const right2 = this.math.relu(right1); + + return this.math.concat3D(left2, right2, 2); + } + + /** + * Get the topK classes for pre-softmax logits. Returns a map of className + * to softmax normalized probability. + * + * @param logits Pre-softmax logits array. + * @param topK How many top classes to return. + */ + getTopKClasses(logits: Array1D, topK: number): {[className: string]: number} { + const predictions = this.math.softmax(logits); + const topk = new NDArrayMathCPU().topK(predictions, topK); + const topkIndices = topk.indices.getValues(); + const topkValues = topk.values.getValues(); + + const topClassesToProbability: {[className: string]: number} = {}; + for (let i = 0; i < topkIndices.length; i++) { + topClassesToProbability[imagenet_classes + .IMAGENET_CLASSES[topkIndices[i]]] = + topkValues[i]; + } + return topClassesToProbability; + } +} diff --git a/demos/ndarray-image-visualizer.html b/demos/ndarray-image-visualizer.html new file mode 100644 index 0000000000..304a9b21a4 --- /dev/null +++ b/demos/ndarray-image-visualizer.html @@ -0,0 +1,25 @@ + + + + + + diff --git a/demos/ndarray-image-visualizer.ts b/demos/ndarray-image-visualizer.ts new file mode 100644 index 0000000000..1b3d1abfa2 --- /dev/null +++ b/demos/ndarray-image-visualizer.ts @@ -0,0 +1,90 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// tslint:disable-next-line:no-unused-variable +import {Array3D} from '../src/math/ndarray'; + +import {PolymerElement, PolymerHTMLElement} from './polymer-spec'; + +// tslint:disable-next-line +export let NDArrayImageVisualizerPolymer = + PolymerElement({is: 'ndarray-image-visualizer', properties: {}}); + +export class NDArrayImageVisualizer extends NDArrayImageVisualizerPolymer { + private canvas: HTMLCanvasElement; + private canvasContext: CanvasRenderingContext2D; + private imageData: ImageData; + + ready() { + this.canvas = this.querySelector('#canvas') as HTMLCanvasElement; + this.canvas.width = 0; + this.canvas.height = 0; + this.canvasContext = + this.canvas.getContext('2d') as CanvasRenderingContext2D; + this.canvas.style.display = 'none'; + } + + setShape(shape: number[]) { + this.canvas.width = shape[1]; + this.canvas.height = shape[0]; + } + + setSize(width: number, height: number) { + this.canvas.style.width = width + 'px'; + this.canvas.style.height = height + 'px'; + } + + saveImageDataFromNDArray(ndarray: Array3D) { + this.imageData = this.canvasContext.createImageData( + this.canvas.width, this.canvas.height); + if (ndarray.shape[2] === 1) { + this.drawGrayscaleImageData(ndarray); + } else if (ndarray.shape[2] === 3) { + this.drawRGBImageData(ndarray); + } + } + + drawRGBImageData(ndarray: Array3D) { + let pixelOffset = 0; + for (let i = 0; i < ndarray.shape[0]; i++) { + for (let j = 0; j < ndarray.shape[1]; j++) { + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 1); + this.imageData.data[pixelOffset++] = ndarray.get(i, j, 2); + this.imageData.data[pixelOffset++] = 255; + } + } + } + + drawGrayscaleImageData(ndarray: Array3D) { + let pixelOffset = 0; + for (let i = 0; i < ndarray.shape[0]; i++) { + for (let j = 0; j < ndarray.shape[1]; j++) { + const value = ndarray.get(i, j, 0); + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = value; + this.imageData.data[pixelOffset++] = 255; + } + } + } + + draw() { + this.canvas.style.display = ''; + this.canvasContext.putImageData(this.imageData, 0, 0); + } +} +document.registerElement( + NDArrayImageVisualizer.prototype.is, NDArrayImageVisualizer); diff --git a/demos/ndarray-logits-visualizer.html b/demos/ndarray-logits-visualizer.html new file mode 100644 index 0000000000..dd444f5240 --- /dev/null +++ b/demos/ndarray-logits-visualizer.html @@ -0,0 +1,45 @@ + + + + + + diff --git a/demos/ndarray-logits-visualizer.ts b/demos/ndarray-logits-visualizer.ts new file mode 100644 index 0000000000..3d4ae145dd --- /dev/null +++ b/demos/ndarray-logits-visualizer.ts @@ -0,0 +1,98 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// tslint:disable-next-line:no-unused-variable +import {NDArrayMathCPU} from '../src/math/math_cpu'; +import {Array1D} from '../src/math/ndarray'; + +import {PolymerElement, PolymerHTMLElement} from './polymer-spec'; + +const TOP_K = 3; + +// tslint:disable-next-line +export let NDArrayLogitsVisualizerPolymer = + PolymerElement({is: 'ndarray-logits-visualizer', properties: {}}); + +export class NDArrayLogitsVisualizer extends NDArrayLogitsVisualizerPolymer { + private logitLabelElements: HTMLElement[]; + private logitVizElements: HTMLElement[]; + private width: number; + + initialize(width: number, height: number) { + this.width = width; + this.logitLabelElements = []; + this.logitVizElements = []; + const container = this.querySelector('.logits-container') as HTMLElement; + container.style.height = height + 'px'; + + for (let i = 0; i < TOP_K; i++) { + const logitContainer = document.createElement('div'); + logitContainer.style.height = height / (TOP_K + 1) + 'px'; + logitContainer.style.margin = + height / ((2 * TOP_K) * (TOP_K + 1)) + 'px 0'; + logitContainer.className = + 'single-logit-container ndarray-logits-visualizer'; + + const logitLabelElement = document.createElement('div'); + logitLabelElement.className = 'logit-label ndarray-logits-visualizer'; + this.logitLabelElements.push(logitLabelElement); + + const logitVizOuterElement = document.createElement('div'); + logitVizOuterElement.className = + 'logit-viz-outer ndarray-logits-visualizer'; + + const logitVisInnerElement = document.createElement('div'); + logitVisInnerElement.className = + 'logit-viz-inner ndarray-logits-visualizer'; + logitVisInnerElement.innerHTML = ' '; + logitVizOuterElement.appendChild(logitVisInnerElement); + + this.logitVizElements.push(logitVisInnerElement); + + logitContainer.appendChild(logitLabelElement); + logitContainer.appendChild(logitVizOuterElement); + container.appendChild(logitContainer); + } + } + + drawLogits( + predictedLogits: Array1D, labelLogits: Array1D, + labelClassNames?: string[]) { + const mathCpu = new NDArrayMathCPU(); + const labelClass = mathCpu.argMax(labelLogits).get(); + + const topk = mathCpu.topK(predictedLogits, TOP_K); + const topkIndices = topk.indices.getValues(); + const topkValues = topk.values.getValues(); + + for (let i = 0; i < topkIndices.length; i++) { + const index = topkIndices[i]; + this.logitLabelElements[i].innerText = + labelClassNames ? labelClassNames[index] : index + ''; + this.logitLabelElements[i].style.width = + labelClassNames != null ? '100px' : '20px'; + this.logitVizElements[i].style.backgroundColor = index === labelClass ? + 'rgba(120, 185, 50, .84)' : + 'rgba(220, 10, 10, 0.84)'; + this.logitVizElements[i].style.width = + Math.floor(100 * topkValues[i]) + '%'; + this.logitVizElements[i].innerText = + `${(100 * topkValues[i]).toFixed(1)}%`; + } + } +} + +document.registerElement( + NDArrayLogitsVisualizer.prototype.is, NDArrayLogitsVisualizer); diff --git a/demos/nn-art/bundle.js b/demos/nn-art/bundle.js new file mode 100644 index 0000000000..1f8690b70b --- /dev/null +++ b/demos/nn-art/bundle.js @@ -0,0 +1,8264 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o " + inputNumDimensions + ".0) {\n gl_FragColor = vec4(z[1], 0, 0, 0);\n } else {\n gl_FragColor = texture2D(source, resultUV);\n }\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getAddLatentVariablesShader = getAddLatentVariablesShader; +function addLatentVariables(gpgpu, addZShader, sourceTex, resultTex, shapeRowCol, z1, z2) { + gpgpu.setOutputMatrixTexture(resultTex, shapeRowCol[0], shapeRowCol[1]); + gpgpu.setProgram(addZShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + var zLoc = gpgpu.getUniformLocation('z'); + gpgpu.gl.uniform2f(zLoc, z1, z2); + gpgpu.executeProgram(); +} +exports.addLatentVariables = addLatentVariables; +function getRenderShader(gpgpu, imageSize) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n uniform int colorMode;\n uniform float outputNumDimensions;\n\n const float destinationSize = " + imageSize + ".0;\n\n const mat3 yuv2rgb = mat3(\n 1, 1, 1,\n 0, -.34413, 1.772,\n 1.402, -.71414, 0);\n\n vec3 hsv2rgb(vec3 c) {\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n }\n\n void main() {\n vec2 outputCR = floor(gl_FragCoord.xy);\n float inputC = outputCR.y * destinationSize + outputCR.x;\n float u = (inputC + 0.5) / " + imageSize * imageSize + ".0;\n\n vec4 inputR = vec4(0.0, 1.0, 2.0, 3.0);\n vec4 v = (inputR + 0.5) / outputNumDimensions;\n\n vec4 values = vec4(\n texture2D(source, vec2(u, v[0])).r,\n texture2D(source, vec2(u, v[1])).r,\n texture2D(source, vec2(u, v[2])).r,\n texture2D(source, vec2(u, v[3])).r);\n\n if (colorMode == 0) {\n // RGB\n gl_FragColor = vec4(values.rgb, 1.0);\n } else if (colorMode == 1) {\n // RGBA\n gl_FragColor = values;\n } else if (colorMode == 2) {\n // HSV\n vec3 rgb = hsv2rgb(values.rgb);\n gl_FragColor = vec4(rgb, 1.0);\n } else if (colorMode == 3) {\n // HSVA\n vec3 rgb = hsv2rgb(values.rgb);\n gl_FragColor = vec4(rgb, values[3]);\n } else if (colorMode == 4 || colorMode == 5) {\n // YUV\n values[0] = clamp(values[0], 0.2, 0.8);\n values[1] = values[1] - 0.5;\n values[2] = values[2] - 0.5;\n vec3 rgb = yuv2rgb * values.rgb;\n if (colorMode == 4) {\n // YUV\n gl_FragColor = vec4(rgb, 1.0);\n } else if (colorMode == 5) {\n // YUVA\n gl_FragColor = vec4(rgb, values.a);\n }\n } else if (colorMode == 6) {\n gl_FragColor = vec4(values[0], values[0], values[0], 1.0);\n }\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderShader = getRenderShader; +function render(gpgpu, renderShader, sourceTex, outputNumDimensions, colorMode) { + learnjs_1.webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + var colorModeLoc = gpgpu.getUniformLocation('colorMode'); + gpgpu.gl.uniform1i(colorModeLoc, colorMode); + var outputNumDimensionsLoc = gpgpu.getUniformLocation('outputNumDimensions'); + gpgpu.gl.uniform1f(outputNumDimensionsLoc, outputNumDimensions); + gpgpu.executeProgram(); +} +exports.render = render; +function imagePixelToNormalizedCoord(x, y, imageWidth, imageHeight, zSize) { + var halfWidth = imageWidth * 0.5; + var halfHeight = imageHeight * 0.5; + var normX = (x - halfWidth) / imageWidth; + var normY = (y - halfHeight) / imageHeight; + var r = Math.sqrt(normX * normX + normY * normY); + var result = [normX, normY, r]; + for (var i = 0; i < zSize; i++) { + result.push(0); + } + return result; +} +exports.imagePixelToNormalizedCoord = imagePixelToNormalizedCoord; + +},{"../learnjs":3}],7:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function PolymerElement(spec) { + return Polymer.Class(spec); +} +exports.PolymerElement = PolymerElement; + +},{}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var MANIFEST_FILE = 'manifest.json'; +var CheckpointLoader = (function () { + function CheckpointLoader(urlPath) { + this.urlPath = urlPath; + if (this.urlPath.charAt(this.urlPath.length - 1) !== '/') { + this.urlPath += '/'; + } + } + CheckpointLoader.prototype.loadManifest = function () { + var _this = this; + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', _this.urlPath + MANIFEST_FILE); + xhr.onload = function () { + _this.checkpointManifest = JSON.parse(xhr.responseText); + resolve(); + }; + xhr.onerror = function (error) { + throw new Error(MANIFEST_FILE + " not found at " + _this.urlPath + ". " + error); + }; + xhr.send(); + }); + }; + CheckpointLoader.prototype.getCheckpointManifest = function () { + var _this = this; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + resolve(_this.checkpointManifest); + }); + }); + } + return new Promise(function (resolve, reject) { + resolve(_this.checkpointManifest); + }); + }; + CheckpointLoader.prototype.getAllVariables = function () { + var _this = this; + if (this.variables != null) { + return new Promise(function (resolve, reject) { + resolve(_this.variables); + }); + } + return new Promise(function (resolve, reject) { + _this.getCheckpointManifest().then(function (checkpointDefinition) { + var variableNames = Object.keys(_this.checkpointManifest); + var variablePromises = []; + for (var i = 0; i < variableNames.length; i++) { + variablePromises.push(_this.getVariable(variableNames[i])); + } + Promise.all(variablePromises).then(function (variables) { + _this.variables = {}; + for (var i = 0; i < variables.length; i++) { + _this.variables[variableNames[i]] = variables[i]; + } + resolve(_this.variables); + }); + }); + }); + }; + CheckpointLoader.prototype.getVariable = function (varName) { + var _this = this; + if (!(varName in this.checkpointManifest)) { + throw new Error('Cannot load non-existant variable ' + varName); + } + var variableRequestPromiseMethod = function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + var fname = _this.checkpointManifest[varName].filename; + xhr.open('GET', _this.urlPath + fname); + xhr.onload = function () { + var values = new Float32Array(xhr.response); + var ndarray = ndarray_1.NDArray.make(_this.checkpointManifest[varName].shape, { values: values }); + resolve(ndarray); + }; + xhr.onerror = function (error) { + throw new Error('Could not fetch variable ' + varName + ': ' + error); + }; + xhr.send(); + }; + if (this.checkpointManifest == null) { + return new Promise(function (resolve, reject) { + _this.loadManifest().then(function () { + new Promise(variableRequestPromiseMethod).then(resolve); + }); + }); + } + return new Promise(variableRequestPromiseMethod); + }; + return CheckpointLoader; +}()); +exports.CheckpointLoader = CheckpointLoader; + +},{"./math/ndarray":25}],9:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var STATS_SAMPLE_PERCENTAGE = 0.1; +var InMemoryDataset = (function () { + function InMemoryDataset(dataShapes) { + this.dataShapes = dataShapes; + this.normalizationInfo = {}; + } + InMemoryDataset.prototype.getDataShape = function (dataIndex) { + return this.dataShapes[dataIndex]; + }; + InMemoryDataset.prototype.getData = function () { + return this.dataset; + }; + InMemoryDataset.prototype.getStats = function () { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + return this.dataset.map(function (d) { return _this.getStatsForData(d); }); + }; + InMemoryDataset.prototype.getStatsForData = function (data) { + var inputMin = Number.POSITIVE_INFINITY; + var inputMax = Number.NEGATIVE_INFINITY; + var exampleIndices = data.map(function (example, i) { return i; }); + util.shuffle(exampleIndices); + exampleIndices = + exampleIndices.slice(exampleIndices.length * STATS_SAMPLE_PERCENTAGE); + for (var i = 0; i < exampleIndices.length; i++) { + var inputValues = data[exampleIndices[i]].getValues(); + for (var j = 0; j < inputValues.length; j++) { + inputMin = Math.min(inputMin, inputValues[j]); + inputMax = Math.max(inputMax, inputValues[j]); + } + } + return { + inputMin: inputMin, + inputMax: inputMax, + exampleCount: data.length, + shape: data[0].shape, + }; + }; + InMemoryDataset.prototype.normalizeExamplesToRange = function (examples, curLowerBounds, curUpperBounds, newLowerBounds, newUpperBounds) { + var curBoundsIsPerDimension = (curUpperBounds instanceof Float32Array && + curLowerBounds instanceof Float32Array); + var newBoundsIsPerDimension = (newLowerBounds instanceof Float32Array && + newUpperBounds instanceof Float32Array); + var inputSize = util.sizeFromShape(examples[0].shape); + var newExamples = []; + examples.forEach(function (example) { + var inputValues = example.getValues(); + var normalizedValues = new Float32Array(inputSize); + for (var j = 0; j < inputSize; j++) { + var curLowerBound = curBoundsIsPerDimension ? + curLowerBounds[j] : + curLowerBounds; + var curUpperBound = curBoundsIsPerDimension ? + curUpperBounds[j] : + curUpperBounds; + var curRange = curUpperBound - curLowerBound; + var newLowerBound = newBoundsIsPerDimension ? + newLowerBounds[j] : + newLowerBounds; + var newUpperBound = newBoundsIsPerDimension ? + newUpperBounds[j] : + newUpperBounds; + var newRange = newUpperBound - newLowerBound; + if (curRange === 0) { + normalizedValues[j] = newLowerBound; + } + else { + normalizedValues[j] = newLowerBound + + newRange * (inputValues[j] - curLowerBound) / curRange; + } + } + newExamples.push(ndarray_1.NDArray.make(example.shape, { values: normalizedValues })); + }); + return newExamples; + }; + InMemoryDataset.prototype.computeBounds = function (dataIndex) { + var _this = this; + if (this.dataset == null) { + throw new Error('Data is null.'); + } + var size = util.sizeFromShape(this.dataset[dataIndex][0].shape); + this.normalizationInfo[dataIndex] = { + isNormalized: false, + minValues: new Float32Array(size), + maxValues: new Float32Array(size) + }; + for (var i = 0; i < size; i++) { + this.normalizationInfo[dataIndex].minValues[i] = Number.POSITIVE_INFINITY; + this.normalizationInfo[dataIndex].maxValues[i] = Number.NEGATIVE_INFINITY; + } + this.dataset[dataIndex].forEach(function (example) { + var inputValues = example.getValues(); + for (var k = 0; k < size; k++) { + _this.normalizationInfo[dataIndex].minValues[k] = Math.min(_this.normalizationInfo[dataIndex].minValues[k], inputValues[k]); + _this.normalizationInfo[dataIndex].maxValues[k] = Math.max(_this.normalizationInfo[dataIndex].maxValues[k], inputValues[k]); + } + }); + }; + InMemoryDataset.prototype.normalizeWithinBounds = function (dataIndex, lowerBound, upperBound) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + if (dataIndex >= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":25,"./util":89}],10:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":11,"./math/concat3d_util":18,"./math/conv_util":19,"./math/ndarray":25,"./util":89}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":15}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":25,"./session":85}],13:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":10,"./priority_queue":84}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":8,"./dataset":9,"./graph":10,"./graph_runner":12,"./initializers":15,"./input_provider":16,"./math/conv_util":19,"./math/math":22,"./math/math_cpu":23,"./math/math_gpu":24,"./math/ndarray":25,"./math/webgl/gpgpu_context":38,"./math/webgl/gpgpu_util":39,"./math/webgl/render_ndarray_gpu_util":51,"./math/webgl/webgl_util":61,"./optimizer":83,"./session":85,"./sgd_optimizer":87,"./util":89}],15:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":25}],16:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":25,"./util":89}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":25}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":89}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":89}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],21:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":25}],22:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":89,"./concat3d_util":18,"./copy2d_util":20,"./ndarray":25}],23:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":19,"../util":89,"./concat3d_util":18,"./copy2d_util":20,"./math":22,"./ndarray":25}],24:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":89,"./concat3d_util":18,"./conv_util":19,"./math":22,"./ndarray":25,"./webgl/addscaledmat_gpu":26,"./webgl/addsubmuldiv_gpu":27,"./webgl/argmaxequals_gpu":28,"./webgl/argminmax_gpu":29,"./webgl/avg_pool_gpu":30,"./webgl/batchnorm_gpu":31,"./webgl/concat3d_gpu":33,"./webgl/conv_backprop_gpu":34,"./webgl/conv_gpu":35,"./webgl/copy_gpu":36,"./webgl/exp_gpu":37,"./webgl/gpgpu_context":38,"./webgl/gpgpu_util":39,"./webgl/log_gpu":40,"./webgl/logsumexp_gpu":41,"./webgl/max_pool_backprop_gpu":42,"./webgl/max_pool_gpu":43,"./webgl/min_pool_gpu":44,"./webgl/minmax_gpu":45,"./webgl/mulmat_gpu":46,"./webgl/neg_gpu":47,"./webgl/pool_gpu":48,"./webgl/reducesum_gpu":49,"./webgl/relu_gpu":50,"./webgl/reshape_gpu":52,"./webgl/resize_bilinear_gpu":53,"./webgl/shader_compiler":54,"./webgl/sigmoid_gpu":55,"./webgl/step_gpu":56,"./webgl/texture_manager":58,"./webgl/trig_gpu":59,"./webgl/webgl_util":61}],25:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":89,"./webgl/webgl_util":61}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":38}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":22,"./binaryop_gpu":32}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":29}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":61}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":48}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":38}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":19}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":19,"./conv_gpu":35}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":19}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":60}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":39,"./tex_util":57,"./webgl_util":61}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":57,"./webgl_util":61}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":60}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":38}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":19}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":48}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":48}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":61}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":22,"./shader_compiler":54}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":60}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":19,"./webgl_util":61}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":38}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":60}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":61}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":89}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":19}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":89}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":60}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":60}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":60}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":38}],61:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":89}],62:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":10,"./graph_util":13,"./ops/add":63,"./ops/argmax":64,"./ops/argmaxequals":65,"./ops/concat3d":66,"./ops/convolution":67,"./ops/divide":68,"./ops/element_wise_activation":69,"./ops/element_wise_cost":70,"./ops/exp":71,"./ops/linear_combination":72,"./ops/log":73,"./ops/matmul":74,"./ops/max_pool":75,"./ops/multiply":76,"./ops/reduce_sum":78,"./ops/reshape":79,"./ops/softmax":80,"./ops/split":81,"./ops/subtract":82}],63:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":13,"../math/ndarray":25,"../util":89,"./op":77}],64:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":77}],65:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":77}],66:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":18,"./op":77}],67:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":19,"../util":89,"./op":77}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":13,"../util":89,"./op":77}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":17,"./op":77}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":13,"../math/cost_functions":21,"../math/ndarray":25,"../util":89,"./op":77}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":13,"./op":77}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":13,"./op":77}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":13,"./op":77}],74:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":13,"../math/math":22,"./op":77}],75:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":19,"../util":89,"./op":77}],76:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":13,"../util":89,"./op":77}],77:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],78:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":13,"../math/ndarray":25,"../util":89,"./op":77}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":89,"./op":77}],80:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":10,"../math/ndarray":25,"../util":89,"./op":77}],81:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":13,"../util":89,"./op":77}],82:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":13,"../math/ndarray":25,"../util":89,"./op":77}],83:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],84:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],85:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":25,"./operation_emitter":62,"./session_util":86,"./tensor_array_map":88,"./util":89}],86:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":10,"./graph_util":13,"./math/ndarray":25,"./util":89}],87:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":25,"./optimizer":83,"./session_util":86,"./tensor_array_map":88}],88:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],89:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[5]) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJkZW1vcy9kZW1vLWZvb3Rlci50cyIsImRlbW9zL2RlbW8taGVhZGVyLnRzIiwiZGVtb3MvbGVhcm5qcy50cyIsImRlbW9zL25uLWFydC9jcHBuLnRzIiwiZGVtb3Mvbm4tYXJ0L25uLWFydC50cyIsImRlbW9zL25uLWFydC9ubl9hcnRfdXRpbC50cyIsImRlbW9zL3BvbHltZXItc3BlYy50cyIsInNyYy9jaGVja3BvaW50X2xvYWRlci50cyIsInNyYy9kYXRhc2V0LnRzIiwic3JjL2dyYXBoLnRzIiwic3JjL2dyYXBoX2xheWVycy50cyIsInNyYy9ncmFwaF9ydW5uZXIudHMiLCJzcmMvZ3JhcGhfdXRpbC50cyIsInNyYy9pbmRleC50cyIsInNyYy9pbml0aWFsaXplcnMudHMiLCJzcmMvaW5wdXRfcHJvdmlkZXIudHMiLCJzcmMvbWF0aC9hY3RpdmF0aW9uX2Z1bmN0aW9ucy50cyIsInNyYy9tYXRoL2NvbmNhdDNkX3V0aWwudHMiLCJzcmMvbWF0aC9jb252X3V0aWwudHMiLCJzcmMvbWF0aC9jb3B5MmRfdXRpbC50cyIsInNyYy9tYXRoL2Nvc3RfZnVuY3Rpb25zLnRzIiwic3JjL21hdGgvbWF0aC50cyIsInNyYy9tYXRoL21hdGhfY3B1LnRzIiwic3JjL21hdGgvbWF0aF9ncHUudHMiLCJzcmMvbWF0aC9uZGFycmF5LnRzIiwic3JjL21hdGgvd2ViZ2wvYWRkc2NhbGVkbWF0X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9hcmdtYXhlcXVhbHNfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvYXJnbWlubWF4X2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2F2Z19wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2JhdGNobm9ybV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9iaW5hcnlvcF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb25jYXQzZF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9jb252X2JhY2twcm9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL2NvbnZfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvY29weV9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9leHBfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvZ3BncHVfY29udGV4dC50cyIsInNyYy9tYXRoL3dlYmdsL2dwZ3B1X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC9sb2dfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbG9nc3VtZXhwX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21heF9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21pbl9wb29sX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL21pbm1heF9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9tdWxtYXRfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvbmVnX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3Bvb2xfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVkdWNlc3VtX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3JlbHVfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC9yZXNoYXBlX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3Jlc2l6ZV9iaWxpbmVhcl9ncHUudHMiLCJzcmMvbWF0aC93ZWJnbC9zaGFkZXJfY29tcGlsZXIudHMiLCJzcmMvbWF0aC93ZWJnbC9zaWdtb2lkX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3N0ZXBfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvdGV4X3V0aWwudHMiLCJzcmMvbWF0aC93ZWJnbC90ZXh0dXJlX21hbmFnZXIudHMiLCJzcmMvbWF0aC93ZWJnbC90cmlnX2dwdS50cyIsInNyYy9tYXRoL3dlYmdsL3VuYXJ5b3BfZ3B1LnRzIiwic3JjL21hdGgvd2ViZ2wvd2ViZ2xfdXRpbC50cyIsInNyYy9vcGVyYXRpb25fZW1pdHRlci50cyIsInNyYy9vcHMvYWRkLnRzIiwic3JjL29wcy9hcmdtYXgudHMiLCJzcmMvb3BzL2FyZ21heGVxdWFscy50cyIsInNyYy9vcHMvY29uY2F0M2QudHMiLCJzcmMvb3BzL2NvbnZvbHV0aW9uLnRzIiwic3JjL29wcy9kaXZpZGUudHMiLCJzcmMvb3BzL2VsZW1lbnRfd2lzZV9hY3RpdmF0aW9uLnRzIiwic3JjL29wcy9lbGVtZW50X3dpc2VfY29zdC50cyIsInNyYy9vcHMvZXhwLnRzIiwic3JjL29wcy9saW5lYXJfY29tYmluYXRpb24udHMiLCJzcmMvb3BzL2xvZy50cyIsInNyYy9vcHMvbWF0bXVsLnRzIiwic3JjL29wcy9tYXhfcG9vbC50cyIsInNyYy9vcHMvbXVsdGlwbHkudHMiLCJzcmMvb3BzL29wLnRzIiwic3JjL29wcy9yZWR1Y2Vfc3VtLnRzIiwic3JjL29wcy9yZXNoYXBlLnRzIiwic3JjL29wcy9zb2Z0bWF4LnRzIiwic3JjL29wcy9zcGxpdC50cyIsInNyYy9vcHMvc3VidHJhY3QudHMiLCJzcmMvb3B0aW1pemVyLnRzIiwic3JjL3ByaW9yaXR5X3F1ZXVlLnRzIiwic3JjL3Nlc3Npb24udHMiLCJzcmMvc2Vzc2lvbl91dGlsLnRzIiwic3JjL3NnZF9vcHRpbWl6ZXIudHMiLCJzcmMvdGVuc29yX2FycmF5X21hcC50cyIsInNyYy91dGlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDY0EsT0FBTyxDQUFDLEVBQUMsRUFBRSxFQUFFLGFBQWEsRUFBQyxDQUFDLENBQUM7OztBQ0E3QixPQUFPLENBQUMsRUFBQyxFQUFFLEVBQUUsYUFBYSxFQUFDLENBQUMsQ0FBQzs7Ozs7Ozs7QUNHN0Isa0NBQTZCOzs7OztBQ0Y3QixzQ0FBMEc7QUFFMUcsMkNBQTZDO0FBRTdDLElBQU0sVUFBVSxHQUFHLEVBQUUsQ0FBQztBQUd0QixJQUFNLHlCQUF5QixHQUF1QztJQUNwRSxL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Q0FBQztRQUN6QyxDQUFDO1FBQ0QsTUFBTSxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBQyxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQztJQUN2RCxDQUFDO0lBRVMsd0RBQStCLEdBQXpDLFVBQTBDLENBQVUsRUFBRSxDQUFVO1FBQzlELElBQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDaEQsSUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVoRCxJQUFNLE1BQU0sR0FBRyxJQUFJLFlBQVksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLENBQUM7UUFDakQsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsR0FBRyxDQUFDLENBQUMsSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztZQUN0QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLE1BQU0sRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO2dCQUN0QyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUN2RCxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFD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QUcsVUFBVSxDQUFDLENBQUMsQ0FBQztnQkFDNUQsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLEdBQUcsVUFBVSxDQUFDLENBQUM7Z0JBRS9ELEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO29CQUN2QyxJQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsR0FBRyxDQUFDO29CQUMxQixJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO29CQUM1RCxJQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxDQUFDLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxVQUFVLENBQUMsQ0FBQztvQkFFL0QsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO29CQUNoQixHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUN0QyxJQUFNLEVBQUUsR0FBRyxFQUFFLEdBQUcsVUFBVSxHQUFHLFFBQVEsQ0FBQzt3QkFFdEMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQzs0QkFDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLFVBQVUsR0FBRyxRQUFRLENBQUM7NEJBRXRDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsZUFBZSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7Z0NBQzVDLElBQU0sS0FBSyxHQUFHLENBQUMsQ0FB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QU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEdBQUcsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDOUQsSUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEdBQUcsT0FBTyxHQUFHLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDO2dCQUVyRSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFVBQVUsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO29CQUN2QyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRSxHQUFHLFdBQVcsRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO3dCQUV4QyxJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7d0JBQ2hCLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUM7NEJBQ3RDLElBQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsTUFBTSxHQUFHLE9BQU8sQ0FBQzs0QkFDdEMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztnQ0FDdEMsSUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxNQUFNLEdBQUcsT0FBTyxDQUFDO2dDQUN0QyxPQUFPLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQzs0QkFDcEQsQ0FBQzt3QkFDSCxDQUFDO3dCQUNELEVBQUUsQ0FBQyxHQUFHLENBQUMs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LENBQUM7WUFDekMsS0FBSyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFDRCxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxDQUFDO0lBQ2xDLENBQUM7SUFFRCw0QkFBVSxHQUFWLFVBQVcsSUFBYztRQUN2QixJQUFJLEtBQUssR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNsQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7WUFDekMsS0FBSyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFDRCxNQUFNLENBQUMsS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELDRCQUFVLEdBQVYsVUFBVyxLQUFhO1FBQ3RCLElBQU0sSUFBSSxHQUFhLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDcEQsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1lBQ3pDLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDOUMsS0FBSyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0s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S0FBYSxFQUFFLENBQVMsRUFBRSxDQUFTLEVBQUUsQ0FBUyxFQUFFLENBQVM7UUFDM0QsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUNYLElBQUksQ0FBQyxPQUFPLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEtBQUssQ0FBQztJQUMzRSxDQUFDO0lBRUQscUJBQUcsR0FBSCxVQUFJLEtBQWEsRUFBRSxDQUFTLEVBQUUsQ0FBUyxFQUFFLENBQVMsRUFBRSxDQUFTO1FBQzNELElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FDWCxJQUFJLENBQUMsT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUM7SUFDNUUsQ0FBQztJQUVELDRCQUFVLEdBQVYsVUFBVyxJQUFzQztRQUMvQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ2xELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQsNEJBQVUsR0FBVixVQUFXLEtBQWE7UUFDdEIsSUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNDLEtBQUssSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUMxQixJQUFNLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDM0MsS0FBSyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMs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Q0FBQyxDQUFDO1FBQzVCLE1BQU0sQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUMvQixDQUFDO0FBQ0gsQ0FBQztBQWRELG9CQWNDO0FBRUQsNkJBQW9DLElBQVk7SUFDOUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ3JELEVBQUUsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNuQixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ25CLENBQUM7QUFQRCxrREFPQztBQUVELCtCQUFzQyxDQUFTO0lBQzdDLElBQU0sZUFBZSxHQUFHLElBQUksV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDM0IsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QixDQUFDO0lBQ0QsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ3pCLE1BQU0sQ0FBQyxlQUFlLENBQUM7QUFDekIsQ0FBQztBQVBELHNEQU9DIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblBvbHltZXIoe2lzOiAnZGVtby1mb290ZXInfSk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5Qb2x5bWVyKHtpczogJ2RlbW8taGVhZGVyJ30pO1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vLyBUaGlzIGZpbGUgaXMganVzdCBhbiBhbGlhcyB0aGF0IHBvaW50cyB0byB0aGUgY3VycmVudCBsZWFybmpzIHZlcnNpb25cbi8vIGF0IHRoaXMgYnJhbmNoLCBzbyBkZW1vcyBjYW4gaW1wb3J0IHRoZSBsaWJyYXJ5IGFzICcuLi9sZWFybmpzJy5cbmV4cG9ydCAqIGZyb20gJy4uL3NyYy9pbmRleCc7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgZ3BncHVfdXRpbCwgR1BHUFVDb250ZXh0LCBOREFycmF5TWF0aEdQVSwgU2NhbGFyLCB3ZWJnbF91dGlsfSBmcm9tICcuLi9sZWFybmpzJztcblxuaW1wb3J0ICogYXMgbm5fYXJ0X3V0aWwgZnJvbSAnLi9ubl9hcnRfdXRpbCc7XG5cbmNvbnN0IE1BWF9MQVlFUlMgPSAxMDtcblxuZXhwb3J0IHR5cGUgQ29sb3JNb2RlID0gJ3JnYid8J3JnYmEnfCdoc3YnfCdoc3ZhJ3wneXV2J3wneXV2YSd8J2J3JztcbmNvbnN0IGNvbG9yTW9kZU91dHB1dERpbWVuc2lvbnM6IHtbY29sb3JNb2RlIGluIENvbG9yTW9kZV06IG51bWJlcn0gPSB7XG4gICdyZ2InOiAzLFxuICAncmdiYSc6IDQsXG4gICdoc3YnOiAzLFxuICAnaHN2YSc6IDQsXG4gICd5dXYnOiAzLFxuICAneXV2YSc6IDQsXG4gICdidyc6IDFcbn07XG5cbmV4cG9ydCB0eXBlIEFjdGl2YXRpb25GdW5jdGlvbiA9ICd0YW5oJ3wnc2luJ3wncmVsdSd8J3N0ZXAnO1xuY29uc3QgYWN0aXZhdGlvbkZ1bmN0aW9uTWFwOiB7XG4gIFthY3RpdmF0aW9uRnVuY3Rpb24gaW4gQWN0aXZhdGlvbkZ1bmN0aW9uXTpcbiAgICAgIChtYXRoOiBOREFycmF5TWF0aEdQVSwgbmRhcnJheTogQXJyYXkyRCkgPT4gQXJyYXkyRFxufSA9IHtcbiAgJ3RhbmgnOiAobWF0aDogTkRBcnJheU1hdGhHUFUsIG5kYXJyYXk6IEFycmF5MkQpID0+IG1hdGgudGFuaChuZGFycmF5KSxcbiAgJ3Npbic6IChtYXRoOiBOREFycmF5TWF0aEdQVSwgbmRhcnJheTogQXJyYXkyRCkgPT4gbWF0aC5zaW4obmRhcnJheSksXG4gICdyZWx1JzogKG1hdGg6IE5EQXJyYXlNYXRoR1BVLCBuZGFycmF5OiBBcnJheTJEKSA9PiBtYXRoLnJlbHUobmRhcnJheSksXG4gICdzdGVwJzogKG1hdGg6IE5EQXJyYXlNYXRoR1BVLCBuZGFycmF5OiBBcnJheTJEKSA9PiBtYXRoLnN0ZXAobmRhcnJheSlcbn07XG5cbmNvbnN0IE5VTV9JTUFHRV9TUEFDRV9WQVJJQUJMRVMgPSAzOyAgLy8geCwgeSwgclxuY29uc3QgTlVNX0xBVEVOVF9WQVJJQUJMRVMgPSAyO1xuXG5leHBvcnQgY2xhc3MgQ1BQTiB7XG4gIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGhHUFU7XG4gIHByaXZhdGUgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgcHJpdmF0ZSBncGdwdTogR1BHUFVDb250ZXh0O1xuICBwcml2YXRlIHJlbmRlclNoYWRlcjogV2ViR0xQcm9ncmFtO1xuICBwcml2YXRlIGFkZExhdGVudFZhcmlhYmxlc1NoYWRlcjogV2ViR0xQcm9ncmFtO1xuXG4gIHByaXZhdGUgaW5wdXRBdGxhczogQXJyYXkyRDtcbiAgcHJpdmF0ZSB3ZWlnaHRzOiBBcnJheTJEW10gPSBbXTtcblxuICBwcml2YXRlIHoxQ291bnRlciA9IDA7XG4gIHByaXZhdGUgejJDb3VudGVyID0gMDtcbiAgcHJpdmF0ZSB6MVNjYWxlOiBudW1iZXI7XG4gIHByaXZhdGUgejJTY2FsZTogbnVtYmVyO1xuICBwcml2YXRlIG51bUxheWVyczogbnVtYmVyO1xuXG4gIHByaXZhdGUgY29sb3JNb2RlTmFtZXM6IENvbG9yTW9kZVtdID1cbiAgICAgIFsncmdiJywgJ3JnYmEnLCAnaHN2JywgJ2hzdmEnLCAneXV2JywgJ3l1dmEnLCAnYncnXTtcbiAgcHJpdmF0ZSBhY3RpdmF0aW9uRnVuY3Rpb25OYW1lczogQWN0aXZhdGlvbkZ1bmN0aW9uW10gPVxuICAgICAgWyd0YW5oJywgJ3NpbicsICdyZWx1JywgJ3N0ZXAnXTtcblxuICBwcml2YXRlIHNlbGVjdGVkQ29sb3JNb2RlTmFtZTogQ29sb3JNb2RlO1xuICBwcml2YXRlIHNlbGVjdGVkQWN0aXZhdGlvbkZ1bmN0aW9uTmFtZTogQWN0aXZhdGlvbkZ1bmN0aW9uO1xuXG4gIHByaXZhdGUgaXNJbmZlcnJpbmcgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGluZmVyZW5jZUNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQpIHtcbiAgICB0aGlzLmdsID0gZ3BncHVfdXRpbC5jcmVhdGVXZWJHTENvbnRleHQodGhpcy5pbmZlcmVuY2VDYW52YXMpO1xuICAgIHRoaXMuZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KHRoaXMuZ2wpO1xuICAgIHRoaXMubWF0aCA9IG5ldyBOREFycmF5TWF0aEdQVSh0aGlzLmdwZ3B1KTtcblxuICAgIGNvbnN0IG1heFRleHR1cmVTaXplID0gd2ViZ2xfdXRpbC5xdWVyeU1heFRleHR1cmVTaXplKHRoaXMuZ2wpO1xuICAgIGNvbnN0IGNhbnZhc1NpemUgPSBNYXRoLmZsb29yKE1hdGguc3FydChtYXhUZXh0dXJlU2l6ZSkpO1xuICAgIHRoaXMuaW5mZXJlbmNlQ2FudmFzLndpZHRoID0gY2FudmFzU2l6ZTtcbiAgICB0aGlzLmluZmVyZW5jZUNhbnZhcy5oZWlnaHQgPSBjYW52YXNTaXplO1xuXG4gICAgdGhpcy5yZW5kZXJTaGFkZXIgPSBubl9hcnRfdXRpbC5nZXRSZW5kZXJTaGFkZXIodGhpcy5ncGdwdSwgY2FudmFzU2l6ZSk7XG4gICAgdGhpcy5hZGRMYXRlbnRWYXJpYWJsZXNTaGFkZXIgPSBubl9hcnRfdXRpbC5nZXRBZGRMYXRlbnRWYXJpYWJsZXNTaGFkZXIoXG4gICAgICAgIHRoaXMuZ3BncHUsIE5VTV9JTUFHRV9TUEFDRV9WQVJJQUJMRVMpO1xuICAgIHRoaXMuaW5wdXRBdGxhcyA9IG5uX2FydF91dGlsLmNyZWF0ZUlucHV0QXRsYXMoXG4gICAgICAgIGNhbnZhc1NpemUsIE5VTV9JTUFHRV9TUEFDRV9WQVJJQUJMRVMsIE5VTV9MQVRFTlRfVkFSSUFCTEVTKTtcbiAgfVxuXG4gIGdlbmVyYXRlV2VpZ2h0cyhuZXVyb25zUGVyTGF5ZXI6IG51bWJlciwgd2VpZ2h0c1N0ZGV2OiBudW1iZXIpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMud2VpZ2h0cy5sZW5ndGg7IGkrKykge1xuICAgICAgdGhpcy53ZWlnaHRzW2ldLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgdGhpcy53ZWlnaHRzID0gW107XG5cbiAgICB0aGlzLndlaWdodHMucHVzaChBcnJheTJELnJhbmRUcnVuY2F0ZWROb3JtYWw8QXJyYXkyRD4oXG4gICAgICAgIFtuZXVyb25zUGVyTGF5ZXIsIE5VTV9JTUFHRV9TUEFDRV9WQVJJQUJMRVMgKyBOVU1fTEFURU5UX1ZBUklBQkxFU10sIDAsXG4gICAgICAgIHdlaWdodHNTdGRldikpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTUFYX0xBWUVSUzsgaSsrKSB7XG4gICAgICB0aGlzLndlaWdodHMucHVzaChBcnJheTJELnJhbmRUcnVuY2F0ZWROb3JtYWw8QXJyYXkyRD4oXG4gICAgICAgICAgW25ldXJvbnNQZXJMYXllciwgbmV1cm9uc1BlckxheWVyXSwgMCwgd2VpZ2h0c1N0ZGV2KSk7XG4gICAgfVxuICAgIHRoaXMud2VpZ2h0cy5wdXNoKEFycmF5MkQucmFuZFRydW5jYXRlZE5vcm1hbDxBcnJheTJEPihcbiAgICAgICAgWzQgLyoqIG1heCBvdXRwdXQgY2hhbm5lbHMgKi8sIG5ldXJvbnNQZXJMYXllcl0sIDAsIHdlaWdodHNTdGRldikpO1xuICB9XG5cbiAgc2V0Q29sb3JNb2RlKGNvbG9yTW9kZTogQ29sb3JNb2RlKSB7XG4gICAgdGhpcy5zZWxlY3RlZENvbG9yTW9kZU5hbWUgPSBjb2xvck1vZGU7XG4gIH1cblxuICBzZXRBY3RpdmF0aW9uRnVuY3Rpb24oYWN0aXZhdGlvbkZ1bmN0aW9uOiBBY3RpdmF0aW9uRnVuY3Rpb24pIHtcbiAgICB0aGlzLnNlbGVjdGVkQWN0aXZhdGlvbkZ1bmN0aW9uTmFtZSA9IGFjdGl2YXRpb25GdW5jdGlvbjtcbiAgfVxuXG4gIHNldE51bUxheWVycyhudW1MYXllcnM6IG51bWJlcikge1xuICAgIHRoaXMubnVtTGF5ZXJzID0gbnVtTGF5ZXJzO1xuICB9XG5cbiAgc2V0WjFTY2FsZSh6MVNjYWxlOiBudW1iZXIpIHtcbiAgICB0aGlzLnoxU2NhbGUgPSB6MVNjYWxlO1xuICB9XG5cbiAgc2V0WjJTY2FsZSh6MlNjYWxlOiBudW1iZXIpIHtcbiAgICB0aGlzLnoyU2NhbGUgPSB6MlNjYWxlO1xuICB9XG5cbiAgc3RhcnQoKSB7XG4gICAgdGhpcy5pc0luZmVycmluZyA9IHRydWU7XG4gICAgdGhpcy5ydW5JbmZlcmVuY2VMb29wKCk7XG4gIH1cblxuICBwcml2YXRlIHJ1bkluZmVyZW5jZUxvb3AoKSB7XG4gICAgaWYgKCF0aGlzLmlzSW5mZXJyaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgY29sb3JNb2RlSW5kZXggPVxuICAgICAgICB0aGlzLmNvbG9yTW9kZU5hbWVzLmluZGV4T2YodGhpcy5zZWxlY3RlZENvbG9yTW9kZU5hbWUpO1xuICAgIGNvbnN0IG91dHB1dERpbWVuc2lvbnMgPVxuICAgICAgICBjb2xvck1vZGVPdXRwdXREaW1lbnNpb25zW3RoaXMuc2VsZWN0ZWRDb2xvck1vZGVOYW1lXTtcblxuICAgIHRoaXMuejFDb3VudGVyICs9IDEgLyB0aGlzLnoxU2NhbGU7XG4gICAgdGhpcy56MkNvdW50ZXIgKz0gMSAvIHRoaXMuejJTY2FsZTtcbiAgICBjb25zdCB6MSA9IE1hdGguc2luKHRoaXMuejFDb3VudGVyKTtcbiAgICBjb25zdCB6MiA9IE1hdGguY29zKHRoaXMuejJDb3VudGVyKTtcblxuICAgIGNvbnN0IGludGVybWVkaWF0ZVJlc3VsdHMgPSBbXTtcblxuICAgIC8vIEFkZCB0aGUgbGF0ZW50IHZhcmlhYmxlcy5cbiAgICBjb25zdCBhZGRMYXRlbnRWYXJpYWJsZXNSZXN1bHRUZXggPVxuICAgICAgICB0aGlzLm1hdGguZ2V0VGV4dHVyZU1hbmFnZXIoKS5hY3F1aXJlVGV4dHVyZSh0aGlzLmlucHV0QXRsYXMuc2hhcGUpO1xuICAgIG5uX2FydF91dGlsLmFkZExhdGVudFZhcmlhYmxlcyhcbiAgICAgICAgdGhpcy5ncGdwdSwgdGhpcy5hZGRMYXRlbnRWYXJpYWJsZXNTaGFkZXIsIHRoaXMuaW5wdXRBdGxhcy5nZXRUZXh0dXJlKCksXG4gICAgICAgIGFkZExhdGVudFZhcmlhYmxlc1Jlc3VsdFRleCwgdGhpcy5pbnB1dEF0bGFzLnNoYXBlLCB6MSwgejIpO1xuICAgIGNvbnN0IGlucHV0QXRsYXNXaXRoTGF0ZW50VmFyaWFibGVzID1cbiAgICAgICAgQXJyYXkyRC5tYWtlPEFycmF5MkQ+KHRoaXMuaW5wdXRBdGxhcy5zaGFwZSwge1xuICAgICAgICAgIHRleHR1cmU6IGFkZExhdGVudFZhcmlhYmxlc1Jlc3VsdFRleCxcbiAgICAgICAgICB0ZXh0dXJlU2hhcGVSQzogdGhpcy5pbnB1dEF0bGFzLnNoYXBlXG4gICAgICAgIH0pO1xuICAgIGludGVybWVkaWF0ZVJlc3VsdHMucHVzaChpbnB1dEF0bGFzV2l0aExhdGVudFZhcmlhYmxlcyk7XG5cbiAgICBsZXQgbGFzdE91dHB1dCA9IGlucHV0QXRsYXNXaXRoTGF0ZW50VmFyaWFibGVzO1xuXG4gICAgdGhpcy5tYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1MYXllcnM7IGkrKykge1xuICAgICAgICBjb25zdCBtYXRtdWxSZXN1bHQgPSB0aGlzLm1hdGgubWF0TXVsKHRoaXMud2VpZ2h0c1tpXSwgbGFzdE91dHB1dCk7XG5cbiAgICAgICAgbGFzdE91dHB1dCA9IChpID09PSB0aGlzLm51bUxheWVycyAtIDEpID9cbiAgICAgICAgICAgIHRoaXMubWF0aC5zaWdtb2lkKG1hdG11bFJlc3VsdCkgOlxuICAgICAgICAgICAgYWN0aXZhdGlvbkZ1bmN0aW9uTWFwW3RoaXMuc2VsZWN0ZWRBY3RpdmF0aW9uRnVuY3Rpb25OYW1lXShcbiAgICAgICAgICAgICAgICB0aGlzLm1hdGgsIG1hdG11bFJlc3VsdCk7XG4gICAgICB9XG4gICAgICBubl9hcnRfdXRpbC5yZW5kZXIoXG4gICAgICAgICAgdGhpcy5ncGdwdSwgdGhpcy5yZW5kZXJTaGFkZXIsIGxhc3RPdXRwdXQuZ2V0VGV4dHVyZSgpLFxuICAgICAgICAgIG91dHB1dERpbWVuc2lvbnMsIGNvbG9yTW9kZUluZGV4KTtcbiAgICB9KTtcblxuICAgIGlucHV0QXRsYXNXaXRoTGF0ZW50VmFyaWFibGVzLmRpc3Bvc2UoKTtcblxuICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB0aGlzLnJ1bkluZmVyZW5jZUxvb3AoKSk7XG4gIH1cblxuICBzdG9wSW5mZXJlbmNlTG9vcCgpIHtcbiAgICB0aGlzLmlzSW5mZXJyaW5nID0gZmFsc2U7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cbmltcG9ydCAnLi4vZGVtby1oZWFkZXInO1xuaW1wb3J0ICcuLi9kZW1vLWZvb3Rlcic7XG5cbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgZ3BncHVfdXRpbCwgR1BHUFVDb250ZXh0LCBOREFycmF5TWF0aEdQVSwgU2NhbGFyLCB3ZWJnbF91dGlsfSBmcm9tICcuLi9sZWFybmpzJztcbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby11bnVzZWQtdmFyaWFibGVcbmltcG9ydCB7UG9seW1lckVsZW1lbnQsIFBvbHltZXJIVE1MRWxlbWVudH0gZnJvbSAnLi4vcG9seW1lci1zcGVjJztcblxuaW1wb3J0IHtBY3RpdmF0aW9uRnVuY3Rpb24sIENvbG9yTW9kZSwgQ1BQTn0gZnJvbSAnLi9jcHBuJztcbmltcG9ydCAqIGFzIG5uX2FydF91dGlsIGZyb20gJy4vbm5fYXJ0X3V0aWwnO1xuXG5jb25zdCBDQU5WQVNfVVBTQ0FMRV9GQUNUT1IgPSAzO1xuXG5jb25zdCBOVU1fSU1BR0VfU1BBQ0VfVkFSSUFCTEVTID0gMzsgIC8vIHgsIHksIHJcbmNvbnN0IE5VTV9MQVRFTlRfVkFSSUFCTEVTID0gMjtcblxuY29uc3QgTUFYX05VTV9MQVlFUlMgPSAxNTtcbmNvbnN0IE1BVF9XSURUSCA9IDMwO1xuLy8gU3RhbmRhcmQgZGV2aWF0aW9ucyBmb3IgZ2F1c3NpYW4gd2VpZ2h0IGluaXRpYWxpemF0aW9uLlxuY29uc3QgV0VJR0hUU19TVERFViA9IC42O1xuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6dmFyaWFibGUtbmFtZVxuZXhwb3J0IGNvbnN0IE5OQXJ0UG9seW1lciA9IFBvbHltZXJFbGVtZW50KHtpczogJ25uLWFydCcsIHByb3BlcnRpZXM6IHt9fSk7XG5cbmV4cG9ydCBjbGFzcyBOTkFydCBleHRlbmRzIE5OQXJ0UG9seW1lciB7XG4gIHByaXZhdGUgY3BwbjogQ1BQTjtcblxuICBwcml2YXRlIGluZmVyZW5jZUNhbnZhczogSFRNTENhbnZhc0VsZW1lbnQ7XG5cbiAgcHJpdmF0ZSB6MVNjYWxlOiBudW1iZXI7XG4gIHByaXZhdGUgejJTY2FsZTogbnVtYmVyO1xuICBwcml2YXRlIG51bUxheWVyczogbnVtYmVyO1xuXG4gIHJlYWR5KCkge1xuICAgIHRoaXMuaW5mZXJlbmNlQ2FudmFzID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjaW5mZXJlbmNlJykgYXMgSFRNTENhbnZhc0VsZW1lbnQ7XG5cbiAgICB0aGlzLmNwcG4gPSBuZXcgQ1BQTih0aGlzLmluZmVyZW5jZUNhbnZhcyk7XG5cbiAgICB0aGlzLmluZmVyZW5jZUNhbnZhcy5zdHlsZS53aWR0aCA9XG4gICAgICAgIHRoaXMuaW5mZXJlbmNlQ2FudmFzLndpZHRoICogQ0FOVkFTX1VQU0NBTEVfRkFDVE9SICsgJ3B4JztcbiAgICB0aGlzLmluZmVyZW5jZUNhbnZhcy5zdHlsZS5oZWlnaHQgPVxuICAgICAgICB0aGlzLmluZmVyZW5jZUNhbnZhcy5oZWlnaHQgKiBDQU5WQVNfVVBTQ0FMRV9GQUNUT1IgKyAncHgnO1xuXG4gICAgY29uc3QgY3VycmVudENvbG9yRWxlbWVudCA9XG4gICAgICAgIHRoaXMucXVlcnlTZWxlY3RvcignI2NvbG9ybW9kZScpIGFzIEhUTUxJbnB1dEVsZW1lbnQ7XG4gICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjY29sb3Itc2VsZWN0b3InKSEuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICAnY2xpY2snLCAoZXZlbnQ6IGFueSkgPT4ge1xuICAgICAgICAgIGNvbnN0IGNvbG9yTW9kZSA9XG4gICAgICAgICAgICAgIChldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQpLmdldEF0dHJpYnV0ZSgnZGF0YS12YWwnKSBhc1xuICAgICAgICAgICAgICBDb2xvck1vZGU7XG4gICAgICAgICAgY3VycmVudENvbG9yRWxlbWVudC52YWx1ZSA9IGNvbG9yTW9kZTtcbiAgICAgICAgICB0aGlzLmNwcG4uc2V0Q29sb3JNb2RlKGNvbG9yTW9kZSk7XG4gICAgICAgIH0pO1xuICAgIHRoaXMuY3Bwbi5zZXRDb2xvck1vZGUoJ3JnYicpO1xuXG4gICAgY29uc3QgY3VycmVudEFjdGl2YXRpb25GbkVsZW1lbnQgPVxuICAgICAgICB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNhY3RpdmF0aW9uLWZuJykgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICB0aGlzLnF1ZXJ5U2VsZWN0b3IoJyNhY3RpdmF0aW9uLXNlbGVjdG9yJykhLmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgJ2NsaWNrJywgKGV2ZW50OiBhbnkpID0+IHtcbiAgICAgICAgICBjb25zdCBhY3RpdmF0aW9uRm4gPVxuICAgICAgICAgICAgICAoZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50KS5nZXRBdHRyaWJ1dGUoJ2RhdGEtdmFsJykgYXNcbiAgICAgICAgICAgICAgQWN0aXZhdGlvbkZ1bmN0aW9uO1xuICAgICAgICAgIGN1cnJlbnRBY3RpdmF0aW9uRm5FbGVtZW50LnZhbHVlID0gYWN0aXZhdGlvbkZuO1xuICAgICAgICAgIHRoaXMuY3Bwbi5zZXRBY3RpdmF0aW9uRnVuY3Rpb24oYWN0aXZhdGlvbkZuKTtcbiAgICAgICAgfSk7XG4gICAgdGhpcy5jcHBuLnNldEFjdGl2YXRpb25GdW5jdGlvbigndGFuaCcpO1xuXG4gICAgY29uc3QgbGF5ZXJzU2xpZGVyID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjbGF5ZXJzLXNsaWRlcicpIGFzIEhUTUxJbnB1dEVsZW1lbnQ7XG4gICAgY29uc3QgbGF5ZXJzQ291bnRFbGVtZW50ID1cbiAgICAgICAgdGhpcy5xdWVyeVNlbGVjdG9yKCcjbGF5ZXJzLWNvdW50JykgYXMgSFRNTERpdkVsZW1lbnQ7XG4gICAgbGF5ZXJzU2xpZGVyIS5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIChldmVudCkgPT4ge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgdGhpcy5udW1MYXllcnMgPSAoZXZlbnQgYXMgYW55KS50YXJnZXQudmFsdWU7XG4gICAgICBsYXllcnNDb3VudEVsZW1lbnQuaW5uZXJUZXh0ID0gJycgKyB0aGlzLm51bUxheWVycztcbiAgICAgIHRoaXMuY3Bwbi5zZXROdW1MYXllcnModGhpcy5udW1MYXllcnMpO1xuICAgIH0pO1xuICAgIHRoaXMubnVtTGF5ZXJzID0gK2xheWVyc1NsaWRlci52YWx1ZTtcbiAgICBsYXllcnNDb3VudEVsZW1lbnQuaW5uZXJUZXh0ID0gJycgKyB0aGlzLm51bUxheWVycztcbiAgICB0aGlzLmNwcG4uc2V0TnVtTGF5ZXJzKHRoaXMubnVtTGF5ZXJzKTtcblxuICAgIGNvbnN0IHoxU2xpZGVyID0gdGhpcy5xdWVyeVNlbGVjdG9yKCcjejEtc2xpZGVyJykgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICB6MVNsaWRlci5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIChldmVudCkgPT4ge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgdGhpcy56MVNjYWxlID0gKGV2ZW50IGFzIGFueSkudGFyZ2V0LnZhbHVlO1xuICAgICAgdGhpcy5jcHBuLnNldFoxU2NhbGUodGhpcy56MVNjYWxlKTtcbiAgICB9KTtcbiAgICB0aGlzLnoxU2NhbGUgPSArejFTbGlkZXIudmFsdWU7XG4gICAgdGhpcy5jcHBuLnNldFoxU2NhbGUodGhpcy56MVNjYWxlKTtcblxuICAgIGNvbnN0IHoyU2xpZGVyID0gdGhpcy5xdWVyeVNlbGVjdG9yKCcjejItc2xpZGVyJykgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICB6MlNsaWRlci5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIChldmVudCkgPT4ge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgdGhpcy56MlNjYWxlID0gKGV2ZW50IGFzIGFueSkudGFyZ2V0LnZhbHVlO1xuICAgICAgdGhpcy5jcHBuLnNldFoyU2NhbGUodGhpcy56MVNjYWxlKTtcbiAgICB9KTtcbiAgICB0aGlzLnoyU2NhbGUgPSArejJTbGlkZXIudmFsdWU7XG4gICAgdGhpcy5jcHBuLnNldFoyU2NhbGUodGhpcy56MlNjYWxlKTtcblxuICAgIGNvbnN0IHJhbmRvbWl6ZUJ1dHRvbiA9IHRoaXMucXVlcnlTZWxlY3RvcignI3JhbmRvbScpIGFzIEhUTUxCdXR0b25FbGVtZW50O1xuICAgIHJhbmRvbWl6ZUJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcbiAgICAgIHRoaXMuY3Bwbi5nZW5lcmF0ZVdlaWdodHMoTUFUX1dJRFRILCBXRUlHSFRTX1NUREVWKTtcbiAgICB9KTtcblxuICAgIHRoaXMuY3Bwbi5nZW5lcmF0ZVdlaWdodHMoTUFUX1dJRFRILCBXRUlHSFRTX1NUREVWKTtcbiAgICB0aGlzLmNwcG4uc3RhcnQoKTtcbiAgfVxufVxuXG5kb2N1bWVudC5yZWdpc3RlckVsZW1lbnQoTk5BcnQucHJvdG90eXBlLmlzLCBOTkFydCk7XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7QXJyYXkyRCwgR1BHUFVDb250ZXh0LCB3ZWJnbF91dGlsfSBmcm9tICcuLi9sZWFybmpzJztcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUlucHV0QXRsYXMoXG4gICAgaW1hZ2VTaXplOiBudW1iZXIsIGlucHV0TnVtRGltZW5zaW9uczogbnVtYmVyLCBudW1MYXRlbnRWYXJpYWJsZXM6IG51bWJlcikge1xuICBjb25zdCBjb29yZHMgPSBuZXcgRmxvYXQzMkFycmF5KFxuICAgICAgaW1hZ2VTaXplICogaW1hZ2VTaXplICogKGlucHV0TnVtRGltZW5zaW9ucyArIG51bUxhdGVudFZhcmlhYmxlcykpO1xuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgZCA9IDA7IGQgPCBpbnB1dE51bURpbWVuc2lvbnMgKyBudW1MYXRlbnRWYXJpYWJsZXM7IGQrKykge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW1hZ2VTaXplICogaW1hZ2VTaXplOyBpKyspIHtcbiAgICAgIGNvbnN0IHggPSBpICUgaW1hZ2VTaXplO1xuICAgICAgY29uc3QgeSA9IE1hdGguZmxvb3IoaSAvIGltYWdlU2l6ZSk7XG4gICAgICBjb25zdCBjb29yZCA9IGltYWdlUGl4ZWxUb05vcm1hbGl6ZWRDb29yZChcbiAgICAgICAgICB4LCB5LCBpbWFnZVNpemUsIGltYWdlU2l6ZSwgbnVtTGF0ZW50VmFyaWFibGVzKTtcbiAgICAgIGNvb3Jkc1tkc3QrK10gPSBjb29yZFtkXTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gQXJyYXkyRC5uZXcoXG4gICAgICBbaW5wdXROdW1EaW1lbnNpb25zICsgbnVtTGF0ZW50VmFyaWFibGVzLCBpbWFnZVNpemUgKiBpbWFnZVNpemVdLCBjb29yZHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWRkTGF0ZW50VmFyaWFibGVzU2hhZGVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGlucHV0TnVtRGltZW5zaW9uczogbnVtYmVyKTogV2ViR0xQcm9ncmFtIHtcbiAgY29uc3QgZnJhZ21lbnRTaGFkZXJTb3VyY2UgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHNvdXJjZTtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB1bmlmb3JtIHZlYzIgejtcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIG91dHB1dENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcbiAgICAgIGlmIChvdXRwdXRDUlsxXSA9PSAke2lucHV0TnVtRGltZW5zaW9uc30uMCkge1xuICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHpbMF0sIDAsIDAsIDApO1xuICAgICAgfSBlbHNlIGlmIChvdXRwdXRDUlsxXSA+ICR7aW5wdXROdW1EaW1lbnNpb25zfS4wKSB7XG4gICAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoelsxXSwgMCwgMCwgMCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBnbF9GcmFnQ29sb3IgPSB0ZXh0dXJlMkQoc291cmNlLCByZXN1bHRVVik7XG4gICAgICB9XG4gICAgfWA7XG4gIHJldHVybiBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFkZExhdGVudFZhcmlhYmxlcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBhZGRaU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleDogV2ViR0xUZXh0dXJlLCBzaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgejE6IG51bWJlcixcbiAgICB6MjogbnVtYmVyKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0VGV4LCBzaGFwZVJvd0NvbFswXSwgc2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKGFkZFpTaGFkZXIpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc291cmNlVGV4LCAnc291cmNlJywgMCk7XG4gIGNvbnN0IHpMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ3onKTtcbiAgZ3BncHUuZ2wudW5pZm9ybTJmKHpMb2MsIHoxLCB6Mik7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRSZW5kZXJTaGFkZXIoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgaW1hZ2VTaXplOiBudW1iZXIpOiBXZWJHTFByb2dyYW0ge1xuICBjb25zdCBmcmFnbWVudFNoYWRlclNvdXJjZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgc291cmNlO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHVuaWZvcm0gaW50IGNvbG9yTW9kZTtcbiAgICB1bmlmb3JtIGZsb2F0IG91dHB1dE51bURpbWVuc2lvbnM7XG5cbiAgICBjb25zdCBmbG9hdCBkZXN0aW5hdGlvblNpemUgPSAke2ltYWdlU2l6ZX0uMDtcblxuICAgIGNvbnN0IG1hdDMgeXV2MnJnYiA9IG1hdDMoXG4gICAgICAgICAgMSwgICAgICAgMSwgICAgIDEsXG4gICAgICAgICAgMCwgLS4zNDQxMywgMS43NzIsXG4gICAgICAxLjQwMiwgLS43MTQxNCwgICAgIDApO1xuXG4gICAgdmVjMyBoc3YycmdiKHZlYzMgYykge1xuICAgICAgdmVjNCBLID0gdmVjNCgxLjAsIDIuMCAvIDMuMCwgMS4wIC8gMy4wLCAzLjApO1xuICAgICAgdmVjMyBwID0gYWJzKGZyYWN0KGMueHh4ICsgSy54eXopICogNi4wIC0gSy53d3cpO1xuICAgICAgcmV0dXJuIGMueiAqIG1peChLLnh4eCwgY2xhbXAocCAtIEsueHh4LCAwLjAsIDEuMCksIGMueSk7XG4gICAgfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBvdXRwdXRDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG4gICAgICBmbG9hdCBpbnB1dEMgPSBvdXRwdXRDUi55ICogZGVzdGluYXRpb25TaXplICsgb3V0cHV0Q1IueDtcbiAgICAgIGZsb2F0IHUgPSAoaW5wdXRDICsgMC41KSAvICR7aW1hZ2VTaXplICogaW1hZ2VTaXplfS4wO1xuXG4gICAgICB2ZWM0IGlucHV0UiA9IHZlYzQoMC4wLCAxLjAsIDIuMCwgMy4wKTtcbiAgICAgIHZlYzQgdiA9IChpbnB1dFIgKyAwLjUpIC8gb3V0cHV0TnVtRGltZW5zaW9ucztcblxuICAgICAgdmVjNCB2YWx1ZXMgPSB2ZWM0KFxuICAgICAgICB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHUsIHZbMF0pKS5yLFxuICAgICAgICB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHUsIHZbMV0pKS5yLFxuICAgICAgICB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHUsIHZbMl0pKS5yLFxuICAgICAgICB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHUsIHZbM10pKS5yKTtcblxuICAgICAgaWYgKGNvbG9yTW9kZSA9PSAwKSB7XG4gICAgICAgIC8vIFJHQlxuICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlcy5yZ2IsIDEuMCk7XG4gICAgICB9IGVsc2UgaWYgKGNvbG9yTW9kZSA9PSAxKSB7XG4gICAgICAgIC8vIFJHQkFcbiAgICAgICAgZ2xfRnJhZ0NvbG9yID0gdmFsdWVzO1xuICAgICAgfSBlbHNlIGlmIChjb2xvck1vZGUgPT0gMikge1xuICAgICAgICAvLyBIU1ZcbiAgICAgICAgdmVjMyByZ2IgPSBoc3YycmdiKHZhbHVlcy5yZ2IpO1xuICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHJnYiwgMS4wKTtcbiAgICAgIH0gZWxzZSBpZiAoY29sb3JNb2RlID09IDMpIHtcbiAgICAgICAgLy8gSFNWQVxuICAgICAgICB2ZWMzIHJnYiA9IGhzdjJyZ2IodmFsdWVzLnJnYik7XG4gICAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQocmdiLCB2YWx1ZXNbM10pO1xuICAgICAgfSBlbHNlIGlmIChjb2xvck1vZGUgPT0gNCB8fCBjb2xvck1vZGUgPT0gNSkge1xuICAgICAgICAvLyBZVVZcbiAgICAgICAgdmFsdWVzWzBdID0gY2xhbXAodmFsdWVzWzBdLCAwLjIsIDAuOCk7XG4gICAgICAgIHZhbHVlc1sxXSA9IHZhbHVlc1sxXSAtIDAuNTtcbiAgICAgICAgdmFsdWVzWzJdID0gdmFsdWVzWzJdIC0gMC41O1xuICAgICAgICB2ZWMzIHJnYiA9IHl1djJyZ2IgKiB2YWx1ZXMucmdiO1xuICAgICAgICBpZiAoY29sb3JNb2RlID09IDQpIHtcbiAgICAgICAgICAvLyBZVVZcbiAgICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHJnYiwgMS4wKTtcbiAgICAgICAgfSBlbHNlIGlmIChjb2xvck1vZGUgPT0gNSkge1xuICAgICAgICAgIC8vIFlVVkFcbiAgICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHJnYiwgdmFsdWVzLmEpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGNvbG9yTW9kZSA9PSA2KSB7XG4gICAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsdWVzWzBdLCB2YWx1ZXNbMF0sIHZhbHVlc1swXSwgMS4wKTtcbiAgICAgIH1cbiAgICB9YDtcblxuICByZXR1cm4gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXIoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVuZGVyU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlLFxuICAgIG91dHB1dE51bURpbWVuc2lvbnM6IG51bWJlciwgY29sb3JNb2RlOiBudW1iZXIpIHtcbiAgd2ViZ2xfdXRpbC5iaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihncGdwdS5nbCk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVuZGVyU2hhZGVyKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNvdXJjZVRleCwgJ3NvdXJjZScsIDApO1xuICBjb25zdCBjb2xvck1vZGVMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2NvbG9yTW9kZScpO1xuICBncGdwdS5nbC51bmlmb3JtMWkoY29sb3JNb2RlTG9jLCBjb2xvck1vZGUpO1xuICBjb25zdCBvdXRwdXROdW1EaW1lbnNpb25zTG9jID1cbiAgICAgIGdwZ3B1LmdldFVuaWZvcm1Mb2NhdGlvbignb3V0cHV0TnVtRGltZW5zaW9ucycpO1xuICBncGdwdS5nbC51bmlmb3JtMWYob3V0cHV0TnVtRGltZW5zaW9uc0xvYywgb3V0cHV0TnVtRGltZW5zaW9ucyk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbi8vIE5vcm1hbGl6ZXMgeCwgeSB0byAtLjUgPD0+ICsuNSwgYWRkcyBhIHJhZGl1cyB0ZXJtLCBhbmQgcGFkcyB6ZXJvcyB3aXRoIHRoZVxuLy8gbnVtYmVyIG9mIHogcGFyYW1ldGVycyB0aGF0IHdpbGwgZ2V0IGFkZGVkIGJ5IHRoZSBhZGQgeiBzaGFkZXIuXG5leHBvcnQgZnVuY3Rpb24gaW1hZ2VQaXhlbFRvTm9ybWFsaXplZENvb3JkKFxuICAgIHg6IG51bWJlciwgeTogbnVtYmVyLCBpbWFnZVdpZHRoOiBudW1iZXIsIGltYWdlSGVpZ2h0OiBudW1iZXIsXG4gICAgelNpemU6IG51bWJlcik6IG51bWJlcltdIHtcbiAgY29uc3QgaGFsZldpZHRoID0gaW1hZ2VXaWR0aCAqIDAuNTtcbiAgY29uc3QgaGFsZkhlaWdodCA9IGltYWdlSGVpZ2h0ICogMC41O1xuICBjb25zdCBub3JtWCA9ICh4IC0gaGFsZldpZHRoKSAvIGltYWdlV2lkdGg7XG4gIGNvbnN0IG5vcm1ZID0gKHkgLSBoYWxmSGVpZ2h0KSAvIGltYWdlSGVpZ2h0O1xuXG4gIGNvbnN0IHIgPSBNYXRoLnNxcnQobm9ybVggKiBub3JtWCArIG5vcm1ZICogbm9ybVkpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IFtub3JtWCwgbm9ybVksIHJdO1xuXG4gIC8vIFBhZCB3aXRoIHplcm9zIHRoZSBudW1iZXIgb2YgbGF0ZW50IHRlcm1zLCB0aGVzZSBnZXQgYWRkZWQgb24gdGhlIEdQVSBhc1xuICAvLyB1bmlmb3Jtcy5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB6U2l6ZTsgaSsrKSB7XG4gICAgcmVzdWx0LnB1c2goMCk7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuLyoqXG4gKiBAZmlsZW92ZXJ2aWV3XG4gKlxuICogRGVmaW5lcyBhbiBpbnRlcmZhY2UgZm9yIGNyZWF0aW5nIFBvbHltZXIgZWxlbWVudHMgaW4gVHlwZXNjcmlwdCB3aXRoIHRoZVxuICogY29ycmVjdCB0eXBpbmdzLiBBIFBvbHltZXIgZWxlbWVudCBzaG91bGQgYmUgZGVmaW5lZCBsaWtlIHRoaXM6XG4gKlxuICogYGBgXG4gKiBsZXQgTXlFbGVtZW50UG9seW1lciA9IFBvbHltZXJFbGVtZW50KHtcbiAqICAgaXM6ICdteS1wb2x5bWVyLWVsZW1lbnQnLFxuICogICBwcm9wZXJ0aWVzOiB7XG4gKiAgICAgZm9vOiBzdHJpbmcsXG4gKiAgICAgYmFyOiBBcnJheVxuICogICB9XG4gKiB9KTtcbiAqXG4gKiBjbGFzcyBNeUVsZW1lbnQgZXh0ZW5kcyBNeUVsZW1lbnRQb2x5bWVyIHtcbiAqICAgZm9vOiBzdHJpbmc7XG4gKiAgIGJhcjogbnVtYmVyW107XG4gKlxuICogICByZWFkeSgpIHtcbiAqICAgICBjb25zb2xlLmxvZygnTXlFbGVtZW50IGluaXRpYWxpemVkIScpO1xuICogICB9XG4gKiB9XG4gKlxuICogZG9jdW1lbnQucmVnaXN0ZXJFbGVtZW50KE15RWxlbWVudC5wcm90b3R5cGUuaXMsIE15RWxlbWVudCk7XG4gKiBgYGBcbiAqL1xuXG5leHBvcnQgdHlwZSBTcGVjID0ge1xuICBpczogc3RyaW5nOyBwcm9wZXJ0aWVzOiB7XG4gICAgW2tleTogc3RyaW5nXTogKEZ1bmN0aW9ufHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHR5cGU6IEZ1bmN0aW9uLCB2YWx1ZT86IGFueTtcbiAgICAgIHJlZmxlY3RUb0F0dHJpYnV0ZT86IGJvb2xlYW47XG4gICAgICByZWFkb25seT86IGJvb2xlYW47XG4gICAgICBub3RpZnk/OiBib29sZWFuO1xuICAgICAgY29tcHV0ZWQ/OiBzdHJpbmc7XG4gICAgICBvYnNlcnZlcj86IHN0cmluZztcbiAgICB9KVxuICB9O1xuICBvYnNlcnZlcnM/OiBzdHJpbmdbXTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBQb2x5bWVyRWxlbWVudChzcGVjOiBTcGVjKSB7XG4gIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgcmV0dXJuIFBvbHltZXIuQ2xhc3Moc3BlYyBhcyBhbnkpIGFzIHtuZXcgKCk6IFBvbHltZXJIVE1MRWxlbWVudH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUG9seW1lckhUTUxFbGVtZW50IGV4dGVuZHMgSFRNTEVsZW1lbnQsIHBvbHltZXIuQmFzZSB7fVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2hlY2twb2ludFZhcmlhYmxlIHtcbiAgZmlsZW5hbWU6IHN0cmluZztcbiAgc2hhcGU6IG51bWJlcltdO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IHR5cGUgQ2hlY2twb2ludE1hbmlmZXN0ID0ge1xuICBbdmFyTmFtZTogc3RyaW5nXTogQ2hlY2twb2ludFZhcmlhYmxlXG59O1xuXG5jb25zdCBNQU5JRkVTVF9GSUxFID0gJ21hbmlmZXN0Lmpzb24nO1xuXG5leHBvcnQgY2xhc3MgQ2hlY2twb2ludExvYWRlciB7XG4gIHByaXZhdGUgY2hlY2twb2ludE1hbmlmZXN0OiBDaGVja3BvaW50TWFuaWZlc3Q7XG4gIHByaXZhdGUgdmFyaWFibGVzOiB7W3Zhck5hbWU6IHN0cmluZ106IE5EQXJyYXl9O1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgdXJsUGF0aDogc3RyaW5nKSB7XG4gICAgaWYgKHRoaXMudXJsUGF0aC5jaGFyQXQodGhpcy51cmxQYXRoLmxlbmd0aCAtIDEpICE9PSAnLycpIHtcbiAgICAgIHRoaXMudXJsUGF0aCArPSAnLyc7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBsb2FkTWFuaWZlc3QoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgeGhyLm9wZW4oJ0dFVCcsIHRoaXMudXJsUGF0aCArIE1BTklGRVNUX0ZJTEUpO1xuXG4gICAgICB4aHIub25sb2FkID0gKCkgPT4ge1xuICAgICAgICB0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCA9IEpTT04ucGFyc2UoeGhyLnJlc3BvbnNlVGV4dCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH07XG4gICAgICB4aHIub25lcnJvciA9IChlcnJvcikgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgJHtNQU5JRkVTVF9GSUxFfSBub3QgZm91bmQgYXQgJHt0aGlzLnVybFBhdGh9LiBgICsgZXJyb3IpO1xuICAgICAgfTtcbiAgICAgIHhoci5zZW5kKCk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRDaGVja3BvaW50TWFuaWZlc3QoKTogUHJvbWlzZTxDaGVja3BvaW50TWFuaWZlc3Q+IHtcbiAgICBpZiAodGhpcy5jaGVja3BvaW50TWFuaWZlc3QgPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPENoZWNrcG9pbnRNYW5pZmVzdD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLmxvYWRNYW5pZmVzdCgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHJlc29sdmUodGhpcy5jaGVja3BvaW50TWFuaWZlc3QpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFByb21pc2U8Q2hlY2twb2ludE1hbmlmZXN0PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICByZXNvbHZlKHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldEFsbFZhcmlhYmxlcygpOiBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+IHtcbiAgICBpZiAodGhpcy52YXJpYWJsZXMgIT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPHtbdmFyTmFtZTogc3RyaW5nXTogTkRBcnJheX0+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgcmVzb2x2ZSh0aGlzLnZhcmlhYmxlcyk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2U8e1t2YXJOYW1lOiBzdHJpbmddOiBOREFycmF5fT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy5nZXRDaGVja3BvaW50TWFuaWZlc3QoKS50aGVuKFxuICAgICAgICAgIChjaGVja3BvaW50RGVmaW5pdGlvbjogQ2hlY2twb2ludE1hbmlmZXN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB2YXJpYWJsZU5hbWVzID0gT2JqZWN0LmtleXModGhpcy5jaGVja3BvaW50TWFuaWZlc3QpO1xuXG4gICAgICAgICAgICBjb25zdCB2YXJpYWJsZVByb21pc2VzOiBBcnJheTxQcm9taXNlPE5EQXJyYXk+PiA9IFtdO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YXJpYWJsZU5hbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgIHZhcmlhYmxlUHJvbWlzZXMucHVzaCh0aGlzLmdldFZhcmlhYmxlKHZhcmlhYmxlTmFtZXNbaV0pKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgUHJvbWlzZS5hbGwodmFyaWFibGVQcm9taXNlcykudGhlbih2YXJpYWJsZXMgPT4ge1xuICAgICAgICAgICAgICB0aGlzLnZhcmlhYmxlcyA9IHt9O1xuICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhcmlhYmxlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIHRoaXMudmFyaWFibGVzW3ZhcmlhYmxlTmFtZXNbaV1dID0gdmFyaWFibGVzW2ldO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJlc29sdmUodGhpcy52YXJpYWJsZXMpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICBnZXRWYXJpYWJsZSh2YXJOYW1lOiBzdHJpbmcpOiBQcm9taXNlPE5EQXJyYXk+IHtcbiAgICBpZiAoISh2YXJOYW1lIGluIHRoaXMuY2hlY2twb2ludE1hbmlmZXN0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgbG9hZCBub24tZXhpc3RhbnQgdmFyaWFibGUgJyArIHZhck5hbWUpO1xuICAgIH1cblxuICAgIGNvbnN0IHZhcmlhYmxlUmVxdWVzdFByb21pc2VNZXRob2QgPVxuICAgICAgICAocmVzb2x2ZTogKG5kYXJyYXk6IE5EQXJyYXkpID0+IHZvaWQsIHJlamVjdDogKCkgPT4gdm9pZCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgICAgIHhoci5yZXNwb25zZVR5cGUgPSAnYXJyYXlidWZmZXInO1xuICAgICAgICAgIGNvbnN0IGZuYW1lID0gdGhpcy5jaGVja3BvaW50TWFuaWZlc3RbdmFyTmFtZV0uZmlsZW5hbWU7XG4gICAgICAgICAgeGhyLm9wZW4oJ0dFVCcsIHRoaXMudXJsUGF0aCArIGZuYW1lKTtcblxuICAgICAgICAgIHhoci5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHhoci5yZXNwb25zZSk7XG4gICAgICAgICAgICBjb25zdCBuZGFycmF5ID1cbiAgICAgICAgICAgICAgICBOREFycmF5Lm1ha2UodGhpcy5jaGVja3BvaW50TWFuaWZlc3RbdmFyTmFtZV0uc2hhcGUsIHt2YWx1ZXN9KTtcbiAgICAgICAgICAgIHJlc29sdmUobmRhcnJheSk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICB4aHIub25lcnJvciA9IChlcnJvcikgPT4ge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICdDb3VsZCBub3QgZmV0Y2ggdmFyaWFibGUgJyArIHZhck5hbWUgKyAnOiAnICsgZXJyb3IpO1xuICAgICAgICAgIH07XG4gICAgICAgICAgeGhyLnNlbmQoKTtcbiAgICAgICAgfTtcblxuICAgIGlmICh0aGlzLmNoZWNrcG9pbnRNYW5pZmVzdCA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2U8TkRBcnJheT4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLmxvYWRNYW5pZmVzdCgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIG5ldyBQcm9taXNlPE5EQXJyYXk+KHZhcmlhYmxlUmVxdWVzdFByb21pc2VNZXRob2QpLnRoZW4ocmVzb2x2ZSk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZTxOREFycmF5Pih2YXJpYWJsZVJlcXVlc3RQcm9taXNlTWV0aG9kKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuY29uc3QgU1RBVFNfU0FNUExFX1BFUkNFTlRBR0UgPSAwLjE7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRGF0YVN0YXRzIHtcbiAgZXhhbXBsZUNvdW50OiBudW1iZXI7XG4gIGlucHV0TWluOiBudW1iZXI7XG4gIGlucHV0TWF4OiBudW1iZXI7XG4gIHNoYXBlOiBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIE5vcm1hbGl6YXRpb25JbmZvIHtcbiAgaXNOb3JtYWxpemVkOiBib29sZWFuO1xuICAvLyBCb3VuZHMgb2YgdGhlIG5vcm1hbGl6YXRpb24gaWYgbm9ybWFsaXplZC5cbiAgbG93ZXJCb3VuZD86IG51bWJlcjtcbiAgdXBwZXJCb3VuZD86IG51bWJlcjtcbiAgLy8gTWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgZm9yIGVhY2ggZGltZW5zaW9uIG9mIHRoZSBvcmlnaW5hbCBkYXRhLiBUaGVzZVxuICAvLyBhcmUgdGhlIHNhbWUgc2l6ZSBhcyBhbiBpbnB1dCBleGFtcGxlLiBUaGVzZSBhcmUgY29tcHV0ZWQgbGF6aWx5LCBvbmx5IGlmXG4gIC8vIG5vcm1hbGl6YXRpb24gaXMgcmVxdWVzdGVkLiBJZiB0aGUgZGF0YSBpcyB1bi1ub3JtYWxpemVkLCB0aGVzZSBhcmUga2VwdFxuICAvLyBhcm91bmQgc28gdGhleSBkb24ndCBoYXZlIHRvIGJlIHJlY29tcHV0ZWQuXG4gIG1pblZhbHVlczogRmxvYXQzMkFycmF5O1xuICBtYXhWYWx1ZXM6IEZsb2F0MzJBcnJheTtcbn1cblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEluTWVtb3J5RGF0YXNldCB7XG4gIHByb3RlY3RlZCBkYXRhc2V0OiBOREFycmF5W11bXXxudWxsO1xuXG4gIC8vIENvbnRhaW5zIGluZm9ybWF0aW9uIG5lY2Vzc2FyeSBmb3IgcmVjb25zdHJ1Y3Rpb24gb2YgdGhlIG9yaWdpbmFsIGRhdGFcbiAgLy8gYWZ0ZXIgbm9ybWFsaXphdGlvbi5cbiAgcHJpdmF0ZSBub3JtYWxpemF0aW9uSW5mbzoge1tkYXRhSW5kZXg6IG51bWJlcl06IE5vcm1hbGl6YXRpb25JbmZvfTtcblxuICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgZGF0YVNoYXBlczogbnVtYmVyW11bXSkge1xuICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm8gPSB7fTtcbiAgfVxuXG4gIGdldERhdGFTaGFwZShkYXRhSW5kZXg6IG51bWJlcik6IG51bWJlcltdIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhU2hhcGVzW2RhdGFJbmRleF07XG4gIH1cblxuICBhYnN0cmFjdCBmZXRjaERhdGEoKTogUHJvbWlzZTx2b2lkPjtcblxuICBnZXREYXRhKCk6IE5EQXJyYXlbXVtdfG51bGwge1xuICAgIHJldHVybiB0aGlzLmRhdGFzZXQ7XG4gIH1cblxuICBnZXRTdGF0cygpOiBEYXRhU3RhdHNbXSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5kYXRhc2V0Lm1hcChkID0+IHRoaXMuZ2V0U3RhdHNGb3JEYXRhKGQpKTtcbiAgfVxuXG4gIC8vIENvbXB1dGVzIHN0YXRzIGFjcm9zcyBhIHNhbXBsZWQgcG9ydGlvbiBvZiB0aGUgZGF0YS5cbiAgcHJpdmF0ZSBnZXRTdGF0c0ZvckRhdGEoZGF0YTogTkRBcnJheVtdKTogRGF0YVN0YXRzIHtcbiAgICBsZXQgaW5wdXRNaW4gPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IGlucHV0TWF4ID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuXG4gICAgbGV0IGV4YW1wbGVJbmRpY2VzID0gZGF0YS5tYXAoKGV4YW1wbGUsIGkpID0+IGkpO1xuICAgIHV0aWwuc2h1ZmZsZShleGFtcGxlSW5kaWNlcyk7XG4gICAgZXhhbXBsZUluZGljZXMgPVxuICAgICAgICBleGFtcGxlSW5kaWNlcy5zbGljZShleGFtcGxlSW5kaWNlcy5sZW5ndGggKiBTVEFUU19TQU1QTEVfUEVSQ0VOVEFHRSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGV4YW1wbGVJbmRpY2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGRhdGFbZXhhbXBsZUluZGljZXNbaV1dLmdldFZhbHVlcygpO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dFZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgICAgICBpbnB1dE1pbiA9IE1hdGgubWluKGlucHV0TWluLCBpbnB1dFZhbHVlc1tqXSk7XG4gICAgICAgIGlucHV0TWF4ID0gTWF0aC5tYXgoaW5wdXRNYXgsIGlucHV0VmFsdWVzW2pdKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgaW5wdXRNaW4sXG4gICAgICBpbnB1dE1heCxcbiAgICAgIGV4YW1wbGVDb3VudDogZGF0YS5sZW5ndGgsXG4gICAgICBzaGFwZTogZGF0YVswXS5zaGFwZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBleGFtcGxlcyBOREFycmF5cyB0byBiZSBub3JtYWxpemVkLlxuICAgKiBAcGFyYW0gY3VyTG93ZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyB0aGUgbWluaW11bSB2YWx1ZSBmb3IgZWFjaFxuICAgKiBkaW1lbnNpb24gb3IgYSBmaXhlZCBtaW5pbXVtIHZhbHVlLlxuICAgKiBAcGFyYW0gY3VyVXBwZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyB0aGUgbWF4aW11bSB2YWx1ZSBmb3IgZWFjaFxuICAgKiBkaW1lbnNpb24gb3IgYSBmaXhlZCBtYXhpbXVtIHZhbHVlLlxuICAgKiBAcGFyYW0gbmV3TG93ZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyBuZXcgbWluaW11bSB2YWx1ZXMgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uLCBvciBhIGZpeGVkIG1pbnVtdW0gdmFsdWUgdG8gbm9ybWFsaXplIHRoZSBkYXRhIHRvLlxuICAgKiBAcGFyYW0gbmV3VXBwZXJCb3VuZHMgQW4gYXJyYXkgY29udGFpbmluZyBuZXcgbWF4aW11bSB2YWx1ZXMgZm9yIGVhY2hcbiAgICogZGltZW5zaW9uLCBvciBhIGZpeGVkIG1heGltdW0gdmFsdWUgdG8gbm9ybWFsaXplIHRoZSBkYXRhIHRvLlxuICAgKi9cbiAgcHJpdmF0ZSBub3JtYWxpemVFeGFtcGxlc1RvUmFuZ2UoXG4gICAgICBleGFtcGxlczogTkRBcnJheVtdLCBjdXJMb3dlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcixcbiAgICAgIGN1clVwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyLCBuZXdMb3dlckJvdW5kczogRmxvYXQzMkFycmF5fG51bWJlcixcbiAgICAgIG5ld1VwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyKTogTkRBcnJheVtdIHtcbiAgICBjb25zdCBjdXJCb3VuZHNJc1BlckRpbWVuc2lvbiA9XG4gICAgICAgIChjdXJVcHBlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSAmJlxuICAgICAgICAgY3VyTG93ZXJCb3VuZHMgaW5zdGFuY2VvZiBGbG9hdDMyQXJyYXkpO1xuICAgIGNvbnN0IG5ld0JvdW5kc0lzUGVyRGltZW5zaW9uID1cbiAgICAgICAgKG5ld0xvd2VyQm91bmRzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5ICYmXG4gICAgICAgICBuZXdVcHBlckJvdW5kcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSk7XG5cbiAgICBjb25zdCBpbnB1dFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoZXhhbXBsZXNbMF0uc2hhcGUpO1xuICAgIGNvbnN0IG5ld0V4YW1wbGVzOiBOREFycmF5W10gPSBbXTtcblxuICAgIGV4YW1wbGVzLmZvckVhY2goZXhhbXBsZSA9PiB7XG4gICAgICBjb25zdCBpbnB1dFZhbHVlcyA9IGV4YW1wbGUuZ2V0VmFsdWVzKCk7XG4gICAgICBjb25zdCBub3JtYWxpemVkVmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShpbnB1dFNpemUpO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBpbnB1dFNpemU7IGorKykge1xuICAgICAgICBjb25zdCBjdXJMb3dlckJvdW5kID0gY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKGN1ckxvd2VyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgY3VyTG93ZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBjdXJVcHBlckJvdW5kID0gY3VyQm91bmRzSXNQZXJEaW1lbnNpb24gP1xuICAgICAgICAgICAgKGN1clVwcGVyQm91bmRzIGFzIEZsb2F0MzJBcnJheSlbal0gOlxuICAgICAgICAgICAgY3VyVXBwZXJCb3VuZHMgYXMgbnVtYmVyO1xuICAgICAgICBjb25zdCBjdXJSYW5nZSA9IGN1clVwcGVyQm91bmQgLSBjdXJMb3dlckJvdW5kO1xuXG4gICAgICAgIGNvbnN0IG5ld0xvd2VyQm91bmQgPSBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA/XG4gICAgICAgICAgICAobmV3TG93ZXJCb3VuZHMgYXMgRmxvYXQzMkFycmF5KVtqXSA6XG4gICAgICAgICAgICBuZXdMb3dlckJvdW5kcyBhcyBudW1iZXI7XG4gICAgICAgIGNvbnN0IG5ld1VwcGVyQm91bmQgPSBuZXdCb3VuZHNJc1BlckRpbWVuc2lvbiA/XG4gICAgICAgICAgICAobmV3VXBwZXJCb3VuZHMgYXMgRmxvYXQzMkFycmF5KVtqXSA6XG4gICAgICAgICAgICBuZXdVcHBlckJvdW5kcyBhcyBudW1iZXI7XG4gICAgICAgIGNvbnN0IG5ld1JhbmdlID0gbmV3VXBwZXJCb3VuZCAtIG5ld0xvd2VyQm91bmQ7XG5cbiAgICAgICAgaWYgKGN1clJhbmdlID09PSAwKSB7XG4gICAgICAgICAgbm9ybWFsaXplZFZhbHVlc1tqXSA9IG5ld0xvd2VyQm91bmQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbm9ybWFsaXplZFZhbHVlc1tqXSA9IG5ld0xvd2VyQm91bmQgK1xuICAgICAgICAgICAgICBuZXdSYW5nZSAqIChpbnB1dFZhbHVlc1tqXSAtIGN1ckxvd2VyQm91bmQpIC8gY3VyUmFuZ2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIG5ld0V4YW1wbGVzLnB1c2goTkRBcnJheS5tYWtlKGV4YW1wbGUuc2hhcGUsIHt2YWx1ZXM6IG5vcm1hbGl6ZWRWYWx1ZXN9KSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ld0V4YW1wbGVzO1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wdXRlQm91bmRzKGRhdGFJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG5cbiAgICBjb25zdCBzaXplID0gdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdWzBdLnNoYXBlKTtcblxuICAgIC8vIENvbXB1dGUgbWluIGFuZCBtYXggdmFsdWVzIGZvciBldmVyeSBkaW1lbnNpb24uXG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdID0ge1xuICAgICAgaXNOb3JtYWxpemVkOiBmYWxzZSxcbiAgICAgIG1pblZhbHVlczogbmV3IEZsb2F0MzJBcnJheShzaXplKSxcbiAgICAgIG1heFZhbHVlczogbmV3IEZsb2F0MzJBcnJheShzaXplKVxuICAgIH07XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNpemU7IGkrKykge1xuICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlc1tpXSA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXNbaV0gPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgfVxuXG4gICAgdGhpcy5kYXRhc2V0W2RhdGFJbmRleF0uZm9yRWFjaChleGFtcGxlID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VmFsdWVzID0gZXhhbXBsZS5nZXRWYWx1ZXMoKTtcbiAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgc2l6ZTsgaysrKSB7XG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNba10gPSBNYXRoLm1pbihcbiAgICAgICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXNba10sIGlucHV0VmFsdWVzW2tdKTtcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlc1trXSA9IE1hdGgubWF4KFxuICAgICAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlc1trXSwgaW5wdXRWYWx1ZXNba10pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbm9ybWFsaXplV2l0aGluQm91bmRzKFxuICAgICAgZGF0YUluZGV4OiBudW1iZXIsIGxvd2VyQm91bmQ6IG51bWJlciwgdXBwZXJCb3VuZDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgaXMgbnVsbC4nKTtcbiAgICB9XG4gICAgaWYgKGRhdGFJbmRleCA+PSB0aGlzLmRhdGFzZXQubGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2RhdGFJbmRleCBvdXQgb2YgYm91bmRzLicpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0gPT0gbnVsbCkge1xuICAgICAgdGhpcy5jb21wdXRlQm91bmRzKGRhdGFJbmRleCk7XG4gICAgfVxuXG4gICAgLy8gY3VyTG93ZXIvVXBwZXJCb3VuZHMgb2YgdGhlIGN1cnJlbnQgZGF0YSBzZXQgY2FuIGVpdGhlciBiZSBmaXhlZCBudW1iZXJzXG4gICAgLy8gaWYgdGhlIGRhdGEgaGFzIGFscmVhZHkgYmVlbiBub3JtYWxpemVkLCBvciBjdXJMb3dlci9VcHBlciBmb3IgZWFjaFxuICAgIC8vIGRpbWVuc2lvbiBpZiBpdCBoYXNuJ3QgYmVlbiBub3JtYWxpemVkIHlldC5cbiAgICBsZXQgY3VyTG93ZXJCb3VuZHM6IEZsb2F0MzJBcnJheXxudW1iZXI7XG4gICAgbGV0IGN1clVwcGVyQm91bmRzOiBGbG9hdDMyQXJyYXl8bnVtYmVyO1xuXG4gICAgaWYgKHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQpIHtcbiAgICAgIGN1ckxvd2VyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhO1xuICAgICAgY3VyVXBwZXJCb3VuZHMgPSB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCE7XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1ckxvd2VyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlcztcbiAgICAgIGN1clVwcGVyQm91bmRzID0gdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlcztcbiAgICB9XG5cbiAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSA9IHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICB0aGlzLmRhdGFzZXRbZGF0YUluZGV4XSwgY3VyTG93ZXJCb3VuZHMsIGN1clVwcGVyQm91bmRzLCBsb3dlckJvdW5kLFxuICAgICAgICB1cHBlckJvdW5kKTtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0uaXNOb3JtYWxpemVkID0gdHJ1ZTtcbiAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubG93ZXJCb3VuZCA9IGxvd2VyQm91bmQ7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLnVwcGVyQm91bmQgPSB1cHBlckJvdW5kO1xuICB9XG5cbiAgcHJpdmF0ZSBpc05vcm1hbGl6ZWQoZGF0YUluZGV4OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5ub3JtYWxpemF0aW9uSW5mbyAhPSBudWxsICYmXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5pc05vcm1hbGl6ZWQ7XG4gIH1cblxuICByZW1vdmVOb3JtYWxpemF0aW9uKGRhdGFJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZGF0YXNldCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RyYWluaW5nIG9yIHRlc3QgZGF0YSBpcyBudWxsLicpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pc05vcm1hbGl6ZWQoZGF0YUluZGV4KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdID0gdGhpcy5ub3JtYWxpemVFeGFtcGxlc1RvUmFuZ2UoXG4gICAgICAgIHRoaXMuZGF0YXNldFtkYXRhSW5kZXhdLCB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0ubG93ZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS51cHBlckJvdW5kISxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1pblZhbHVlcyxcbiAgICAgICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLm1heFZhbHVlcyk7XG4gICAgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmlzTm9ybWFsaXplZCA9IGZhbHNlO1xuICB9XG5cbiAgdW5ub3JtYWxpemVFeGFtcGxlcyhleGFtcGxlczogTkRBcnJheVtdLCBkYXRhSW5kZXg6IG51bWJlcik6IE5EQXJyYXlbXSB7XG4gICAgaWYgKCF0aGlzLmlzTm9ybWFsaXplZChkYXRhSW5kZXgpKSB7XG4gICAgICByZXR1cm4gZXhhbXBsZXM7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMubm9ybWFsaXplRXhhbXBsZXNUb1JhbmdlKFxuICAgICAgICBleGFtcGxlcywgdGhpcy5ub3JtYWxpemF0aW9uSW5mb1tkYXRhSW5kZXhdLmxvd2VyQm91bmQhLFxuICAgICAgICB0aGlzLm5vcm1hbGl6YXRpb25JbmZvW2RhdGFJbmRleF0udXBwZXJCb3VuZCEsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5taW5WYWx1ZXMsXG4gICAgICAgIHRoaXMubm9ybWFsaXphdGlvbkluZm9bZGF0YUluZGV4XS5tYXhWYWx1ZXMpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5kYXRhc2V0ID09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuZGF0YXNldC5sZW5ndGg7IGkrKykge1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCB0aGlzLmRhdGFzZXRbaV0ubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgdGhpcy5kYXRhc2V0W2ldW2pdLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5kYXRhc2V0ID0gW107XG4gIH1cbn1cblxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoTGF5ZXJzfSBmcm9tICcuL2dyYXBoX2xheWVycyc7XG5pbXBvcnQgKiBhcyBjb25jYXQzZF91dGlsIGZyb20gJy4vbWF0aC9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuL21hdGgvY29udl91dGlsJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogR3JhcGggaXMgdGhlIHByaW1hcnkgY29udGFpbmVyIHN0cnVjdHVyZSBmb3IgbGVhcm4uanMgb3BlcmF0aW9ucy4gR3JhcGhcbiAqIGhvbGRzIHRoZSB0b3BvbG9neSBvZiBvcGVyYXRpb24gbm9kZXMgYW5kIHRoZSBjb25uZWN0aXZpdHkgYmV0d2VlbiB0aGVtLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGgge1xuICBsYXllcnM6IEdyYXBoTGF5ZXJzO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMubGF5ZXJzID0gbmV3IEdyYXBoTGF5ZXJzKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuYW1lZCB2YXJpYWJsZS4gVmFyaWFibGVzIGFyZSB0ZW5zb3JzIHRoYXQgbWFpbnRhaW4gc3RhdGUgYWNyb3NzXG4gICAqIHNlc3Npb24gY2FsbHMgYW5kIHdob3NlIHZhbHVlcyBhcmUgYWRqdXN0ZWQgZHVyaW5nIGJhY2twcm9wYWdhdGlvblxuICAgKiB0cmFpbmluZy5cbiAgICogQHBhcmFtIG5hbWUgVGhlIG5hbWUgb2YgdGhpcyB2YXJpYWJsZS5cbiAgICogQHBhcmFtIGRhdGEgVGhlIE5EQXJyYXkgdG8gYXNzb2NpYXRlIHdpdGggdGhpcyB2YXJpYWJsZSB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHZhcmlhYmxlLlxuICAgKi9cbiAgdmFyaWFibGUobmFtZTogc3RyaW5nLCBkYXRhOiBOREFycmF5KTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBWYXJpYWJsZU5vZGUodGhpcywgbmFtZSwgZGF0YSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluc2VydHMgYSBwbGFjZWhvbGRlciBmb3IgYSB0ZW5zb3IgdGhhdCB3aWxsIGJlIGFsd2F5cyBmZWQuIFBsYWNlaG9sZGVyc1xuICAgKiBhcmUgaW5wdXQgdGVuc29ycyB3aG9zZSB2YWx1ZXMgYXJlIHByb3ZpZGVkIGJ5IHRoZSBjbGllbnQgdmlhIGZlZWRcbiAgICogZGljdGlvbmFyaWVzLiBQbGFjZWhvbGRlcnMgYXJlIG5vdCB1cGRhdGVkIGFzIHBhcnQgb2YgdHJhaW5pbmc7IHRoZXkgYXJlXG4gICAqIG9ubHkgdXNlZCBhcyBpbW11dGFibGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgcGxhY2Vob2xkZXIuXG4gICAqIEBwYXJhbSBzaGFwZSBUaGUgc2hhcGUgb2YgdGhlIHBsYWNlaG9sZGVyIHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgcGxhY2Vob2xkZXIuXG4gICAqL1xuICBwbGFjZWhvbGRlcihuYW1lOiBzdHJpbmcsIHNoYXBlOiBudW1iZXJbXSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgUGxhY2Vob2xkZXJOb2RlKHRoaXMsIG5hbWUsIHNoYXBlKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uc3RhbnQgdmFsdWUgdGhhdCBwZXJzaXN0cyBhY3Jvc3Mgc2Vzc2lvbiBjYWxscy5cbiAgICogQHBhcmFtIHZhbHVlIFRoZSB2YWx1ZSB0byByZXR1cm4uXG4gICAqIEByZXR1cm4gQSBub2RlIG91dHB1dGluZyB0aGUgY29uc3RhbnQgdmFsdWUuXG4gICAqL1xuICBjb25zdGFudCh2YWx1ZTogQXJyYXlEYXRhKTogVGVuc29yIHtcbiAgICBsZXQgZmluYWxWYWx1ZTogTkRBcnJheTtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJykge1xuICAgICAgZmluYWxWYWx1ZSA9IFNjYWxhci5uZXcodmFsdWUpO1xuICAgIH0gZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICBmaW5hbFZhbHVlID0gdmFsdWU7XG4gICAgfSBlbHNlIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICBjb25zdCB2YWxzID0gbmV3IEZsb2F0MzJBcnJheSh1dGlsLmZsYXR0ZW4odmFsdWUpKTtcbiAgICAgIGZpbmFsVmFsdWUgPSBOREFycmF5Lm1ha2UodXRpbC5pbmZlclNoYXBlKHZhbHVlKSwge3ZhbHVlczogdmFsc30pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3VuaW1wbGVtZW50ZWQgY29uc3RhbnQgdHlwZS4nKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29uc3RhbnROb2RlKHRoaXMsIGZpbmFsVmFsdWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNoYXBlIHRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gYmUgcmVzaGFwZWQuXG4gICAqIEBwYXJhbSBzaGFwZSBUaGUgc2hhcGUgb2YgdGhlIG91dHB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHJlc2hhcGUgb3BlcmF0aW9uLlxuICAgKi9cbiAgcmVzaGFwZSh4OiBUZW5zb3IsIHNoYXBlOiBudW1iZXJbXSk6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChcbiAgICAgICAgbmV3IFJlc2hhcGVOb2RlKHRoaXMsICdSZXNoYXBlJywgeCwgc2hhcGUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIGZ1c2VkIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0d28gdGVuc29ycy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci4gU2FtZSBzaGFwZSBhcyB0MS5cbiAgICogQHBhcmFtIGMxIENvZWZmaWNpZW50IG9mIHQxLiBNdXN0IGJlIHNpemUgMS5cbiAgICogQHBhcmFtIGMyIENvZWZmaWNpZW50IG9mIHQyLiBNdXN0IGJlIHNpemUgMS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyBjMSp0MStjMip0Mi5cbiAgICovXG4gIGZ1c2VkTGluZWFyQ29tYmluYXRpb24oeDE6IFRlbnNvciwgeDI6IFRlbnNvciwgYzE6IFRlbnNvciwgYzI6IFRlbnNvcik6XG4gICAgICBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSh0aGlzLCB4MSwgeDIsIGMxLCBjMikpO1xuICB9XG5cblxuICAvKipcbiAgICogQWRkcyB0d28gdGVuc29ycyAoZWxlbWVudHdpc2UpLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpcyBzY2FsYXIuXG4gICAqIEBwYXJhbSB4MSBUaGUgZmlyc3QgaW5wdXQgdGVuc29yLlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdDErdDIuXG4gICAqL1xuICBhZGQoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQWRkTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJ0cmFjdHMgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxLXQyLlxuICAgKi9cbiAgc3VidHJhY3QoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgU3VidHJhY3ROb2RlKHRoaXMsIHgxLCB4MikpO1xuICB9XG5cbiAgLyoqXG4gICAqIE11bHRpcGx5IHR3byB0ZW5zb3JzIChlbGVtZW50d2lzZSkuIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0MSp0Mi5cbiAgICovXG4gIG11bHRpcGx5KHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IE11bHRpcGx5Tm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXZpZGUgdHdvIHRlbnNvcnMgKGVsZW1lbnR3aXNlKS4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKiBAcGFyYW0geDEgVGhlIGZpcnN0IGlucHV0IHRlbnNvci5cbiAgICogQHBhcmFtIHgyIFRoZSBzZWNvbmQgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHQxIC8gdDIuXG4gICAqL1xuICBkaXZpZGUoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgRGl2aWRlTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgc3VtIG9mIGVsZW1lbnRzIGluIHRoZSB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqL1xuICByZWR1Y2VTdW0oeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBSZWR1Y2VTdW1Ob2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25jYXRzIHR3byAzRCB0ZW5zb3JzIGFsb25nIGEgZ2l2ZW4gYXhpcy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyBjb25jYXQgb2YgdHdvIHRlbnNvcnMgYWxvbmcgYXhpcy5cbiAgICovXG4gIGNvbmNhdDNkKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IsIGF4aXM6IG51bWJlcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29uY2F0M0ROb2RlKHRoaXMsIHgxLCB4MiwgYXhpcykpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBkb3QgcHJvZHVjdCBiZXR3ZWVuIHR3byBtYXRyaWNlcy5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4MiBUaGUgc2Vjb25kIGlucHV0IHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgZG90IHByb2R1Y3Qgb2YgeDEgYW5kIHgyLlxuICAgKi9cbiAgbWF0bXVsKHgxOiBUZW5zb3IsIHgyOiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IE1hdE11bE5vZGUodGhpcywgeDEsIHgyKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSAyRCBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdyBUaGUgd2VpZ2h0IHRlbnNvciB1c2VkIGJ5IHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBiIFRoZSBiaWFzIHRlbnNvciB1c2VkIGJ5IHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBmaWVsZFNpemUgVGhlIHNpemUgb2YgdGhlIGNvbnZvbHV0aW9uYWwga2VybmVsLlxuICAgKiBAcGFyYW0gb3V0cHV0RGVwdGggVGhlIG91dHB1dCBkZXB0aCBvZiB0aGUgY29udm9sdXRpb24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIHplcm9QYWQgVGhlIGFtb3VudCBvZiB6ZXJvIHBhZGRpbmcgb24gYWxsIHNpZGVzIG9mIHRoZSBpbnB1dCB0ZW5zb3IuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbi5cbiAgICovXG4gIGNvbnYyZChcbiAgICAgIHg6IFRlbnNvciwgdzogVGVuc29yLCBiOiBUZW5zb3IsIGZpZWxkU2l6ZTogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgICAgc3RyaWRlID0gMSwgemVyb1BhZD86IG51bWJlcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQ29udm9sdXRpb24yRE5vZGUoXG4gICAgICAgIHRoaXMsIHgsIHcsIGIsIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgMkQgbWF4IHBvb2wgb2YgeC5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgbWF4IHBvb2wgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gZmllbGRTaXplIFRoZSBzaXplIG9mIHRoZSBjb252b2x1dGlvbmFsIGtlcm5lbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBjb252b2x1dGlvbiBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSBhbW91bnQgb2YgemVybyBwYWRkaW5nIG9uIGFsbCBzaWRlcyBvZiB0aGUgaW5wdXQgdGVuc29yLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBtYXggcG9vbCBvcGVyYXRpb24uXG4gICAqL1xuICBtYXhQb29sKHg6IFRlbnNvciwgZmllbGRTaXplOiBudW1iZXIsIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBNYXhQb29sTm9kZSh0aGlzLCB4LCBmaWVsZFNpemUsIHN0cmlkZSwgemVyb1BhZCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGV4cG9uZW50aWFsIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBleHAuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIGUgXiB4IG9wZXJhdGlvbi5cbiAgICovXG4gIGV4cCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IEV4cE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGxvZyBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgbG9nLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBsbih4KSBvcGVyYXRpb24uXG4gICAqL1xuICBsb2coeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBMb2dOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBSZUxVIG9mIHggZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0geCBUaGUgaW5wdXQgdGVuc29yIHRvIHRoZSBSZUxVLlxuICAgKiBAcmV0dXJuIFRoZSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBSZUxVIG9wZXJhdGlvbi5cbiAgICovXG4gIHJlbHUoeDogVGVuc29yKTogVGVuc29yIHtcbiAgICByZXR1cm4gdGhpcy5hZGROb2RlQW5kUmV0dXJuT3V0cHV0KG5ldyBSZUxVTm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgVGFuSCBvZiB4IGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byB0aGUgVGFuSC5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgVGFuSCBvcGVyYXRpb24uXG4gICAqL1xuICB0YW5oKHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgVGFuSE5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIFNpZ21vaWQgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIHNpZ21vaWQuXG4gICAqIEByZXR1cm4gVGhlIHRlbnNvciByZXByZXNlbnRpbmcgdGhlIHNpZ21vaWQgb3BlcmF0aW9uLlxuICAgKi9cbiAgc2lnbW9pZCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNpZ21vaWROb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzcXVhcmUgb2YgeCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCB0ZW5zb3IgdG8gdGhlIHNxdWFyZS5cbiAgICovXG4gIHNxdWFyZSh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNxdWFyZU5vZGUodGhpcywgeCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHNvZnRtYXggcHJvYmFiaWxpdGllcyBmcm9tIGxvZ2l0cy5cbiAgICpcbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGxvZ2l0cy5cbiAgICogQHJldHVybiBUaGUgc29mdG1heCBwcm9iYWJpbGl0aWVzLlxuICAgKi9cbiAgc29mdG1heCh4OiBUZW5zb3IpOiBUZW5zb3Ige1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQobmV3IFNvZnRtYXhOb2RlKHRoaXMsIHgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHRlbnNvciB0byBjbGFzc2lmeS5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgc29mdG1heENyb3NzRW50cm9weUNvc3QoeDogVGVuc29yLCB0YXJnZXQ6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChcbiAgICAgICAgbmV3IFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSh0aGlzLCB4LCB0YXJnZXQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbWVhbi1zcXVhcmVkIGNvc3Qgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAgICogQHBhcmFtIGxhYmVsIFRoZSBsYWJlbCB0ZW5zb3IuXG4gICAqIEBwYXJhbSBwcmVkaWN0aW9uIFRoZSBwcmVkaWN0aW9uIHRlbnNvci5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgbWVhbi1zcXVhcmVkIGNvc3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWVhblNxdWFyZWRDb3N0KGxhYmVsOiBUZW5zb3IsIHByZWRpY3Rpb246IFRlbnNvcikge1xuICAgIHJldHVybiB0aGlzLmFkZE5vZGVBbmRSZXR1cm5PdXRwdXQoXG4gICAgICAgIG5ldyBNZWFuU3F1YXJlZENvc3ROb2RlKHRoaXMsIGxhYmVsLCBwcmVkaWN0aW9uKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgZmxhdHRlbmVkIGluZGV4IG9mIHRoZSBtYXhpbXVtIGVudHJ5IGluIHRoZSB0ZW5zb3IuXG4gICAqIEBwYXJhbSB4IFRoZSB0ZW5zb3Igd2l0aCB0aGUgdmFsdWUuXG4gICAqIEByZXR1cm4gQSBTY2FsYXIgdGVuc29yIHdpdGggdGhlIGluZGV4IG9mIHRoZSBtYXhpbXVtIGVudHJ5LlxuICAgKi9cbiAgYXJnbWF4KHg6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQXJnTWF4Tm9kZSh0aGlzLCB4KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBhcmdtYXggZXF1YWxzIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gICAqIEBwYXJhbSB4MSBGaXJzdCBpbnB1dCB0ZW5zb3IgdG8gY2hlY2sgYWdhaW5zdC5cbiAgICogQHBhcmFtIHgyIFNlY29uZCBpbnB1dCB0ZW5zb3IgdG8gY2hlY2sgYWdhaW5zdC5cbiAgICogQHJldHVybiBUaGUgdGVuc29yIHJlcHJlc2VudGluZyB0aGUgYXJnbWF4IGVxdWFscyBvcGVyYXRpb24uXG4gICAqL1xuICBhcmdtYXhFcXVhbHMoeDE6IFRlbnNvciwgeDI6IFRlbnNvcik6IFRlbnNvciB7XG4gICAgcmV0dXJuIHRoaXMuYWRkTm9kZUFuZFJldHVybk91dHB1dChuZXcgQXJnTWF4RXF1YWxzTm9kZSh0aGlzLCB4MSwgeDIpKTtcbiAgfVxuXG4gIHByaXZhdGUgYWRkTm9kZUFuZFJldHVybk91dHB1dChub2RlOiBOb2RlKTogVGVuc29yIHtcbiAgICB0aGlzLm5vZGVzLnB1c2gobm9kZSk7XG4gICAgbm9kZS52YWxpZGF0ZSgpO1xuICAgIHJldHVybiBub2RlLm91dHB1dDtcbiAgfVxuXG4gIGdldE5vZGVzKCk6IE5vZGVbXSB7XG4gICAgcmV0dXJuIHRoaXMubm9kZXM7XG4gIH1cblxuICBwcml2YXRlIG5vZGVzOiBOb2RlW10gPSBbXTtcbn1cblxuLyoqXG4gKiBUZW5zb3IgcmVwcmVzZW50cyB0aGUgb3V0cHV0IG9mIGFuIG9wZXJhdGlvbiBub2RlIGluIHRoZSBncmFwaC5cbiAqIFRlbnNvcnMgaGF2ZSBubyBkYXRhIGFzc29jaWF0ZWQgd2l0aCB0aGVtLCBidXQgbWFpbnRhaW4gYSBzaGFwZSBhcnJheVxuICogdG8gZGV0ZXJtaW5lIG9wZXJhdGlvbiBjb21wYXRpYmlsaXR5LiBBbGwgZ3JhcGggbWV0aG9kcyB0aGF0IGNyZWF0ZSBncmFwaFxuICogb3BlcmF0aW9ucyByZXR1cm4gVGVuc29yIG9iamVjdHMsIHdoaWNoIGNhbiBiZSB0aG91Z2h0IG9mIGFzICdoYW5kbGVzJyB0b1xuICogb3BlcmF0aW9ucy5cbiAqL1xuZXhwb3J0IGNsYXNzIFRlbnNvciB7XG4gIG5vZGU6IE5vZGU7XG4gIGlkOiBudW1iZXI7XG4gIC8qKlxuICAgKiBAcGFyYW0gc2hhcGUgVGhlIHNoYXBlIG9mIHRoaXMgdGVuc29yLCBpbiBkaW1lbnNpb24gc2l6ZXMuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgdGhpcy5pZCA9IFRlbnNvci5uZXh0SUQrKztcbiAgfVxuICBwcml2YXRlIHN0YXRpYyBuZXh0SUQgPSAwO1xufVxuXG4vKipcbiAqIE5vZGUgaXMgdGhlIGNvbmNyZXRlIGJhc2UgY2xhc3MgZm9yIGFsbCBvcGVyYXRpb25zIGluIHRoZSBncmFwaC5cbiAqIFVzZXJzIGdlbmVyYWxseSBkb24ndCBuZWVkIHRvIGludGVyYWN0IGRpcmVjdGx5IHdpdGggTm9kZSBpbnN0YW5jZXMsIGJ1dCB0aGV5XG4gKiBhcmUgcHJvdmlkZWQgZm9yIGluZm9ybWF0aW9uYWwgYW5kIGludHJvc3BlY3Rpb24gcHVycG9zZXMuXG4gKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTm9kZSB7XG4gIC8qKlxuICAgKiBAcGFyYW0gZ3JhcGggVGhlIGdyYXBoIGNvbnRhaW5pbmcgdGhpcyBub2RlXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lIG9mIHRoaXMgbm9kZVxuICAgKiBAcGFyYW0gaW5wdXRzIEEgZGljdGlvbmFyeSBvZiBuYW1lZCBUZW5zb3JzIHRoYXQgY29tcHJpc2UgdGhpcyBub2RlJ3NcbiAgICogaW5wdXRzLlxuICAgKiBAcGFyYW0gb3V0cHV0IFRoaXMgbm9kZSdzIG91dHB1dCBUZW5zb3JcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHVibGljIGdyYXBoOiBHcmFwaCwgcHVibGljIG5hbWU6IHN0cmluZyxcbiAgICAgIHB1YmxpYyBpbnB1dHM6IHtbbmFtZTogc3RyaW5nXTogVGVuc29yfSwgcHVibGljIG91dHB1dDogVGVuc29yKSB7XG4gICAgdGhpcy5pZCA9IE5vZGUubmV4dElEKys7XG4gICAgb3V0cHV0Lm5vZGUgPSB0aGlzO1xuICB9XG4gIGFic3RyYWN0IHZhbGlkYXRlKCk6IHZvaWQ7XG4gIGlkOiBudW1iZXI7XG4gIHByaXZhdGUgc3RhdGljIG5leHRJRCA9IDA7XG59XG5cbi8qKlxuICogVmFyaWFibGVOb2RlIHJlcHJlc2VudHMgYSB2YXJpYWJsZSwgYSB1c2VyLXByb3ZpZGVkIE5EQXJyYXkgdGhhdCdzXG4gKiBhZGp1c3RlZCBkdXJpbmcgYmFja3Byb3BhZ2F0aW9uIHRyYWluaW5nLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFZhcmlhYmxlTm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIG5hbWU6IHN0cmluZywgcHVibGljIGRhdGE6IE5EQXJyYXkpIHtcbiAgICBzdXBlcihncmFwaCwgbmFtZSwge30sIG5ldyBUZW5zb3IoZGF0YS5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLmRhdGEgIT0gbnVsbCxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyB2YXJpYWJsZSBvcDogRGF0YSBmb3IgdmFyaWFibGUgXFwnJyArIHRoaXMubmFtZSArXG4gICAgICAgICAgICAnXFwnIGlzIG51bGwgb3IgdW5kZWZpbmVkJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBQbGFjZWhvbGRlck5vZGUgcmVwcmVzZW50cyBhIHBsYWNlaG9sZGVyLCBhIHVzZXItcHJvdmlkZWQgTkRBcnJheVxuICogdGhhdCdzIHVzZWQgYXMgaW1tdXRhYmxlIGlucHV0IGR1cmluZyBpbmZlcmVuY2UgYW5kIHRyYWluaW5nLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFBsYWNlaG9sZGVyTm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIG5hbWU6IHN0cmluZywgc2hhcGU6IG51bWJlcltdKSB7XG4gICAgc3VwZXIoZ3JhcGgsIG5hbWUsIHt9LCBuZXcgVGVuc29yKHNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIENvbnN0YW50Tm9kZSByZXByZXNlbnRzIGEgY29uc3RhbnQgdmFsdWUgaW4gdGhlIGdyYXBoLlxuICpcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbnN0YW50Tm9kZSBleHRlbmRzIE5vZGUge1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHB1YmxpYyBkYXRhOiBOREFycmF5KSB7XG4gICAgc3VwZXIoZ3JhcGgsICdDb25zdGFudCcsIHt9LCBuZXcgVGVuc29yKGRhdGEuc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5kYXRhICE9IG51bGwsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29uc3RhbnQ6IGRhdGEgZm9yIHBsYWNlaG9sZGVyIFxcJycgKyB0aGlzLm5hbWUgK1xuICAgICAgICAgICAgJ1xcJyBpcyBudWxsIG9yIHVuZGVmaW5lZCcpO1xuICB9XG59XG5cbi8qKlxuICogUmVzaGFwZU5vZGUgcmVwcmVzZW50cyBhIHJlc2hhcGUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXNoYXBlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHB1YmxpYyBuYW1lOiBzdHJpbmcsIHByaXZhdGUgeDogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBzaGFwZTogbnVtYmVyW10pIHtcbiAgICBzdXBlcihncmFwaCwgbmFtZSwge3h9LCBuZXcgVGVuc29yKHNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7XG4gICAgY29uc3QgeFNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy54LnNoYXBlKTtcbiAgICBjb25zdCBzaGFwZVNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy5zaGFwZSk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHhTaXplID09PSBzaGFwZVNpemUsXG4gICAgICAgICdFcnJvciBtYWtpbmcgcmVzaGFwZSBvcGVyYXRpb246IGlucHV0IFRlbnNvciB0byByZXNoYXBlIFxcJycgK1xuICAgICAgICAgICAgdGhpcy5uYW1lICsgJ1xcJyBvZiBzaGFwZSAoJyArIHRoaXMueC5zaGFwZSArXG4gICAgICAgICAgICAnKSBkb2VzIG5vdCBtYXRjaCBzaXplIG9mIHJlcXVlc3RlZCBzaGFwZSAnICsgdGhpcy5zaGFwZSArICcuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBMaW5lYXJDb21iaW5hdGlvbk5vZGUgcmVwcmVzZW50cyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0d28gdGVuc29ycy5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEZ1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG4gIHN0YXRpYyByZWFkb25seSBDMSA9ICdjMSc7XG4gIHN0YXRpYyByZWFkb25seSBDMiA9ICdjMic7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvciwgcHJpdmF0ZSBjMTogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBjMjogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdMaW5lYXIgQ29tYmluYXRpb24nLCB7dDEsIHQyLCBjMSwgYzJ9LCBuZXcgVGVuc29yKHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpO1xuICAgIGlmICghdXRpbC5pc1NjYWxhclNoYXBlKHRoaXMuYzEuc2hhcGUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBmdXNlZExpbmVhckNvbWJpbmF0aW9uOiBjMSBpcyBub3QgYSBzY2FsYXIsIGdvdCAnICtcbiAgICAgICAgICAnc2hhcGU6ICcgKyB0aGlzLmMxLnNoYXBlKTtcbiAgICB9XG4gICAgaWYgKCF1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy5jMi5zaGFwZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnRXJyb3IgYWRkaW5nIGZ1c2VkTGluZWFyQ29tYmluYXRpb246IGMyIGlzIG5vdCBhIHNjYWxhciwgZ290ICcgK1xuICAgICAgICAgICdzaGFwZTogJyArIHRoaXMuYzIuc2hhcGUpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFkZE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnQWRkJywge3QxLCB0Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IodXRpbC5zaXplRnJvbVNoYXBlKHQxLnNoYXBlKSA9PT0gMSA/IHQyLnNoYXBlIDogdDEuc2hhcGUpKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50MS5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQyLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLnQxLnNoYXBlLCB0aGlzLnQyLnNoYXBlKSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBhZGQgb3BlcmF0aW9uIG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTdWJ0cmFjdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnU3VidHJhY3QnLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIHN1YnRyYWN0IG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNdWx0aXBseU5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFQxID0gJ3QxJztcbiAgc3RhdGljIHJlYWRvbmx5IFQyID0gJ3QyJztcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgdDE6IFRlbnNvciwgcHJpdmF0ZSB0MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTXVsdGlwbHknLCB7dDEsIHQyfSxcbiAgICAgICAgbmV3IFRlbnNvcih1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxID8gdDIuc2hhcGUgOiB0MS5zaGFwZSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh0aGlzLnQxLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMudDEuc2hhcGUsIHRoaXMudDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIG11bHRpcGx5IG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBEaXZpZGVOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBUMSA9ICd0MSc7XG4gIHN0YXRpYyByZWFkb25seSBUMiA9ICd0Mic7XG5cbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHQxOiBUZW5zb3IsIHByaXZhdGUgdDI6IFRlbnNvcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0RpdmlkZScsIHt0MSwgdDJ9LFxuICAgICAgICBuZXcgVGVuc29yKHV0aWwuc2l6ZUZyb21TaGFwZSh0MS5zaGFwZSkgPT09IDEgPyB0Mi5zaGFwZSA6IHQxLnNoYXBlKSk7XG4gIH1cblxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMudDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodGhpcy50Mi5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwodGhpcy50MS5zaGFwZSwgdGhpcy50Mi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgZGl2aWRlIG9wOiBvbmUgb2YgaW5wdXRzIG11c3QgYmUgc2NhbGFyIG9yIHRoZSAnICtcbiAgICAgICAgICAgICdzaGFwZXMgJyArIHRoaXMudDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy50Mi5zaGFwZSArXG4gICAgICAgICAgICAnIG11c3QgbWF0Y2guJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZWR1Y2VTdW1Ob2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdSZWR1Y2VTdW0nLCB7eH0sIG5ldyBUZW5zb3IoW10pKTtcbiAgfVxuXG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBDb25jYXQzRE5vZGUgcmVwcmVzZW50cyBhIDNEIGNvbmNhdGVuYXRpb24gb2YgdHdvIHRlbnNvcnMgYWxvbmcgYW4gYXhpcy5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbmNhdDNETm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBzdGF0aWMgcmVhZG9ubHkgQVhJUyA9ICdheGlzJztcbiAgY29uc3RydWN0b3IoXG4gICAgICBncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yLFxuICAgICAgcHVibGljIGF4aXM6IG51bWJlcikge1xuICAgIHN1cGVyKFxuICAgICAgICBncmFwaCwgJ0NvbmNhdDNEJywge3gxLCB4Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZShcbiAgICAgICAgICAgIHgxLnNoYXBlLCB4Mi5zaGFwZSwgYXhpcykpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIHRoaXMueDEuc2hhcGUsIHRoaXMueDIuc2hhcGUsIHRoaXMuYXhpcyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0TWF0TXVsT3V0cHV0U2hhcGUoeDFTaGFwZTogbnVtYmVyW10sIHgyU2hhcGU6IG51bWJlcltdKTogbnVtYmVyW10ge1xuICBpZiAoeDFTaGFwZS5sZW5ndGggPT09IDEgJiYgeDJTaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gWzFdO1xuICB9IGVsc2UgaWYgKHgxU2hhcGUubGVuZ3RoID09PSAxICYmIHgyU2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgcmV0dXJuIFt4MlNoYXBlWzFdXTtcbiAgfSBlbHNlIGlmICh4MVNoYXBlLmxlbmd0aCA9PT0gMiAmJiB4MlNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgIHJldHVybiBbeDFTaGFwZVswXV07XG4gIH1cbiAgcmV0dXJuIFt4MVNoYXBlWzBdLCB4MlNoYXBlWzFdXTtcbn1cblxuLyoqXG4gKiBNYXRNdWxOb2RlIHJlcHJlc2VudHMgYSBmdWxseSBjb25uZWN0ZWQgbGF5ZXIgaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF0TXVsTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWDEgPSAneDEnO1xuICBzdGF0aWMgcmVhZG9ubHkgWDIgPSAneDInO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgeDE6IFRlbnNvciwgcHJpdmF0ZSB4MjogVGVuc29yKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTWF0TXVsJywge3gxLCB4Mn0sXG4gICAgICAgIG5ldyBUZW5zb3IoZ2V0TWF0TXVsT3V0cHV0U2hhcGUoeDEuc2hhcGUsIHgyLnNoYXBlKSkpO1xuICB9XG5cbiAgdmFsaWRhdGUoKSB7XG4gICAgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzFdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBpbm5lciBzaGFwZXMgb2YgbWF0cmljZXMgd2l0aCBzaGFwZXMgJyArXG4gICAgICAgICAgICAgIHRoaXMueDEuc2hhcGUgKyAnIGFuZCAnICsgdGhpcy54Mi5zaGFwZSArICcgbXVzdCBtYXRjaC4nKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMueDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHRoaXMueDIuc2hhcGUubGVuZ3RoID09PSAxKSB7XG4gICAgICB1dGlsLmFzc2VydChcbiAgICAgICAgICB0aGlzLngxLnNoYXBlWzFdID09PSB0aGlzLngyLnNoYXBlWzBdLFxuICAgICAgICAgICdFcnJvciBhZGRpbmcgbWF0bXVsIG9wOiBzZWNvbmQgZGltZW5zaW9uIG9mIG1hdHJpeCB3aXRoIHNoYXBlICcgK1xuICAgICAgICAgICAgICB0aGlzLngxLnNoYXBlICsgJyBtdXN0IG1hdGNoIHNpemUgb2YgdmVjdG9yIHdpdGggc2hhcGUgJyArXG4gICAgICAgICAgICAgIHRoaXMueDIuc2hhcGUgKyAnLicpO1xuICAgIH0gZWxzZSBpZiAodGhpcy54MS5zaGFwZS5sZW5ndGggPT09IDEgJiYgdGhpcy54Mi5zaGFwZS5sZW5ndGggPT09IDIpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIHRoaXMueDEuc2hhcGVbMF0gPT09IHRoaXMueDIuc2hhcGVbMF0sXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXRtdWwgb3A6IHNpemUgb2YgdmVjdG9yIHdpdGggc2hhcGUgJyArIHRoaXMueDEuc2hhcGUgK1xuICAgICAgICAgICAgICAnIG11c3QgbWF0Y2ggZmlyc3QgZGltZW5zaW9uIG9mIG1hdHJpeCB3aXRoICcgK1xuICAgICAgICAgICAgICAnc2hhcGUgJyArIHRoaXMueDIuc2hhcGUgKyAnLicpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXRtdWwgb3A6IGlucHV0cyBtdXN0IGJlIHZlY3RvcnMgb3IgbWF0cmljZXMuJyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ29udm9sdXRpb24yRE5vZGUgcmVwcmVzZW50cyBhIDJkIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb252b2x1dGlvbjJETm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgc3RhdGljIHJlYWRvbmx5IFcgPSAndyc7XG4gIHN0YXRpYyByZWFkb25seSBCID0gJ2InO1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IsIHByaXZhdGUgdzogVGVuc29yLCBwcml2YXRlIGI6IFRlbnNvcixcbiAgICAgIHB1YmxpYyBmaWVsZFNpemU6IG51bWJlciwgcHVibGljIG91dHB1dERlcHRoOiBudW1iZXIsIHB1YmxpYyBzdHJpZGUgPSAxLFxuICAgICAgcHVibGljIHplcm9QYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcihcbiAgICAgICAgZ3JhcGgsICdDb252b2x1dGlvbiAyRCcsIHt4LCB3LCBifSxcbiAgICAgICAgbmV3IFRlbnNvcihjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgICAgICB4LnNoYXBlIGFzIFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZmllbGRTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLFxuICAgICAgICAgICAgemVyb1BhZCkpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBjb252MmQgb3A6IGlucHV0IG11c3QgYmUgb2YgcmFuayAzLCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMueC5zaGFwZSArICcuJyk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMudy5zaGFwZS5sZW5ndGggPT09IDQsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiB3ZWlnaHRzIG11c3QgYmUgb2YgcmFuayA0LCBidXQgZ290IHNoYXBlOiAnICtcbiAgICAgICAgICAgIHRoaXMudy5zaGFwZSArICcuJyk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHRoaXMuYi5zaGFwZS5sZW5ndGggPT09IDEsXG4gICAgICAgICdFcnJvciBhZGRpbmcgY29udjJkIG9wOiBiaWFzZXMgbXVzdCBiZSBvZiByYW5rIDEsIGJ1dCBnb3Qgc2hhcGU6ICcgK1xuICAgICAgICAgICAgdGhpcy5iLnNoYXBlICsgJy4nKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGVbMl0gPT09IHRoaXMudy5zaGFwZVsyXSxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBjb252MmQgb3A6IGRlcHRoIG9mIGlucHV0ICgnICsgdGhpcy54LnNoYXBlWzJdICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgKCcgKyB0aGlzLncuc2hhcGVbMl0gKyAnKS4nKTtcbiAgfVxufVxuXG4vKipcbiAqIE1heFBvb2xOb2RlIHJlcHJlc2VudHMgYSAyZCBtYXggcG9vbCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF4UG9vbE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKFxuICAgICAgZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHg6IFRlbnNvciwgcHVibGljIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgICAgcHVibGljIHN0cmlkZSA9IDEsIHB1YmxpYyB6ZXJvUGFkPzogbnVtYmVyKSB7XG4gICAgc3VwZXIoXG4gICAgICAgIGdyYXBoLCAnTWF4IHBvb2wnLCB7eH0sXG4gICAgICAgIG5ldyBUZW5zb3IoY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICAgICAgeC5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZpZWxkU2l6ZSwgeC5zaGFwZVsyXSwgc3RyaWRlLFxuICAgICAgICAgICAgemVyb1BhZCkpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy54LnNoYXBlLmxlbmd0aCA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGFkZGluZyBtYXhQb29sIG9wOiBpbnB1dCBtdXN0IGJlIG9mIHJhbmsgMywgYnV0IGdvdCBzaGFwZTogJyArXG4gICAgICAgICAgICB0aGlzLnguc2hhcGUgKyAnLicpO1xuICB9XG59XG5cbi8qKlxuICogUmVMVU5vZGUgcmVwcmVzZW50cyBhIFJlTFUgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlTFVOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnUmVMVScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIEV4cE5vZGUgcmVwcmVzZW50cyBhIEV4cG9uZW50aWF0aW9uIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBFeHBOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnRXhwJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogTG9nTm9kZSByZXByZXNlbnRzIGEgRXhwb25lbnRpYXRpb24gb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIExvZ05vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdMb2cnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBUYW5ITm9kZSByZXByZXNlbnRzIGEgdGFuaCBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgVGFuSE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgeDogVGVuc29yKSB7XG4gICAgc3VwZXIoZ3JhcGgsICdUYW5IJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHt9XG59XG5cbi8qKlxuICogU2lnbW9pZE5vZGUgcmVwcmVzZW50cyBhIHNpZ21vaWQgb3BlcmF0aW9uIGluIHRoZSBncmFwaC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNpZ21vaWROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU2lnbW9pZCcsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNxdWFyZSBub2RlIHJlcHJlc2VudHMgYW4gZWxlbWVudC13aXNlIHNxdWFyZSBvcGVyYXRpb24gaW4gdGhlIGdyYXBoLlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgU3F1YXJlTm9kZSBleHRlbmRzIE5vZGUge1xuICBzdGF0aWMgcmVhZG9ubHkgWCA9ICd4JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NxdWFyZScsIHt4fSwgbmV3IFRlbnNvcih4LnNoYXBlKSk7XG4gIH1cbiAgdmFsaWRhdGUoKSB7fVxufVxuXG4vKipcbiAqIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSByZXByZXNlbnRzIGEgc29mdG1heCBjcm9zcy1lbnRyb3B5IGNvc3Qgb3BlcmF0aW9uXG4gKiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG4gIHN0YXRpYyByZWFkb25seSBUQVJHRVQgPSAndGFyZ2V0JztcbiAgY29uc3RydWN0b3IoZ3JhcGg6IEdyYXBoLCBwcml2YXRlIHg6IFRlbnNvciwgcHJpdmF0ZSB0YXJnZXQ6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU29mdG1heENyb3NzRW50cm9weUNvc3QnLCB7eCwgdGFyZ2V0fSwgbmV3IFRlbnNvcihbXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMueC5zaGFwZSwgdGhpcy50YXJnZXQuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIHNvZnRtYXhDcm9zc0VudHJvcHlDb3N0IG9wOiB4IHNoYXBlICgnICsgdGhpcy54LnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggdGFyZ2V0IHNoYXBlICgnICsgdGhpcy50YXJnZXQuc2hhcGUgKyAnKS4nKTtcbiAgfVxufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFNvZnRtYXhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuXG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ1NvZnRtYXgnLCB7eH0sIG5ldyBUZW5zb3IoeC5zaGFwZSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAnVGhlIGlucHV0IHRvIGEgc29mdG1heCBtdXN0IGJlIGEgMS1EIHRlbnNvcicpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB0aGlzLnguc2hhcGVbMF0gPj0gMixcbiAgICAgICAgJ1RoZSBpbnB1dCB0byBhIHNvZnRtYXggbXVzdCBoYXZlIGF0IGxlYXN0IDIgdmFsdWVzJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBNZWFuU3F1YXJlZENvc3ROb2RlIHJlcHJlc2VudHMgYSBtZWFuIHNxdWFyZWQgY29zdCBvcGVyYXRpb25cbiAqIGluIHRoZSBncmFwaC5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBNZWFuU3F1YXJlZENvc3ROb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBMQUJFTCA9ICdsYWJlbCc7XG4gIHN0YXRpYyByZWFkb25seSBQUkVESUNUSU9OID0gJ3ByZWRpY3Rpb24nO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHByaXZhdGUgbGFiZWw6IFRlbnNvciwgcHJpdmF0ZSBwcmVkaWN0aW9uOiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ01lYW4gU3F1YXJlZCBDb3N0Jywge2xhYmVsLCBwcmVkaWN0aW9ufSwgbmV3IFRlbnNvcihbXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMubGFiZWwuc2hhcGUsIHRoaXMucHJlZGljdGlvbi5zaGFwZSksXG4gICAgICAgICdFcnJvciBhZGRpbmcgbWVhblNxdWFyZWRDb3N0IG9wOiBsYWJlbCBzaGFwZSAoJyArIHRoaXMubGFiZWwuc2hhcGUgK1xuICAgICAgICAgICAgJykgbXVzdCBtYXRjaCBwcmVkaWN0aW9uIHNoYXBlICgnICsgdGhpcy5wcmVkaWN0aW9uLnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBBcmdNYXhOb2RlIHJlcHJlc2VudHMgYW4gYXJnbWF4IG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXhOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYID0gJ3gnO1xuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHB1YmxpYyB4OiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0FyZ01heCcsIHt4fSwgbmV3IFRlbnNvcihbMV0pKTtcbiAgfVxuICB2YWxpZGF0ZSgpIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHRoaXMueC5zaGFwZSkgPiAwLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIGFyZ21heCBvcDogaW5wdXQgdGVuc29yIG11c3QgaGF2ZSBhdCBsZWFzdCBvbmUgZW50cnkuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBBcmdNYXhFcXVhbHNOb2RlIHJlcHJlc2VudHMgYSBhcmdtYXggZXF1YWxzIG9wZXJhdGlvbiBpbiB0aGUgZ3JhcGguXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBBcmdNYXhFcXVhbHNOb2RlIGV4dGVuZHMgTm9kZSB7XG4gIHN0YXRpYyByZWFkb25seSBYMSA9ICd4MSc7XG4gIHN0YXRpYyByZWFkb25seSBYMiA9ICd4Mic7XG4gIGNvbnN0cnVjdG9yKGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSB4MTogVGVuc29yLCBwcml2YXRlIHgyOiBUZW5zb3IpIHtcbiAgICBzdXBlcihncmFwaCwgJ0FyZ01heEVxdWFscycsIHt4MSwgeDJ9LCBuZXcgVGVuc29yKFsxXSkpO1xuICB9XG4gIHZhbGlkYXRlKCkge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHRoaXMueDEuc2hhcGUsIHRoaXMueDIuc2hhcGUpLFxuICAgICAgICAnRXJyb3IgYWRkaW5nIEFyZ01heEVxdWFscyBvcDogeDEgc2hhcGUgKCcgKyB0aGlzLngxLnNoYXBlICtcbiAgICAgICAgICAgICcpIG11c3QgbWF0Y2ggeDIgc2hhcGUgKCcgKyB0aGlzLngyLnNoYXBlICsgJykuJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBTcGxpdCBub2RlcyBhcmUgdXNlZCB0byBhY2N1bXVsYXRlIGJhY2twcm9wIGRlcml2YXRpdmVzIHdoZW4gYSBub2RlJ3Mgb3V0cHV0XG4gKiB0ZW5zb3IgaXMgY29uc3VtZWQgYnkgbXVsdGlwbGUgbm9kZXMuXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcGxpdE5vZGUgZXh0ZW5kcyBOb2RlIHtcbiAgc3RhdGljIHJlYWRvbmx5IFggPSAneCc7XG5cbiAgb3V0cHV0czogVGVuc29yW10gPSBbXTtcblxuICBjb25zdHJ1Y3RvcihncmFwaDogR3JhcGgsIHg6IFRlbnNvcikge1xuICAgIHN1cGVyKGdyYXBoLCAnU3BsaXROb2RlJywge3h9LCBuZXcgVGVuc29yKHguc2hhcGUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlcnMgYSBuZXcgY29uc3VtZXIgb2YgdGhpcyBzcGxpdCBub2RlLCBpLmUuIGEgbmV3IG5vZGUgdGhhdCB1c2VzIHRoZVxuICAgKiBub2RlJ3Mgb3V0cHV0IHRlbnNvci5cbiAgICovXG4gIGdldE5ld091dHB1dFRlbnNvcigpOiBUZW5zb3Ige1xuICAgIGNvbnN0IG91dHB1dCA9IG5ldyBUZW5zb3IodGhpcy5pbnB1dHNbU3BsaXROb2RlLlhdLnNoYXBlKTtcbiAgICBvdXRwdXQubm9kZSA9IHRoaXM7XG4gICAgdGhpcy5vdXRwdXRzLnB1c2gob3V0cHV0KTtcbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG4gIHZhbGlkYXRlKCkge31cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCB0eXBlIEFycmF5RGF0YSA9XG4gICAgTkRBcnJheXxudW1iZXJ8bnVtYmVyW118bnVtYmVyW11bXXxudW1iZXJbXVtdW118bnVtYmVyW11bXVtdW107XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R3JhcGgsIFRlbnNvcn0gZnJvbSAnLi9ncmFwaCc7XG5pbXBvcnQge0luaXRpYWxpemVyLCBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciwgWmVyb3NJbml0aWFsaXplcn0gZnJvbSAnLi9pbml0aWFsaXplcnMnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogQSBsYXllcnMgc3VnYXIgY2xhc3MgYXJvdW5kIHRoZSBncmFwaCB0aGF0IGluaXRpYWxpemVzIHZhcmlhYmxlc1xuICogYXV0b21hdGljYWxseSBmb3IgbGF5ZXJzLlxuICovXG5leHBvcnQgY2xhc3MgR3JhcGhMYXllcnMge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGc6IEdyYXBoKSB7fVxuXG4gIGRlbnNlKFxuICAgICAgbmFtZTogc3RyaW5nLCB4OiBUZW5zb3IsIHVuaXRzOiBudW1iZXIsXG4gICAgICBhY3RpdmF0aW9uOiAoKHg6IFRlbnNvcikgPT4gVGVuc29yKXxudWxsID0gbnVsbCwgdXNlQmlhcyA9IHRydWUsXG4gICAgICBrZXJuZWxJbml0aWFsaXplcjogSW5pdGlhbGl6ZXIgPSBuZXcgVmFyaWFuY2VTY2FsaW5nSW5pdGlhbGl6ZXIoKSxcbiAgICAgIGJpYXNJbml0aWFsaXplcjogSW5pdGlhbGl6ZXIgPSBuZXcgWmVyb3NJbml0aWFsaXplcigpKSB7XG4gICAgY29uc3Qgd2VpZ2h0cyA9IHRoaXMuZy52YXJpYWJsZShcbiAgICAgICAgbmFtZSArICctd2VpZ2h0cycsXG4gICAgICAgIGtlcm5lbEluaXRpYWxpemVyLmluaXRpYWxpemUoW3guc2hhcGVbMF0sIHVuaXRzXSwgeC5zaGFwZVswXSwgdW5pdHMpKTtcblxuICAgIGxldCBvdXQgPSB0aGlzLmcubWF0bXVsKHgsIHdlaWdodHMpO1xuXG4gICAgaWYgKHVzZUJpYXMpIHtcbiAgICAgIGNvbnN0IGJpYXMgPSB0aGlzLmcudmFyaWFibGUoXG4gICAgICAgICAgbmFtZSArICctYmlhcycsXG4gICAgICAgICAgYmlhc0luaXRpYWxpemVyLmluaXRpYWxpemUoW3VuaXRzXSwgeC5zaGFwZVswXSwgdW5pdHMpKTtcbiAgICAgIG91dCA9IHRoaXMuZy5hZGQob3V0LCBiaWFzKTtcbiAgICB9XG5cbiAgICBpZiAoYWN0aXZhdGlvbiAhPSBudWxsKSB7XG4gICAgICBvdXQgPSBhY3RpdmF0aW9uKG91dCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dDtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBkYXRhc2V0IGZyb20gJy4vZGF0YXNldCc7XG5pbXBvcnQge0dyYXBoLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheU1hdGhDUFV9IGZyb20gJy4vbWF0aC9tYXRoX2NwdSc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmltcG9ydCB7Q29zdFJlZHVjdGlvbiwgRmVlZEVudHJ5LCBTZXNzaW9ufSBmcm9tICcuL3Nlc3Npb24nO1xuXG5jb25zdCBERUZBVUxUX0VWQUxfSU5URVJWQUxfTVMgPSAxNTAwO1xuY29uc3QgREVGQVVMVF9DT1NUX0lOVEVSVkFMX01TID0gNTAwO1xuY29uc3QgREVGQVVMVF9JTkZFUkVOQ0VfRVhBTVBMRV9JTlRFUlZBTF9NUyA9IDMwMDA7XG5cbmV4cG9ydCBpbnRlcmZhY2UgR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyIHtcbiAgYmF0Y2hlc1RyYWluZWRDYWxsYmFjaz86ICh0b3RhbEJhdGNoZXNUcmFpbmVkOiBudW1iZXIpID0+IHZvaWQ7XG4gIGF2Z0Nvc3RDYWxsYmFjaz86IChhdmdDb3N0OiBTY2FsYXIpID0+IHZvaWQ7XG4gIG1ldHJpY0NhbGxiYWNrPzogKG1ldHJpYzogTkRBcnJheSkgPT4gdm9pZDtcbiAgaW5mZXJlbmNlRXhhbXBsZXNDYWxsYmFjaz86XG4gICAgICAoZmVlZHM6IEZlZWRFbnRyeVtdW10sIGluZmVyZW5jZVZhbHVlczogTkRBcnJheVtdKSA9PiB2b2lkO1xuICBpbmZlcmVuY2VFeGFtcGxlc1BlclNlY0NhbGxiYWNrPzogKGV4YW1wbGVzUGVyU2VjOiBudW1iZXIpID0+IHZvaWQ7XG4gIHRyYWluRXhhbXBsZXNQZXJTZWNDYWxsYmFjaz86IChleGFtcGxlc1BlclNlYzogbnVtYmVyKSA9PiB2b2lkO1xuICB0b3RhbFRpbWVDYWxsYmFjaz86ICh0b3RhbFRpbWVTZWM6IG51bWJlcikgPT4gdm9pZDtcbiAgZG9uZVRyYWluaW5nQ2FsbGJhY2s/OiAoKSA9PiB2b2lkO1xufVxuXG5leHBvcnQgZW51bSBNZXRyaWNSZWR1Y3Rpb24ge1xuICBTVU0sXG4gIE1FQU5cbn1cblxuLyoqXG4gKiBBIGNsYXNzIHRoYXQgZHJpdmVzIHRoZSB0cmFpbmluZyBvZiBhIGdyYXBoIG1vZGVsIGdpdmVuIGEgZGF0YXNldC4gSXQgYWxsb3dzXG4gKiB0aGUgdXNlciB0byBwcm92aWRlIGEgc2V0IG9mIGNhbGxiYWNrcyBmb3IgbWVhc3VyZW1lbnRzIGxpa2UgY29zdCwgYWNjdXJhY3ksXG4gKiBhbmQgc3BlZWQgb2YgdHJhaW5pbmcuXG4gKi9cbmV4cG9ydCBjbGFzcyBHcmFwaFJ1bm5lciB7XG4gIHByaXZhdGUgY29zdFRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIHRyYWluRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdO1xuICBwcml2YXRlIGJhdGNoU2l6ZTogbnVtYmVyO1xuICBwcml2YXRlIG9wdGltaXplcjogT3B0aW1pemVyO1xuICBwcml2YXRlIGN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzOiBudW1iZXJ8dW5kZWZpbmVkO1xuICBwcml2YXRlIGNvc3RJbnRlcnZhbE1zOiBudW1iZXI7XG5cbiAgcHJpdmF0ZSBtZXRyaWNUZW5zb3I6IFRlbnNvcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBtZXRyaWNCYXRjaFNpemU6IG51bWJlcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgbWV0cmljUmVkdWN0aW9uOiBNZXRyaWNSZWR1Y3Rpb247XG4gIHByaXZhdGUgbWV0cmljSW50ZXJ2YWxNczogbnVtYmVyO1xuXG4gIHByaXZhdGUgaW5mZXJlbmNlVGVuc29yOiBUZW5zb3I7XG4gIHByaXZhdGUgaW5mZXJlbmNlRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdfHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBpbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNczogbnVtYmVyO1xuICBwcml2YXRlIGluZmVyZW5jZUV4YW1wbGVDb3VudDogbnVtYmVyO1xuXG4gIC8vIFJ1bnRpbWUgaW5mb3JtYXRpb24uXG4gIHByaXZhdGUgaXNUcmFpbmluZzogYm9vbGVhbjtcbiAgcHJpdmF0ZSB0b3RhbEJhdGNoZXNUcmFpbmVkOiBudW1iZXI7XG4gIHByaXZhdGUgYmF0Y2hlc1RyYWluZWRUaGlzUnVuOiBudW1iZXI7XG4gIHByaXZhdGUgbGFzdENvbXB1dGVkTWV0cmljOiBOREFycmF5O1xuXG4gIHByaXZhdGUgaXNJbmZlcnJpbmc6IGJvb2xlYW47XG4gIHByaXZhdGUgY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXM6IG51bWJlcnx1bmRlZmluZWQ7XG4gIHByaXZhdGUgaW5mZXJlbmNlUGFzc2VzVGhpc1J1bjogbnVtYmVyO1xuXG4gIHByaXZhdGUgdHJhaW5TdGFydFRpbWVzdGFtcDogbnVtYmVyO1xuICBwcml2YXRlIGxhc3RDb3N0VGltZXN0YW1wID0gMDtcbiAgcHJpdmF0ZSBsYXN0RXZhbFRpbWVzdGFtcCA9IDA7XG5cbiAgcHJpdmF0ZSBsYXN0U3RvcFRpbWVzdGFtcDogbnVtYmVyfG51bGw7XG4gIHByaXZhdGUgdG90YWxJZGxlVGltZU1zID0gMDtcblxuICBwcml2YXRlIHplcm9TY2FsYXI6IFNjYWxhcjtcbiAgcHJpdmF0ZSBtZXRyaWNCYXRjaFNpemVTY2FsYXI6IFNjYWxhcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgbWF0aDogTkRBcnJheU1hdGgsIHByaXZhdGUgc2Vzc2lvbjogU2Vzc2lvbixcbiAgICAgIHByaXZhdGUgZXZlbnRPYnNlcnZlcjogR3JhcGhSdW5uZXJFdmVudE9ic2VydmVyKSB7XG4gICAgdGhpcy5yZXNldFN0YXRpc3RpY3MoKTtcbiAgICB0aGlzLnplcm9TY2FsYXIgPSBTY2FsYXIubmV3KDApO1xuICB9XG5cbiAgcmVzZXRTdGF0aXN0aWNzKCkge1xuICAgIHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCA9IDA7XG4gICAgdGhpcy50b3RhbElkbGVUaW1lTXMgPSAwO1xuICAgIHRoaXMubGFzdFN0b3BUaW1lc3RhbXAgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0IHRoZSB0cmFpbmluZyBsb29wIHdpdGggYW4gb3B0aW9uYWwgbnVtYmVyIG9mIGJhdGNoZXMgdG8gdHJhaW4gZm9yLlxuICAgKiBPcHRpb25hbGx5IHRha2VzIGEgbWV0cmljIHRlbnNvciBhbmQgZmVlZCBlbnRyaWVzIHRvIGNvbXB1dGUgcGVyaW9kaWNhbGx5LlxuICAgKiBUaGlzIGNhbiBiZSB1c2VkIGZvciBjb21wdXRpbmcgYWNjdXJhY3ksIG9yIGEgc2ltaWxhciBtZXRyaWMuXG4gICAqL1xuICB0cmFpbihcbiAgICAgIGNvc3RUZW5zb3I6IFRlbnNvciwgdHJhaW5GZWVkRW50cmllczogRmVlZEVudHJ5W10sIGJhdGNoU2l6ZTogbnVtYmVyLFxuICAgICAgb3B0aW1pemVyOiBPcHRpbWl6ZXIsIG51bUJhdGNoZXM/OiBudW1iZXIsIG1ldHJpY1RlbnNvcj86IFRlbnNvcixcbiAgICAgIG1ldHJpY0ZlZWRFbnRyaWVzPzogRmVlZEVudHJ5W10sIG1ldHJpY0JhdGNoU2l6ZT86IG51bWJlcixcbiAgICAgIG1ldHJpY1JlZHVjdGlvbiA9IE1ldHJpY1JlZHVjdGlvbi5NRUFOLFxuICAgICAgZXZhbEludGVydmFsTXMgPSBERUZBVUxUX0VWQUxfSU5URVJWQUxfTVMsXG4gICAgICBjb3N0SW50ZXJ2YWxNcyA9IERFRkFVTFRfQ09TVF9JTlRFUlZBTF9NUykge1xuICAgIHRoaXMuY29zdFRlbnNvciA9IGNvc3RUZW5zb3I7XG4gICAgdGhpcy50cmFpbkZlZWRFbnRyaWVzID0gdHJhaW5GZWVkRW50cmllcztcbiAgICB0aGlzLm1ldHJpY1RlbnNvciA9IG1ldHJpY1RlbnNvcjtcbiAgICB0aGlzLm1ldHJpY0ZlZWRFbnRyaWVzID0gbWV0cmljRmVlZEVudHJpZXM7XG4gICAgaWYgKG1ldHJpY0JhdGNoU2l6ZSAhPSBudWxsICYmIHRoaXMubWV0cmljQmF0Y2hTaXplICE9PSBtZXRyaWNCYXRjaFNpemUpIHtcbiAgICAgIGlmICh0aGlzLm1ldHJpY0JhdGNoU2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhtZXRyaWNCYXRjaFNpemUpO1xuICAgIH1cbiAgICB0aGlzLm1ldHJpY0JhdGNoU2l6ZSA9IG1ldHJpY0JhdGNoU2l6ZTtcbiAgICB0aGlzLm1ldHJpY1JlZHVjdGlvbiA9IG1ldHJpY1JlZHVjdGlvbjtcbiAgICB0aGlzLmJhdGNoU2l6ZSA9IGJhdGNoU2l6ZTtcbiAgICB0aGlzLm9wdGltaXplciA9IG9wdGltaXplcjtcblxuICAgIHRoaXMubWV0cmljSW50ZXJ2YWxNcyA9IGV2YWxJbnRlcnZhbE1zO1xuICAgIHRoaXMuY29zdEludGVydmFsTXMgPSBjb3N0SW50ZXJ2YWxNcztcbiAgICB0aGlzLmN1cnJlbnRUcmFpbkxvb3BOdW1CYXRjaGVzID0gbnVtQmF0Y2hlcztcblxuICAgIHRoaXMuYmF0Y2hlc1RyYWluZWRUaGlzUnVuID0gMDtcbiAgICB0aGlzLmlzVHJhaW5pbmcgPSB0cnVlO1xuICAgIHRoaXMudHJhaW5TdGFydFRpbWVzdGFtcCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIHRoaXMudHJhaW5OZXR3b3JrKCk7XG4gIH1cblxuICBzdG9wVHJhaW5pbmcoKSB7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gZmFsc2U7XG4gICAgdGhpcy5sYXN0U3RvcFRpbWVzdGFtcCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICB9XG5cbiAgcmVzdW1lVHJhaW5pbmcoKSB7XG4gICAgdGhpcy5pc1RyYWluaW5nID0gdHJ1ZTtcbiAgICBpZiAodGhpcy5sYXN0U3RvcFRpbWVzdGFtcCAhPSBudWxsKSB7XG4gICAgICB0aGlzLnRvdGFsSWRsZVRpbWVNcyArPSBwZXJmb3JtYW5jZS5ub3coKSAtIHRoaXMubGFzdFN0b3BUaW1lc3RhbXA7XG4gICAgfVxuICAgIHRoaXMudHJhaW5OZXR3b3JrKCk7XG4gIH1cblxuICBwcml2YXRlIHRyYWluTmV0d29yaygpIHtcbiAgICBpZiAodGhpcy5iYXRjaGVzVHJhaW5lZFRoaXNSdW4gPT09IHRoaXMuY3VycmVudFRyYWluTG9vcE51bUJhdGNoZXMpIHtcbiAgICAgIHRoaXMuc3RvcFRyYWluaW5nKCk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmlzVHJhaW5pbmcpIHtcbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuZG9uZVRyYWluaW5nQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIuZG9uZVRyYWluaW5nQ2FsbGJhY2soKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnN0IHNob3VsZENvbXB1dGVDb3N0ID0gdGhpcy5ldmVudE9ic2VydmVyLmF2Z0Nvc3RDYWxsYmFjayAhPSBudWxsICYmXG4gICAgICAgIChzdGFydCAtIHRoaXMubGFzdENvc3RUaW1lc3RhbXAgPiB0aGlzLmNvc3RJbnRlcnZhbE1zKTtcbiAgICBpZiAoc2hvdWxkQ29tcHV0ZUNvc3QpIHtcbiAgICAgIHRoaXMubGFzdENvc3RUaW1lc3RhbXAgPSBzdGFydDtcbiAgICB9XG5cbiAgICBjb25zdCBjb3N0UmVkdWN0aW9uID1cbiAgICAgICAgc2hvdWxkQ29tcHV0ZUNvc3QgPyBDb3N0UmVkdWN0aW9uLk1FQU4gOiBDb3N0UmVkdWN0aW9uLk5PTkU7XG5cbiAgICB0aGlzLm1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGF2Z0Nvc3QgPSB0aGlzLnNlc3Npb24udHJhaW4oXG4gICAgICAgICAgdGhpcy5jb3N0VGVuc29yLCB0aGlzLnRyYWluRmVlZEVudHJpZXMsIHRoaXMuYmF0Y2hTaXplLFxuICAgICAgICAgIHRoaXMub3B0aW1pemVyLCBjb3N0UmVkdWN0aW9uKTtcblxuICAgICAgaWYgKHNob3VsZENvbXB1dGVDb3N0KSB7XG4gICAgICAgIGNvbnN0IHRyYWluVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnQ7XG5cbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmF2Z0Nvc3RDYWxsYmFjayEoYXZnQ29zdCk7XG5cbiAgICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci50cmFpbkV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgIT0gbnVsbCkge1xuICAgICAgICAgIGNvbnN0IGV4YW1wbGVzUGVyU2VjID0gKHRoaXMuYmF0Y2hTaXplICogMTAwMCAvIHRyYWluVGltZSk7XG4gICAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLnRyYWluRXhhbXBsZXNQZXJTZWNDYWxsYmFjayhleGFtcGxlc1BlclNlYyk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5tZXRyaWNDYWxsYmFjayAhPSBudWxsICYmXG4gICAgICAgICAgdGhpcy5tZXRyaWNGZWVkRW50cmllcyAhPSBudWxsICYmXG4gICAgICAgICAgc3RhcnQgLSB0aGlzLmxhc3RFdmFsVGltZXN0YW1wID4gdGhpcy5tZXRyaWNJbnRlcnZhbE1zKSB7XG4gICAgICAgIHRoaXMubGFzdEV2YWxUaW1lc3RhbXAgPSBzdGFydDtcblxuICAgICAgICBpZiAodGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMgIT0gbnVsbCkge1xuICAgICAgICAgIHRoaXMubGFzdENvbXB1dGVkTWV0cmljLmRpc3Bvc2UoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmxhc3RDb21wdXRlZE1ldHJpYyA9IHRoaXMuY29tcHV0ZU1ldHJpYygpO1xuICAgICAgICB0aGlzLmV2ZW50T2JzZXJ2ZXIubWV0cmljQ2FsbGJhY2sodGhpcy5sYXN0Q29tcHV0ZWRNZXRyaWMpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5ldmVudE9ic2VydmVyLnRvdGFsVGltZUNhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLnRvdGFsVGltZUNhbGxiYWNrKFxuICAgICAgICAgICAgKHN0YXJ0IC0gdGhpcy50cmFpblN0YXJ0VGltZXN0YW1wKSAvIDEwMDApO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmJhdGNoZXNUcmFpbmVkVGhpc1J1bisrO1xuICAgICAgdGhpcy50b3RhbEJhdGNoZXNUcmFpbmVkKys7XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuYmF0Y2hlc1RyYWluZWRDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIHRoaXMuZXZlbnRPYnNlcnZlci5iYXRjaGVzVHJhaW5lZENhbGxiYWNrKHRoaXMudG90YWxCYXRjaGVzVHJhaW5lZCk7XG4gICAgICB9XG5cbiAgICB9KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMudHJhaW5OZXR3b3JrKCkpO1xuICB9XG5cbiAgaW5mZXIoXG4gICAgICBpbmZlcmVuY2VUZW5zb3I6IFRlbnNvciwgaW5mZXJlbmNlRmVlZEVudHJpZXM6IEZlZWRFbnRyeVtdLFxuICAgICAgaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMgPSBERUZBVUxUX0lORkVSRU5DRV9FWEFNUExFX0lOVEVSVkFMX01TLFxuICAgICAgaW5mZXJlbmNlRXhhbXBsZUNvdW50ID0gNSwgbnVtUGFzc2VzPzogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrID09IG51bGwgJiZcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2sgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdDYW5ub3Qgc3RhcnQgaW5mZXJlbmNlIGxvb3AsIG5vIGluZmVyZW5jZSBleGFtcGxlIG9yICcgK1xuICAgICAgICAgICdleGFtcGxlcy9zZWMgb2JzZXJ2ZXIgcHJvdmlkZWQuJyk7XG4gICAgfVxuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBmZWVkIHZhbHVlcyBhcmUgcHJvdmlkZXJzLCBhbmQgbm90IE5EQXJyYXlzLlxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5mZXJlbmNlRmVlZEVudHJpZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGZlZWRFbnRyeSA9IGluZmVyZW5jZUZlZWRFbnRyaWVzW2ldO1xuXG4gICAgICBpZiAoZmVlZEVudHJ5LmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdDYW5ub3Qgc3RhcnQgaW5mZXJlbmNlIG9uIHRoZSBtb2RlbCBydW5uZXIgd2l0aCBmZWVkIGVudHJpZXMgb2YgJyArXG4gICAgICAgICAgICAndHlwZSBOREFycmF5LiBQbGVhc2UgdXNlIElucHV0UHJvdmlkZXJzLicpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuaW5mZXJlbmNlRXhhbXBsZUludGVydmFsTXMgPSBpbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNcztcbiAgICB0aGlzLmluZmVyZW5jZVRlbnNvciA9IGluZmVyZW5jZVRlbnNvcjtcbiAgICB0aGlzLmluZmVyZW5jZUZlZWRFbnRyaWVzID0gaW5mZXJlbmNlRmVlZEVudHJpZXM7XG4gICAgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgPSBpbmZlcmVuY2VFeGFtcGxlQ291bnQ7XG4gICAgdGhpcy5jdXJyZW50SW5mZXJlbmNlTG9vcE51bVBhc3NlcyA9IG51bVBhc3NlcztcbiAgICBpZiAoIXRoaXMuaXNJbmZlcnJpbmcpIHtcbiAgICAgIHRoaXMuaW5mZXJlbmNlUGFzc2VzVGhpc1J1biA9IDA7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuaW5mZXJOZXR3b3JrKCkpO1xuICAgIH1cbiAgICB0aGlzLmlzSW5mZXJyaW5nID0gdHJ1ZTtcbiAgfVxuXG4gIHByaXZhdGUgaW5mZXJOZXR3b3JrKCkge1xuICAgIGlmICghdGhpcy5pc0luZmVycmluZyB8fFxuICAgICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4gPT09IHRoaXMuY3VycmVudEluZmVyZW5jZUxvb3BOdW1QYXNzZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLm1hdGguc2NvcGUoKGtlZXAsIHRyYWNrKSA9PiB7XG4gICAgICBjb25zdCBmZWVkczogRmVlZEVudHJ5W11bXSA9IFtdO1xuICAgICAgY29uc3QgaW5mZXJlbmNlVmFsdWVzOiBOREFycmF5W10gPSBbXTtcblxuICAgICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQ7IGkrKykge1xuICAgICAgICAvLyBQb3B1bGF0ZSBhIG5ldyBGZWVkRW50cnlbXSBwb3B1bGF0ZWQgd2l0aCBOREFycmF5cy5cbiAgICAgICAgY29uc3QgbmRhcnJheUZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5mZXJlbmNlRmVlZEVudHJpZXMhLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgY29uc3QgZmVlZEVudHJ5ID0gdGhpcy5pbmZlcmVuY2VGZWVkRW50cmllcyFbal07XG4gICAgICAgICAgbmRhcnJheUZlZWRFbnRyaWVzLnB1c2goe1xuICAgICAgICAgICAgdGVuc29yOiBmZWVkRW50cnkudGVuc29yLFxuICAgICAgICAgICAgZGF0YTpcbiAgICAgICAgICAgICAgICB0cmFjaygoZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcikuZ2V0TmV4dENvcHkodGhpcy5tYXRoKSlcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBmZWVkcy5wdXNoKG5kYXJyYXlGZWVkRW50cmllcyk7XG5cbiAgICAgICAgaW5mZXJlbmNlVmFsdWVzLnB1c2goXG4gICAgICAgICAgICB0aGlzLnNlc3Npb24uZXZhbCh0aGlzLmluZmVyZW5jZVRlbnNvciwgbmRhcnJheUZlZWRFbnRyaWVzKSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLmV2ZW50T2JzZXJ2ZXIuaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNDYWxsYmFjayAhPSBudWxsKSB7XG4gICAgICAgIC8vIEZvcmNlIGEgR1BVIGRvd25sb2FkLCBzaW5jZSBpbmZlcmVuY2UgcmVzdWx0cyBhcmUgZ2VuZXJhbGx5IG5lZWRlZCBvblxuICAgICAgICAvLyB0aGUgQ1BVIGFuZCBpdCdzIG1vcmUgZmFpciB0byBpbmNsdWRlIGJsb2NraW5nIG9uIHRoZSBHUFUgdG8gY29tcGxldGVcbiAgICAgICAgLy8gaXRzIHdvcmsgZm9yIHRoZSBpbmZlcmVuY2UgbWVhc3VyZW1lbnQuXG4gICAgICAgIGluZmVyZW5jZVZhbHVlc1tpbmZlcmVuY2VWYWx1ZXMubGVuZ3RoIC0gMV0uZ2V0VmFsdWVzKCk7XG5cbiAgICAgICAgY29uc3QgaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNUaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydDtcblxuICAgICAgICBjb25zdCBleGFtcGxlc1BlclNlYyA9XG4gICAgICAgICAgICAodGhpcy5pbmZlcmVuY2VFeGFtcGxlQ291bnQgKiAxMDAwIC8gaW5mZXJlbmNlRXhhbXBsZXNQZXJTZWNUaW1lKTtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzUGVyU2VjQ2FsbGJhY2shKGV4YW1wbGVzUGVyU2VjKTtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuZXZlbnRPYnNlcnZlci5pbmZlcmVuY2VFeGFtcGxlc0NhbGxiYWNrICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5ldmVudE9ic2VydmVyLmluZmVyZW5jZUV4YW1wbGVzQ2FsbGJhY2soZmVlZHMsIGluZmVyZW5jZVZhbHVlcyk7XG4gICAgICB9XG4gICAgICB0aGlzLmluZmVyZW5jZVBhc3Nlc1RoaXNSdW4rKztcblxuICAgIH0pO1xuICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5pbmZlck5ldHdvcmsoKSwgdGhpcy5pbmZlcmVuY2VFeGFtcGxlSW50ZXJ2YWxNcyk7XG4gIH1cblxuICBzdG9wSW5mZXJyaW5nKCkge1xuICAgIHRoaXMuaXNJbmZlcnJpbmcgPSBmYWxzZTtcbiAgfVxuXG4gIGlzSW5mZXJlbmNlUnVubmluZygpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc0luZmVycmluZztcbiAgfVxuXG4gIGNvbXB1dGVNZXRyaWMoKTogU2NhbGFyIHtcbiAgICBpZiAodGhpcy5tZXRyaWNGZWVkRW50cmllcyA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBjb21wdXRlIG1ldHJpYywgbm8gbWV0cmljIEZlZWRFbnRyaWVzIHByb3ZpZGVkLicpO1xuICAgIH1cblxuICAgIGxldCBtZXRyaWMgPSB0aGlzLnplcm9TY2FsYXI7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubWV0cmljQmF0Y2hTaXplITsgaSsrKSB7XG4gICAgICAgIGNvbnN0IG1ldHJpY1ZhbHVlID1cbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbi5ldmFsKHRoaXMubWV0cmljVGVuc29yISwgdGhpcy5tZXRyaWNGZWVkRW50cmllcyEpO1xuXG4gICAgICAgIG1ldHJpYyA9IHRoaXMubWF0aC5hZGQobWV0cmljLCBtZXRyaWNWYWx1ZSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLm1ldHJpY1JlZHVjdGlvbiA9PT0gTWV0cmljUmVkdWN0aW9uLk1FQU4pIHtcbiAgICAgICAgbWV0cmljID0gdGhpcy5tYXRoLmRpdmlkZShtZXRyaWMsIHRoaXMubWV0cmljQmF0Y2hTaXplU2NhbGFyKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG1ldHJpYztcbiAgICB9KTtcbiAgfVxuXG4gIGdldFRvdGFsQmF0Y2hlc1RyYWluZWQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy50b3RhbEJhdGNoZXNUcmFpbmVkO1xuICB9XG5cbiAgZ2V0TGFzdENvbXB1dGVkTWV0cmljKCk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMubGFzdENvbXB1dGVkTWV0cmljO1xuICB9XG5cbiAgc2V0TWF0aChtYXRoOiBOREFycmF5TWF0aCkge1xuICAgIHRoaXMubWF0aCA9IG1hdGg7XG4gIH1cblxuICBzZXRTZXNzaW9uKHNlc3Npb246IFNlc3Npb24pIHtcbiAgICB0aGlzLnNlc3Npb24gPSBzZXNzaW9uO1xuICB9XG5cbiAgc2V0SW5mZXJlbmNlVGVuc29yKGluZmVyZW5jZVRlbnNvcjogVGVuc29yKSB7XG4gICAgdGhpcy5pbmZlcmVuY2VUZW5zb3IgPSBpbmZlcmVuY2VUZW5zb3I7XG4gIH1cblxuICBzZXRJbmZlcmVuY2VFeGFtcGxlQ291bnQoaW5mZXJlbmNlRXhhbXBsZUNvdW50OiBudW1iZXIpIHtcbiAgICB0aGlzLmluZmVyZW5jZUV4YW1wbGVDb3VudCA9IGluZmVyZW5jZUV4YW1wbGVDb3VudDtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0NvbnN0YW50Tm9kZSwgR3JhcGgsIE5vZGUsIFBsYWNlaG9sZGVyTm9kZSwgVGVuc29yLCBWYXJpYWJsZU5vZGV9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgcHJpb3JpdHlfcXVldWUgZnJvbSAnLi9wcmlvcml0eV9xdWV1ZSc7XG5pbXBvcnQge1ByaW9yaXR5UXVldWV9IGZyb20gJy4vcHJpb3JpdHlfcXVldWUnO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuLyoqXG4gKiBHaXZlbiBhIHRhcmdldCBub2RlIGluIGEgZ3JhcGgsIGFjY3VtdWxhdGUgdGhlIHNldCBvZiBhbGwgbm9kZXMgdGhhdCBuZWVkIHRvXG4gKiBiZSBldmFsdWF0ZWQgaW4gb3JkZXIgdG8gZXZhbHVhdGUgdGhlIHRhcmdldCBncmFwaC4gVHJhdmVyc2FsIHN0b3BzIGFueXdoZXJlXG4gKiBhIG5vZGUncyB2YWx1ZXMgYXJlIGZlZCBpbiBleHRlcm5hbGx5IHZpYSBcImZlZWQgZGljdHNcIi5cbiAqIEBwYXJhbSBub2RlcyBUaGUgbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIHRlcm1pbmF0aW5nTm9kZXMgVGhlIHNldCBvZiBub2RlcyB0aGF0IHN0b3AgdHJhdmVyc2FsLlxuICogQHJldHVybiBUaGUgdW5vcmRlcmVkIHNldCBvZiBub2RlcyB0aGF0IG5lZWQgdG8gYmUgZXZhbHVhdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VW5vcmRlcmVkRXZhbHVhdGlvblNldChcbiAgICBub2RlczogTm9kZVtdLCB0ZXJtaW5hdGluZ05vZGVzOiBOb2RlW10pOiBOb2RlW10ge1xuICBjb25zdCB0ZXJtaW5hdGluZ05vZGVNYXA6IHtbaWQ6IG51bWJlcl06IE5vZGV9ID0ge307XG4gIGNvbnN0IHNlZW46IHtbaWQ6IG51bWJlcl06IE5vZGV9ID0ge307XG4gIGNvbnN0IHNldDogTm9kZVtdID0gW107XG4gIGNvbnN0IHZpc2l0OiBOb2RlW10gPSBub2Rlcy5zbGljZSgpO1xuICB0ZXJtaW5hdGluZ05vZGVzLmZvckVhY2gobm9kZSA9PiB0ZXJtaW5hdGluZ05vZGVNYXBbbm9kZS5pZF0gPSBub2RlKTtcbiAgLyogRmxvb2QgZmlsbDogV2hpbGUgdGhlICd0byB2aXNpdCcgc3RhY2sgaXMgbm90IGVtcHR5LCBwb3AgYSBub2RlIG9mZiBvZiBpdC5cbiAgICogSWYgdGhlIG5vZGUgaGFzIG5vdCB5ZXQgYmVlbiB2aXNpdGVkLCBhZGQgaXQgdG8gdGhlIHNldCwgbWFyayBpdCBhcyBzZWVuLFxuICAgKiBhbmQgZW5xdWV1ZSBhbGwgb2YgaXRzIGFuY2VzdG9yIChpbnB1dCkgbm9kZXMuICovXG4gIHdoaWxlICh2aXNpdC5sZW5ndGggIT09IDApIHtcbiAgICBjb25zdCBjdXIgPSB2aXNpdC5wb3AoKSE7XG4gICAgaWYgKHNlZW5bY3VyLmlkXSA9PSBudWxsKSB7XG4gICAgICBpZiAodGVybWluYXRpbmdOb2RlTWFwW2N1ci5pZF0gPT0gbnVsbCkge1xuICAgICAgICBPYmplY3Qua2V5cyhjdXIuaW5wdXRzKVxuICAgICAgICAgICAgLm1hcChpbnB1dE5hbWUgPT4gY3VyLmlucHV0c1tpbnB1dE5hbWVdKVxuICAgICAgICAgICAgLmZvckVhY2goaW5wdXQgPT4gdmlzaXQucHVzaChpbnB1dC5ub2RlKSk7XG4gICAgICB9XG4gICAgICBzZXQucHVzaChjdXIpO1xuICAgICAgc2VlbltjdXIuaWRdID0gY3VyO1xuICAgIH1cbiAgfVxuICByZXR1cm4gc2V0O1xufVxuXG4vKipcbiAqIEdpdmVuIGEgc2V0IG9mIG5vZGVzLCBjb21wdXRlIHRoZWlyIG9yZGVyIHN1Y2ggdGhhdCBhbGwgZGVwZW5kZW50IG5vZGVzIGFyZVxuICogZXZhbHVhdGVkIGFmdGVyIHRoZWlyIGRlcGVuZGVlcy4gVGhpcyBpcyB0aGUgJ2luZmVyZW5jZSBvcmRlcicgZm9yIG5vZGVzIGluXG4gKiB0aGUgb3BlcmF0aW9uIGdyYXBoLlxuICogQHBhcmFtIHVub3JkZXJlZEV2YWx1YXRpb25TZXQgVGhlIHVub3JkZXJlZCBzZXQgb2Ygbm9kZXMgdGhhdCBuZWVkIHRvIGJlXG4gKiBldmFsdWF0ZWQuXG4gKiBAcmV0dXJuIFRoZSBpbnB1dCBub2RlcyBpbiBmb3J3YXJkIGV2YWx1YXRpb24gb3JkZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRlcmVkRXZhbHVhdGlvblNldCh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0OiBOb2RlW10pOlxuICAgIE5vZGVbXSB7XG4gIC8qIEEgcHJpb3JpdHkgcXVldWUgaXMgdXNlZCwgd2hlcmUgdGhlIHByaW9yaXR5IGlzIHRoZSByZW1haW5pbmcgbnVtYmVyIG9mXG4gICAqIHVuZXZhbHVhdGVkIG5vZGVzIHdob3NlIGlucHV0cyBjb21lIGZyb20gdGhlIGVsZW1lbnQgbm9kZS4gVGhpcyBndWFyYW50ZWVzXG4gICAqIHRoYXQgYWxsIGRvd25zdHJlYW0gbm9kZXMgd2lsbCBiZSBkZXF1ZXVlZCBiZWZvcmUgdGhlaXIgYW5jZXN0b3JzLiAqL1xuICBjb25zdCBzZXQ6IE5vZGVbXSA9IFtdO1xuICBjb25zdCBub2RlSW5kaWNlczoge1tpZDogbnVtYmVyXTogbnVtYmVyfSA9IHt9O1xuICBjb25zdCBwZW5kaW5nRGVwZW5kZW5jaWVzOiB7W2lkOiBudW1iZXJdOiBudW1iZXJ9ID0ge307XG5cbiAgLyogVGhlIHF1ZXVlIHByaW9yaXR5IGNhbGxiYWNrIGxvb2tzIGF0IHRoZSBudW1iZXIgb2YgcGVuZGluZyBkZXBlbmRlbmNpZXMgb2ZcbiAgICogYSBnaXZlbiBub2RlLiBUaGUgcXVldWUgaW5kZXggb2JzZXJ2ZXIgY2FsbGJhY2sgbWFpbnRhaW5zIHRoZSBsb2NhdGlvbiBvZlxuICAgKiBlYWNoIG5vZGUgaW4gdGhlIGFycmF5LCBmb3IgcHJpb3JpdHkgdXBkYXRlcy4gKi9cbiAgY29uc3Qgbm9kZVF1ZXVlID0gbmV3IFByaW9yaXR5UXVldWU8Tm9kZT4oXG4gICAgICAoYTogTm9kZSwgYjogTm9kZSkgPT4gcHJpb3JpdHlfcXVldWUuZGVmYXVsdENvbXBhcmUoXG4gICAgICAgICAgcGVuZGluZ0RlcGVuZGVuY2llc1thLmlkXSwgcGVuZGluZ0RlcGVuZGVuY2llc1tiLmlkXSksXG4gICAgICAobm9kZTogTm9kZSwgbmV3SW5kZXg6IG51bWJlcikgPT4gbm9kZUluZGljZXNbbm9kZS5pZF0gPSBuZXdJbmRleCk7XG5cbiAgdW5vcmRlcmVkRXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4gcGVuZGluZ0RlcGVuZGVuY2llc1tub2RlLmlkXSA9IDApO1xuXG4gIC8qIEZvciBldmVyeSBkZXNjZW5kZW50IG9mIGEgbm9kZSAob3V0cHV0IG9mIGFuY2VzdG9yIGlzIGlucHV0IHRvIGRlc2NlbmRhbnQpLFxuICAgKiBpbmNyZW1lbnQgdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIGZvciB0aGUgYW5jZXN0b3IuIFRoaXMgcHJlcGFyZXNcbiAgICogdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIGFzIGEgcHJpb3JpdHkgbWFwLiAqL1xuICB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmZvckVhY2goXG4gICAgICBub2RlID0+IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKVxuICAgICAgICAgICAgICAgICAgLm1hcChrZXkgPT4gbm9kZS5pbnB1dHNba2V5XSlcbiAgICAgICAgICAgICAgICAgIC5mb3JFYWNoKGlucHV0ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHVub3JkZXJlZEV2YWx1YXRpb25TZXQuaW5kZXhPZihpbnB1dC5ub2RlKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgICBwZW5kaW5nRGVwZW5kZW5jaWVzW2lucHV0Lm5vZGUuaWRdKys7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH0pKTtcblxuICB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiBub2RlUXVldWUuZW5xdWV1ZShub2RlKSk7XG5cbiAgd2hpbGUgKCFub2RlUXVldWUuZW1wdHkoKSkge1xuICAgIHNldC51bnNoaWZ0KG5vZGVRdWV1ZS5kZXF1ZXVlKCkpO1xuICAgIC8qIEFzIGVhY2ggbm9kZSBpcyB2aXNpdGVkLCBkZWNyZW1lbnQgdGhlICdwZW5kaW5nIGRlcGVuZGVuY3kgY291bnQnIG9mXG4gICAgICogZWFjaCBhbmNlc3RvciwgYW5kIHRlbGwgdGhlIHByaW9yaXR5IHF1ZXVlIHRoYXQgdGhlIHByaW9yaXR5IGhhcyBjaGFuZ2VkLlxuICAgICAqL1xuICAgIE9iamVjdC5rZXlzKHNldFswXS5pbnB1dHMpLm1hcChrZXkgPT4gc2V0WzBdLmlucHV0c1trZXldKS5mb3JFYWNoKGlucHV0ID0+IHtcbiAgICAgIGlmICh1bm9yZGVyZWRFdmFsdWF0aW9uU2V0LmluZGV4T2YoaW5wdXQubm9kZSkgPT09IC0xKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHBlbmRpbmdEZXBlbmRlbmNpZXNbaW5wdXQubm9kZS5pZF0tLTtcbiAgICAgIG5vZGVRdWV1ZS51cGRhdGUoaW5wdXQubm9kZSwgbm9kZUluZGljZXNbaW5wdXQubm9kZS5pZF0pO1xuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIHNldDtcbn1cblxuLyoqXG4gKiBAcmV0dXJuIFRydWUgaWZmIHRoZSBub2RlIGlzIGFuIGlucHV0IG5vZGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0lucHV0Tm9kZShub2RlOiBOb2RlKTogYm9vbGVhbiB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhub2RlLmlucHV0cykubGVuZ3RoID09PSAwO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkQmFja1Byb3AodDogVGVuc29yKTogYm9vbGVhbiB7XG4gIHJldHVybiAhKHQubm9kZSBpbnN0YW5jZW9mIENvbnN0YW50Tm9kZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1Bhc3N0aHJvdWdoTm9kZShub2RlOiBOb2RlLCBtYXA6IFRlbnNvckFycmF5TWFwKTogYm9vbGVhbiB7XG4gIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhub2RlLmlucHV0cyk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGlucHV0ID0gbm9kZS5pbnB1dHNba2V5c1tpXV07XG4gICAgaWYgKG1hcC5nZXQoaW5wdXQsIHRydWUpID09PSBtYXAuZ2V0KG5vZGUub3V0cHV0LCB0cnVlKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0ICogYXMgZ3BncHVfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyByZW5kZXJfbmRhcnJheV9ncHVfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvcmVuZGVyX25kYXJyYXlfZ3B1X3V0aWwnO1xuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL21hdGgvd2ViZ2wvd2ViZ2xfdXRpbCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbmV4cG9ydCB7Q2hlY2twb2ludExvYWRlcn0gZnJvbSAnLi9jaGVja3BvaW50X2xvYWRlcic7XG5leHBvcnQge0RhdGFTdGF0cywgSW5NZW1vcnlEYXRhc2V0fSBmcm9tICcuL2RhdGFzZXQnO1xuZXhwb3J0IHtHcmFwaCwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmV4cG9ydCB7R3JhcGhSdW5uZXIsIEdyYXBoUnVubmVyRXZlbnRPYnNlcnZlciwgTWV0cmljUmVkdWN0aW9ufSBmcm9tICcuL2dyYXBoX3J1bm5lcic7XG5leHBvcnQge0NvbnN0YW50SW5pdGlhbGl6ZXIsIEluaXRpYWxpemVyLCBOREFycmF5SW5pdGlhbGl6ZXIsIE9uZXNJbml0aWFsaXplciwgUmFuZG9tTm9ybWFsSW5pdGlhbGl6ZXIsIFJhbmRvbVRydW5jYXRlZE5vcm1hbEluaXRpYWxpemVyLCBSYW5kb21Vbmlmb3JtSW5pdGlhbGl6ZXIsIFZhcmlhbmNlU2NhbGluZ0luaXRpYWxpemVyLCBaZXJvc0luaXRpYWxpemVyfSBmcm9tICcuL2luaXRpYWxpemVycyc7XG5leHBvcnQge0luQ1BVTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciwgSW5HUFVNZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyLCBJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmV4cG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5leHBvcnQge05EQXJyYXlNYXRoQ1BVfSBmcm9tICcuL21hdGgvbWF0aF9jcHUnO1xuZXhwb3J0IHtOREFycmF5TWF0aEdQVX0gZnJvbSAnLi9tYXRoL21hdGhfZ3B1JztcbmV4cG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgQXJyYXkzRCwgQXJyYXk0RCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5leHBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9tYXRoL3dlYmdsL2dwZ3B1X2NvbnRleHQnO1xuZXhwb3J0IHtPcHRpbWl6ZXJ9IGZyb20gJy4vb3B0aW1pemVyJztcbmV4cG9ydCB7Q29zdFJlZHVjdGlvbiwgRmVlZEVudHJ5LCBTZXNzaW9ufSBmcm9tICcuL3Nlc3Npb24nO1xuZXhwb3J0IHtTR0RPcHRpbWl6ZXJ9IGZyb20gJy4vc2dkX29wdGltaXplcic7XG4vLyBTZWNvbmQgbGV2ZWwgZXhwb3J0cy5cbmV4cG9ydCB7Y29udl91dGlsLCBncGdwdV91dGlsLCByZW5kZXJfbmRhcnJheV9ncHVfdXRpbCwgdXRpbCwgd2ViZ2xfdXRpbH07XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuXG4vKipcbiAqIEluaXRpYWxpemVyIGludGVyZmFjZSwgYWxsIGluaXRpYWxpemVyIGltcGxlbWVudCB0aGlzIGludGVyZmFjZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbml0aWFsaXplciB7XG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXk7XG59XG5cbmV4cG9ydCBjbGFzcyBWYXJpYW5jZVNjYWxpbmdJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHNjYWxlID0gMS4wLFxuICAgICAgcHJpdmF0ZSBtb2RlOiAnZmFuX2luJ3wnZmFuX291dCd8J2Zhbl9hdmcnID0gJ2Zhbl9pbicsXG4gICAgICBwcml2YXRlIGRpc3RyaWJ1dGlvbjogJ3VuaWZvcm0nfCdub3JtYWwnID0gJ25vcm1hbCcpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgbGV0IG4gPSAwO1xuICAgIGlmICh0aGlzLm1vZGUgPT09ICdmYW5faW4nKSB7XG4gICAgICBuID0gaW5wdXRVbml0cztcbiAgICB9IGVsc2UgaWYgKHRoaXMubW9kZSA9PT0gJ2Zhbl9vdXQnKSB7XG4gICAgICBuID0gb3V0cHV0VW5pdHM7XG4gICAgfSBlbHNlIGlmICh0aGlzLm1vZGUgPT09ICdmYW5fYXZnJykge1xuICAgICAgbiA9IChpbnB1dFVuaXRzICsgb3V0cHV0VW5pdHMpIC8gMjtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdVbmV4cGVjdGVkIG1vZGUgZm9yIHZhcmlhbmNlIHNjYWxpbmcgaW5pdGlhbGl6ZXI6ICcgKyB0aGlzLm1vZGUpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmRpc3RyaWJ1dGlvbiA9PT0gJ25vcm1hbCcpIHtcbiAgICAgIHJldHVybiBOREFycmF5LnJhbmRUcnVuY2F0ZWROb3JtYWwoXG4gICAgICAgICAgd2VpZ2h0c1NoYXBlLCAwLjAsIE1hdGguc3FydCh0aGlzLnNjYWxlIC8gbikpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5kaXN0cmlidXRpb24gPT09ICd1bmlmb3JtJykge1xuICAgICAgcmV0dXJuIE5EQXJyYXkucmFuZFVuaWZvcm0oXG4gICAgICAgICAgd2VpZ2h0c1NoYXBlLCAwLjAsIE1hdGguc3FydCgzICogdGhpcy5zY2FsZSAvIG4pKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdVbmV4cGVjdGVkIGRpc3RyaWJ1dGlvbiBmb3IgdmFyaWFuY2Ugc2NhbGluZyBpbml0aWFsaXplcjogJyArXG4gICAgICAgICAgdGhpcy5kaXN0cmlidXRpb24pO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgWmVyb3NJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIE9uZXNJbml0aWFsaXplciBpbXBsZW1lbnRzIEluaXRpYWxpemVyIHtcbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIGNvbnN0IHZhbHVlcyA9IE5EQXJyYXkuemVyb3Mod2VpZ2h0c1NoYXBlKTtcbiAgICB2YWx1ZXMuZmlsbCgxKTtcbiAgICByZXR1cm4gdmFsdWVzO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBDb25zdGFudEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHZhbHVlID0gMCkge31cblxuICBpbml0aWFsaXplKHdlaWdodHNTaGFwZTogbnVtYmVyW10sIGlucHV0VW5pdHM6IG51bWJlciwgb3V0cHV0VW5pdHM6IG51bWJlcik6XG4gICAgICBOREFycmF5IHtcbiAgICBjb25zdCB2YWx1ZXMgPSBOREFycmF5Lnplcm9zKHdlaWdodHNTaGFwZSk7XG4gICAgdmFsdWVzLmZpbGwodGhpcy52YWx1ZSk7XG4gICAgcmV0dXJuIHZhbHVlcztcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheUluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG5kYXJyYXk6IE5EQXJyYXkpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMubmRhcnJheTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgUmFuZG9tTm9ybWFsSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbWVhbiA9IDAsIHByaXZhdGUgc3RkZXYgPSAuMDUpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZE5vcm1hbCh3ZWlnaHRzU2hhcGUsIHRoaXMubWVhbiwgdGhpcy5zdGRldik7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFJhbmRvbVRydW5jYXRlZE5vcm1hbEluaXRpYWxpemVyIGltcGxlbWVudHMgSW5pdGlhbGl6ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIG1lYW4gPSAwLCBwcml2YXRlIHN0ZGV2ID0gLjA1KSB7fVxuXG4gIGluaXRpYWxpemUod2VpZ2h0c1NoYXBlOiBudW1iZXJbXSwgaW5wdXRVbml0czogbnVtYmVyLCBvdXRwdXRVbml0czogbnVtYmVyKTpcbiAgICAgIE5EQXJyYXkge1xuICAgIHJldHVybiBOREFycmF5LnJhbmRUcnVuY2F0ZWROb3JtYWwod2VpZ2h0c1NoYXBlLCB0aGlzLm1lYW4sIHRoaXMuc3RkZXYpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSYW5kb21Vbmlmb3JtSW5pdGlhbGl6ZXIgaW1wbGVtZW50cyBJbml0aWFsaXplciB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgbWludmFsID0gLS4wNSwgcHJpdmF0ZSBtYXh2YWwgPSAuMDUpIHt9XG5cbiAgaW5pdGlhbGl6ZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdLCBpbnB1dFVuaXRzOiBudW1iZXIsIG91dHB1dFVuaXRzOiBudW1iZXIpOlxuICAgICAgTkRBcnJheSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZFVuaWZvcm0od2VpZ2h0c1NoYXBlLCB0aGlzLm1pbnZhbCwgdGhpcy5tYXh2YWwpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuXG4vKipcbiAqIFRoZSBpbnRlcmZhY2UgZm9yIGlucHV0IHByb3ZpZGVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbnB1dFByb3ZpZGVyIHtcbiAgLyoqXG4gICAqIEdldCB0aGUgbmV4dCBpbnB1dCBhcyBhIGNvcHkuIFRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2UgdGhlIGRhdGEgbWlnaHRcbiAgICogZ2V0IHVwbG9hZGVkIHRvIHRoZSBHUFUgYW5kIG1vZGlmeSB0aGUgb3JpZ2luYWwgZGF0YS5cbiAgICogQHBhcmFtIG1hdGggTkRBcnJheU1hdGhcbiAgICovXG4gIGdldE5leHRDb3B5KG1hdGg6IE5EQXJyYXlNYXRoKTogTkRBcnJheTtcbiAgLyoqXG4gICAqIERpc3Bvc2UgdGhlIGlucHV0IGNvcHkuXG4gICAqIEBwYXJhbSBtYXRoIE5EQXJyYXlNYXRoXG4gICAqIEBwYXJhbSBjb3B5IFRoZSBjb3B5IHByb3ZpZGVkIGZyb20gZ2V0TmV4dENvcHlcbiAgICovXG4gIGRpc3Bvc2VDb3B5KG1hdGg6IE5EQXJyYXlNYXRoLCBjb3B5OiBOREFycmF5KTogdm9pZDtcbn1cblxuLyoqXG4gKiBBIGNvbW1vbiBpbnRlcmZhY2UgZm9yIHNodWZmbGVkIGlucHV0IHByb3ZpZGVyIGJ1aWxkZXJzLiBUaGlzIHJldHVybnNcbiAqIElucHV0UHJvdmlkZXJzIHRoYXQgYXJlIHN5bmNocm9uaXplZC5cbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTaHVmZmxlZElucHV0UHJvdmlkZXJCdWlsZGVyIHtcbiAgZ2V0SW5wdXRQcm92aWRlcnMoKTogSW5wdXRQcm92aWRlcltdO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciBpbXBsZW1lbnRzXG4gICAgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIHByb3RlY3RlZCBzaHVmZmxlZEluZGljZXM6IFVpbnQzMkFycmF5O1xuICBwcm90ZWN0ZWQgbnVtSW5wdXRzOiBudW1iZXI7XG5cbiAgcHJvdGVjdGVkIGlkeCA9IDA7XG4gIC8vIENvdW50ZXIgZm9yIGhvdyBtYW55IHRpbWVzIHRoZSBjdXJyZW50IGluZGV4IGhhcyBiZWVuIGNhbGxlZC4gUmVzZXRzIHRvIDBcbiAgLy8gd2hlbiBpdCByZWFjaGVzIHRoZSBudW1iZXIgb2YgaW5wdXRzLlxuICBwcm90ZWN0ZWQgaW5wdXRDb3VudGVyID0gMDtcbiAgcHJvdGVjdGVkIGVwb2NoID0gMDtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhbiBgSW5NZW1vcnlTaHVmZmxlZElucHV0UHJvdmlkZXJgLiBBbGwgb2YgdGhlIGlucHV0cyBtdXN0IGJlXG4gICAqIGluIG1lbW9yeS5cbiAgICogQHBhcmFtIGlucHV0cyBBbGwgb2YgdGhlIGlucHV0cywgc2l6ZTogW251bWJlciBvZiBpbnB1dHNdW251bWJlciBvZlxuICAgKiBleGFtcGxlc10uXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgaW5wdXRzOiBOREFycmF5W11bXSkge1xuICAgIHRoaXMuc2h1ZmZsZWRJbmRpY2VzID0gdXRpbC5jcmVhdGVTaHVmZmxlZEluZGljZXMoaW5wdXRzWzBdLmxlbmd0aCk7XG4gICAgdGhpcy5udW1JbnB1dHMgPSBpbnB1dHMubGVuZ3RoO1xuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBudW1iZXIgb2YgZXhhbXBsZXMgaW4gZWFjaCBpbnB1dCBtYXRjaGVzLlxuICAgIGNvbnN0IG51bUV4YW1wbGVzID0gdGhpcy5pbnB1dHNbMF0ubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5udW1JbnB1dHM7IGkrKykge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy5pbnB1dHNbaV0ubGVuZ3RoID09PSBudW1FeGFtcGxlcyxcbiAgICAgICAgICAnTnVtYmVyIG9mIGV4YW1wbGVzIG11c3QgbWF0Y2ggYWNyb3NzIGRpZmZlcmVudCBpbnB1dHMuJyk7XG4gICAgfVxuXG4gICAgLy8gTWFrZSBzdXJlIHRoZSBzaGFwZXMgd2l0aGluIGlucHV0cyBhbGwgbWF0Y2guXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm51bUlucHV0czsgaSsrKSB7XG4gICAgICBjb25zdCBpbnB1dFNoYXBlID0gdGhpcy5pbnB1dHNbaV1bMF0uc2hhcGU7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRoaXMuaW5wdXRzW2ldLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goaW5wdXRTaGFwZSwgdGhpcy5pbnB1dHNbaV1bal0uc2hhcGUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByb3RlY3RlZCBnZXRDdXJyZW50RXhhbXBsZUluZGV4KCk6IG51bWJlciB7XG4gICAgY29uc3QgcmV0dXJuSWR4ID0gdGhpcy5pZHg7XG5cbiAgICB0aGlzLmlucHV0Q291bnRlcisrO1xuICAgIGlmICh0aGlzLmlucHV0Q291bnRlciA+PSB0aGlzLm51bUlucHV0cykge1xuICAgICAgdGhpcy5pZHgrKztcbiAgICAgIHRoaXMuaW5wdXRDb3VudGVyID0gMDtcblxuICAgICAgaWYgKHRoaXMuaWR4ID49IHRoaXMuaW5wdXRzWzBdLmxlbmd0aCkge1xuICAgICAgICB0aGlzLmlkeCA9IDA7XG4gICAgICAgIHRoaXMuZXBvY2grKztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJldHVybklkeDtcbiAgfVxuXG4gIHByb3RlY3RlZCBnZXROZXh0SW5wdXQoaW5wdXRJZDogbnVtYmVyKTogTkRBcnJheSB7XG4gICAgY29uc3QgY3VycmVudEV4YW1wbGVJbmRleCA9IHRoaXMuZ2V0Q3VycmVudEV4YW1wbGVJbmRleCgpO1xuXG4gICAgcmV0dXJuIHRoaXMuaW5wdXRzW2lucHV0SWRdW3RoaXMuc2h1ZmZsZWRJbmRpY2VzW2N1cnJlbnRFeGFtcGxlSW5kZXhdXTtcbiAgfVxuXG4gIGdldEVwb2NoKCkge1xuICAgIHJldHVybiB0aGlzLmVwb2NoO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaW5wdXQgcHJvdmlkZXJzIHdoaWNoIHNodWZmbGUgdGhlIGlucHV0cyBhbmQgc3RheSBpbiBzeW5jLlxuICAgKi9cbiAgZ2V0SW5wdXRQcm92aWRlcnMoKTogSW5wdXRQcm92aWRlcltdIHtcbiAgICBjb25zdCBpbnB1dFByb3ZpZGVyczogSW5wdXRQcm92aWRlcltdID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubnVtSW5wdXRzOyBpKyspIHtcbiAgICAgIGlucHV0UHJvdmlkZXJzLnB1c2godGhpcy5nZXRJbnB1dFByb3ZpZGVyKGkpKTtcbiAgICB9XG4gICAgcmV0dXJuIGlucHV0UHJvdmlkZXJzO1xuICB9XG5cbiAgYWJzdHJhY3QgZ2V0SW5wdXRQcm92aWRlcihpbnB1dElkOiBudW1iZXIpOiBJbnB1dFByb3ZpZGVyO1xufVxuXG4vKipcbiAqIEFuIGluIENQVSBtZW1vcnkgU2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB0aGF0IHNodWZmbGVzIE5EQXJyYXlzIG9uIHRoZVxuICogQ1BVIGFuZCBrZWVwcyB0aGVtIG11dHVhbGx5IGluIHN5bmMuXG4gKi9cbmV4cG9ydCBjbGFzcyBJbkNQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgZXh0ZW5kc1xuICAgIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKSB7XG4gICAgY29uc3Qgc2h1ZmZsZWRJbnB1dFByb3ZpZGVyID0gdGhpcztcblxuICAgIHJldHVybiB7XG4gICAgICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXkge1xuICAgICAgICByZXR1cm4gTkRBcnJheS5saWtlKHNodWZmbGVkSW5wdXRQcm92aWRlci5nZXROZXh0SW5wdXQoaW5wdXRJZCkpO1xuICAgICAgfSxcbiAgICAgIGRpc3Bvc2VDb3B5KG1hdGg6IE5EQXJyYXlNYXRoLCBjb3B5OiBOREFycmF5KSB7XG4gICAgICAgIGNvcHkuZGlzcG9zZSgpO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn1cblxuLyoqXG4gKiBBbiBpbiBHUFUgbWVtb3J5IFNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgdGhhdCBzaHVmZmxlcyBOREFycmF5cyBvbiB0aGVcbiAqIEdQVSBhbmQga2VlcHMgdGhlbSBtdXR1YWxseSBpbiBzeW5jLiBUaGlzIGlzIG1vcmUgcGVyZm9ybWFudCB0aGFuIHRoZSBDUFVcbiAqIHZlcnNpb24gYXMgdGV4dHVyZXMgd2lsbCBzdGF5IGluIG1lbW9yeSwgaG93ZXZlciB0aGlzIGlzIG1vcmUgR1BVIG1lbW9yeVxuICogaW50ZW5zaXZlIGFzIGl0IGtlZXBzIHRleHR1cmVzIHJlc2lkZW50IGluIEdQVSBtZW1vcnkuXG4gKi9cbmV4cG9ydCBjbGFzcyBJbkdQVU1lbW9yeVNodWZmbGVkSW5wdXRQcm92aWRlckJ1aWxkZXIgZXh0ZW5kc1xuICAgIEluTWVtb3J5U2h1ZmZsZWRJbnB1dFByb3ZpZGVyQnVpbGRlciB7XG4gIGdldElucHV0UHJvdmlkZXIoaW5wdXRJZDogbnVtYmVyKSB7XG4gICAgY29uc3Qgc2h1ZmZsZWRJbnB1dFByb3ZpZGVyID0gdGhpcztcblxuICAgIHJldHVybiB7XG4gICAgICBnZXROZXh0Q29weShtYXRoOiBOREFycmF5TWF0aCk6IE5EQXJyYXkge1xuICAgICAgICByZXR1cm4gbWF0aC5jbG9uZShzaHVmZmxlZElucHV0UHJvdmlkZXIuZ2V0TmV4dElucHV0KGlucHV0SWQpKTtcbiAgICAgIH0sXG4gICAgICBkaXNwb3NlQ29weShtYXRoOiBOREFycmF5TWF0aCwgY29weTogTkRBcnJheSkge1xuICAgICAgICBjb3B5LmRpc3Bvc2UoKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuLyoqIEEgbm9kZSdzIGFjdGl2YXRpb24gZnVuY3Rpb24gYW5kIGl0cyBkZXJpdmF0aXZlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQ8VCBleHRlbmRzIE5EQXJyYXk+KG1hdGg6IE5EQXJyYXlNYXRoLCBpbnB1dDogVCk6IFQ7XG4gIGRlcjxUIGV4dGVuZHMgTkRBcnJheT4obWF0aDogTkRBcnJheU1hdGgsIGlucHV0OiBULCBvdXRwdXQ6IFQpOiBUO1xufVxuXG5leHBvcnQgY2xhc3MgVGFuSEZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC50YW5oKHgpO1xuICAgIH0pO1xuICB9XG5cbiAgZGVyKG1hdGg6IE5EQXJyYXlNYXRoLCB4OiBOREFycmF5LCB5OiBOREFycmF5KSB7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgICAgY29uc3QgeVNxdWFyZWQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHksIHkpO1xuICAgICAgLy8gMSAtIHleMi5cbiAgICAgIHJldHVybiBtYXRoLnNjYWxhck1pbnVzQXJyYXkoU2NhbGFyLk9ORSwgeVNxdWFyZWQpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBSZUxVRnVuYyBpbXBsZW1lbnRzIEFjdGl2YXRpb25GdW5jdGlvbiB7XG4gIG91dHB1dChtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIHJldHVybiBtYXRoLnJlbHUoeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5zdGVwKHgpO1xuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTaWdtb2lkRnVuYyBpbXBsZW1lbnRzIEFjdGl2YXRpb25GdW5jdGlvbiB7XG4gIG91dHB1dChtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIHJldHVybiBtYXRoLnNpZ21vaWQoeCk7XG4gICAgfSk7XG4gIH1cblxuICBkZXIobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXksIHk6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICAvLyB5ICogKDEgLSB5KSA9IHkgLSB5XjJcbiAgICAgIGNvbnN0IHlTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh5LCB5KTtcbiAgICAgIHJldHVybiBtYXRoLnN1Yih5LCB5U3F1YXJlZCk7XG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIFNxdWFyZUZ1bmMgaW1wbGVtZW50cyBBY3RpdmF0aW9uRnVuY3Rpb24ge1xuICBvdXRwdXQobWF0aDogTkRBcnJheU1hdGgsIHg6IE5EQXJyYXkpIHtcbiAgICByZXR1cm4gbWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICByZXR1cm4gbWF0aC5lbGVtZW50V2lzZU11bCh4LCB4KTtcbiAgICB9KTtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDogTkRBcnJheSwgeTogTkRBcnJheSkge1xuICAgIHJldHVybiBtYXRoLnNjb3BlKCgpID0+IHtcbiAgICAgIC8vIGR5L2R4ID0gMip4LlxuICAgICAgcmV0dXJuIG1hdGguc2NhbGFyVGltZXNBcnJheShTY2FsYXIuVFdPLCB4KTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0Q29uY2F0M0RTaGFwZXNNYXRjaChcbiAgICB4MVNoYXBlOiBudW1iZXJbXSwgeDJTaGFwZTogbnVtYmVyW10sIGF4aXM6IG51bWJlcixcbiAgICBlcnJvck1lc3NhZ2VQcmVmaXggPSAnJykge1xuICB1dGlsLmFzc2VydChcbiAgICAgIHgxU2hhcGUubGVuZ3RoID09PSAzLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgJ0NvbmNhdDNEIHgxIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG4gIHV0aWwuYXNzZXJ0KFxuICAgICAgeDJTaGFwZS5sZW5ndGggPT09IDMsXG4gICAgICBlcnJvck1lc3NhZ2VQcmVmaXggKyAnQ29uY2F0M0QgeDIgc2hhcGUgc2hvdWxkIGJlIG9mIHJhbmsgMy4nKTtcblxuICB1dGlsLmFzc2VydChcbiAgICAgIGF4aXMgPj0gMCAmJiBheGlzIDwgMywgJ0F4aXMgZm9yIGNvbmNhdDNEIG11c3QgYmUgYmV0d2VlbiAwIGFuZCAyLicpO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgMzsgaSsrKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIChpID09PSBheGlzKSB8fCAoeDFTaGFwZVtpXSA9PT0geDJTaGFwZVtpXSksXG4gICAgICAgIGVycm9yTWVzc2FnZVByZWZpeCArXG4gICAgICAgICAgICBgU2hhcGUgKCR7eDFTaGFwZX0pIGRvZXMgbm90IG1hdGNoICgke3gyU2hhcGV9KSBhbG9uZyBgICtcbiAgICAgICAgICAgIGBub24tY29uY2F0ZW5hdGVkIGF4aXMuYCk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVDb25jYXQzRE91dHB1dFNoYXBlKFxuICAgIHgxU2hhcGU6IG51bWJlcltdLCB4MlNoYXBlOiBudW1iZXJbXSxcbiAgICBheGlzOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICB1dGlsLmFzc2VydCh4MVNoYXBlLmxlbmd0aCA9PT0gMywgJ0NvbmNhdDNEIHgxIHNoYXBlIHNob3VsZCBiZSBvZiByYW5rIDMuJyk7XG4gIHV0aWwuYXNzZXJ0KHgyU2hhcGUubGVuZ3RoID09PSAzLCAnQ29uY2F0M0QgeDJzaGFwZSBzaG91bGQgYmUgb2YgcmFuayAzLicpO1xuXG4gIGNvbnN0IG91dHB1dFNoYXBlID0geDFTaGFwZS5zbGljZSgpO1xuICBvdXRwdXRTaGFwZVtheGlzXSArPSB4MlNoYXBlW2F4aXNdO1xuICByZXR1cm4gb3V0cHV0U2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgIGlucHV0U2hhcGVSb3dDb2xEZXB0aDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemU6IG51bWJlcixcbiAgICBkZXB0aDogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZD86IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSB7XG4gIGlmICh6ZXJvUGFkID09IG51bGwpIHtcbiAgICB6ZXJvUGFkID0gY29tcHV0ZURlZmF1bHRQYWQoaW5wdXRTaGFwZVJvd0NvbERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSk7XG4gIH1cbiAgY29uc3QgaW5wdXRSb3dzID0gaW5wdXRTaGFwZVJvd0NvbERlcHRoWzBdO1xuICBjb25zdCBpbnB1dENvbHMgPSBpbnB1dFNoYXBlUm93Q29sRGVwdGhbMV07XG4gIGNvbnN0IG91dHB1dFJvd3MgPSAoaW5wdXRSb3dzIC0gZmllbGRTaXplICsgMiAqIHplcm9QYWQpIC8gc3RyaWRlICsgMTtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB1dGlsLmlzSW50KG91dHB1dFJvd3MpLFxuICAgICAgYFRoZSBvdXRwdXQgIyBvZiByb3dzICgke291dHB1dFJvd3N9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSB0aGUgYCArXG4gICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuXG4gIGNvbnN0IG91dHB1dENvbHMgPSAoaW5wdXRDb2xzIC0gZmllbGRTaXplICsgMiAqIHplcm9QYWQpIC8gc3RyaWRlICsgMTtcbiAgdXRpbC5hc3NlcnQoXG4gICAgICB1dGlsLmlzSW50KG91dHB1dENvbHMpLFxuICAgICAgYFRoZSBvdXRwdXQgIyBvZiBjb2x1bW5zICgke291dHB1dENvbHN9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSBgICtcbiAgICAgICAgICBgdGhlIHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuXG4gIHJldHVybiBbb3V0cHV0Um93cywgb3V0cHV0Q29scywgZGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgaW5wdXRTaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmaWVsZFNpemU6IG51bWJlcixcbiAgICBzdHJpZGU6IG51bWJlcik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLmZsb29yKChpbnB1dFNoYXBlWzBdICogKHN0cmlkZSAtIDEpIC0gc3RyaWRlICsgZmllbGRTaXplKSAvIDIpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZVRleFNoYXBlRnJvbTNEKFxuICAgIHNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW3NoYXBlUm93Q29sRGVwdGhbMF0sIHNoYXBlUm93Q29sRGVwdGhbMV0gKiBzaGFwZVJvd0NvbERlcHRoWzJdXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVXZWlnaHRzU2hhcGU0RChcbiAgICBpbnB1dERlcHRoOiBudW1iZXIsIG91dHB1dERlcHRoOiBudW1iZXIsXG4gICAgZlNpemU6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtmU2l6ZSwgZlNpemUsIGlucHV0RGVwdGgsIG91dHB1dERlcHRoXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoXG4gICAgaW5wdXREZXB0aDogbnVtYmVyLCBvdXRwdXREZXB0aDogbnVtYmVyLFxuICAgIGZpZWxkU2l6ZTogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbZmllbGRTaXplICogZmllbGRTaXplICogaW5wdXREZXB0aCwgb3V0cHV0RGVwdGhdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZUJpYXNlc1RleFNoYXBlKG91dHB1dERlcHRoOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFsxLCBvdXRwdXREZXB0aF07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlRGlsYXRlZFJDKFxuICAgIHJjOiBbbnVtYmVyLCBudW1iZXJdLCBvcmlnU3RyaWRlOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3Qgcm93c0RpbGF0ZWQgPSAocmNbMF0gLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICBjb25zdCBjb2xzRGlsYXRlZCA9IChyY1sxXSAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gIHJldHVybiBbcm93c0RpbGF0ZWQsIGNvbHNEaWxhdGVkXTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlU2hhcGVzKFxuICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGNvbnN0IHNyY0FyZWEgPSBzb3VyY2VTaXplWzBdICogc291cmNlU2l6ZVsxXTtcbiAgY29uc3QgZHN0QXJlYSA9IGRlc3RTaXplWzBdICogZGVzdFNpemVbMV07XG4gIGlmIChzcmNBcmVhICE9PSBkc3RBcmVhKSB7XG4gICAgY29uc3Qgc3JjU3RyID0gJ1snICsgc291cmNlU2l6ZVswXSArICcsICcgKyBzb3VyY2VTaXplWzFdICsgJ10nO1xuICAgIGNvbnN0IGRzdFN0ciA9ICdbJyArIGRlc3RTaXplWzBdICsgJywgJyArIGRlc3RTaXplWzFdICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ2NvcHkyRCBzaGFwZXMgaGF2ZSBkaWZmZXJlbnQgYXJlYXM6XFxuICBzb3VyY2VTaXplICcgKyBzcmNTdHIgK1xuICAgICAgICAnLCBhcmVhICcgKyBzcmNBcmVhICsgJ1xcbiAgZGVzdFNpemUgJyArIGRzdFN0ciArICcsIGFyZWEgJyArIGRzdEFyZWEpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuLyoqXG4gKiBBbiBlcnJvciBmdW5jdGlvbiBhbmQgaXRzIGRlcml2YXRpdmUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRWxlbWVudFdpc2VDb3N0RnVuY3Rpb24ge1xuICBjb3N0PFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgeDE6IFQsIHgyOiBUKTogVDtcbiAgZGVyPFQgZXh0ZW5kcyBOREFycmF5PihtYXRoOiBOREFycmF5TWF0aCwgeDE6IFQsIHgyOiBUKTogVDtcbiAgZGlzcG9zZSgpOiB2b2lkO1xufVxuXG5leHBvcnQgY2xhc3MgU3F1YXJlQ29zdEZ1bmMgaW1wbGVtZW50cyBFbGVtZW50V2lzZUNvc3RGdW5jdGlvbiB7XG4gIHByaXZhdGUgaGFsZk9uZSA9IFNjYWxhci5uZXcoMC41KTtcblxuICBjb3N0KG1hdGg6IE5EQXJyYXlNYXRoLCB4MTogTkRBcnJheSwgeDI6IE5EQXJyYXkpOiBOREFycmF5IHtcbiAgICBjb25zdCBkaWZmID0gbWF0aC5zdWIoeDEsIHgyKTtcbiAgICBjb25zdCBkaWZmU3F1YXJlZCA9IG1hdGguZWxlbWVudFdpc2VNdWwoZGlmZiwgZGlmZik7XG4gICAgY29uc3QgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHRoaXMuaGFsZk9uZSwgZGlmZlNxdWFyZWQpO1xuXG4gICAgZGlmZi5kaXNwb3NlKCk7XG4gICAgZGlmZlNxdWFyZWQuZGlzcG9zZSgpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGRlcihtYXRoOiBOREFycmF5TWF0aCwgeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogTkRBcnJheSB7XG4gICAgcmV0dXJuIG1hdGguc3ViKHgxLCB4Mik7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIHRoaXMuaGFsZk9uZS5kaXNwb3NlKCk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyZF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuXG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IHR5cGUgU2NvcGVSZXN1bHQgPSBOREFycmF5W118TkRBcnJheXx2b2lkO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTkRBcnJheU1hdGgge1xuICBwcml2YXRlIG5kYXJyYXlTY29wZXM6IE5EQXJyYXlbXVtdID0gW107XG4gIHByaXZhdGUgYWN0aXZlU2NvcGU6IE5EQXJyYXlbXTtcblxuICBwcml2YXRlIG5kYXJyYXlzVG9LZWVwOiBOREFycmF5W11bXSA9IFtdO1xuICBwcml2YXRlIGFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXA6IE5EQXJyYXlbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gc2FmZU1vZGUgSW4gc2FmZSBtb2RlLCB5b3UgbXVzdCB1c2UgbWF0aCBvcGVyYXRpb25zIGluc2lkZVxuICAgKiBhIG1hdGguc2NvcGUoKSB3aGljaCB3aWxsIGF1dG9tYXRpY2FsbHkgY2xlYW4gdXAgaW50ZXJtZWRpYXRlIE5EQXJyYXlzLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBzYWZlTW9kZTogYm9vbGVhbikge31cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IG1hdGggc2NvcGUuIFB1dCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucyBpbnNpZGUgYSBzY29wZVxuICAgKiBmdW5jdGlvbiBjbG9zdXJlIHNvIHRoYXQgdGhlIGxpYnJhcnkgYXV0b21hdGljYWxseSBjbGVhbnMgdXAgTkRBcnJheXNcbiAgICogZnJvbSBpbnRlcm1lZGlhdGUgbWF0aCBvcGVyYXRpb25zLiBZb3UgbXVzdCBjcmVhdGUgYSBzY29wZSBpbiBzYWZlIG1vZGVcbiAgICogdG8gY2FsbCBtYXRoIG9wZXJhdGlvbnMuIElmIGEgcmVzdWx0IGlzIHJldHVybmVkIGZyb20gdGhlIHNjb3BlLCBpdCB3aWxsXG4gICAqIGFsc28gYmUgdHJhY2tlZCwgd2hpY2ggbWVhbnMgdGhlcmUgbXVzdCBiZSB5ZXQgYW5vdGhlciB3cmFwcGluZyBzY29wZS5cbiAgICogQHBhcmFtIHNjb3BlRm4gVGhlIGZ1bmN0aW9uIHRvIGV4ZWN1dGUgd2l0aCBjaGFpbmVkIG1hdGggb3BlcmF0aW9ucy5cbiAgICovXG4gIHNjb3BlPFQgZXh0ZW5kcyBTY29wZVJlc3VsdD4oXG4gICAgICBzY29wZUZuOlxuICAgICAgICAgIChrZWVwOiA8VDEgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMSkgPT4gVDEsXG4gICAgICAgICAgIHRyYWNrOiA8VDIgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUMikgPT4gVDIpID0+IFQpIHtcbiAgICB0aGlzLnN0YXJ0U2NvcGUoKTtcblxuICAgIGNvbnN0IGtlZXBGbiA9IDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQgPT4gdGhpcy5rZWVwKG5kYXJyYXkpO1xuICAgIGNvbnN0IHRyYWNrRm4gPSA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUID0+IHRoaXMudHJhY2sobmRhcnJheSk7XG4gICAgY29uc3QgcmVzdWx0ID0gc2NvcGVGbihrZWVwRm4sIHRyYWNrRm4pO1xuXG4gICAgdGhpcy5lbmRTY29wZShyZXN1bHQpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIGVuZFNjb3BlKCkgdG8gYWNoaWV2ZSB0aGUgc2FtZSBmdW5jdGlvbmFsaXR5XG4gICAqIGFzIHNjb3BlKCkgd2l0aG91dCB0aGUgbmVlZCBmb3IgYSBmdW5jdGlvbiBjbG9zdXJlLlxuICAgKi9cbiAgc3RhcnRTY29wZSgpIHtcbiAgICBjb25zdCBuZXdTY29wZTogTkRBcnJheVtdID0gW107XG4gICAgdGhpcy5uZGFycmF5U2NvcGVzLnB1c2gobmV3U2NvcGUpO1xuICAgIHRoaXMuYWN0aXZlU2NvcGUgPSBuZXdTY29wZTtcblxuICAgIGNvbnN0IG5ld05EQXJyYXlzVG9LZWVwOiBOREFycmF5W10gPSBbXTtcbiAgICB0aGlzLm5kYXJyYXlzVG9LZWVwLnB1c2gobmV3TkRBcnJheXNUb0tlZXApO1xuICAgIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCA9IG5ld05EQXJyYXlzVG9LZWVwO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuZCBhIHNjb3BlLiBVc2UgdGhpcyB3aXRoIHN0YXJ0U2NvcGUoKSB0byBhY2hpZXZlIHRoZSBzYW1lIGZ1bmN0aW9uYWxpdHlcbiAgICogYXMgc2NvcGUoKSB3aXRob3V0IHRoZSBuZWVkIGZvciBhIGZ1bmN0aW9uIGNsb3N1cmUuXG4gICAqL1xuICBlbmRTY29wZShyZXN1bHQ6IFNjb3BlUmVzdWx0KSB7XG4gICAgLy8gRGlzcG9zZSB0aGUgY3VycmVudCBzY29wZS5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuYWN0aXZlU2NvcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG5kYXJyYXkgPSB0aGlzLmFjdGl2ZVNjb3BlW2ldO1xuXG4gICAgICBpZiAodGhpcy5pc05EQXJyYXlEYXRhSW5MaXN0KG5kYXJyYXksIHRoaXMuYWN0aXZlU2NvcGVOREFycmF5c1RvS2VlcCkgfHxcbiAgICAgICAgICAocmVzdWx0ICE9IG51bGwgJiYgcmVzdWx0IGluc3RhbmNlb2YgTkRBcnJheSAmJlxuICAgICAgICAgICBuZGFycmF5LmdldERhdGEoKSA9PT0gKHJlc3VsdCBhcyBOREFycmF5KS5nZXREYXRhKCkpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgbmRhcnJheS5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgLy8gUG9wIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgIHRoaXMubmRhcnJheVNjb3Blcy5wb3AoKTtcbiAgICB0aGlzLmFjdGl2ZVNjb3BlID0gdGhpcy5uZGFycmF5U2NvcGVzLmxlbmd0aCA9PT0gMCA/XG4gICAgICAgIG51bGwhIDpcbiAgICAgICAgdGhpcy5uZGFycmF5U2NvcGVzW3RoaXMubmRhcnJheVNjb3Blcy5sZW5ndGggLSAxXTtcblxuICAgIC8vIFRyYWNrIHRoZSBjdXJyZW50IHJlc3VsdCBpbiB0aGUgcGFyZW50IHNjb3BlLlxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBOREFycmF5ICYmXG4gICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QocmVzdWx0LCB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXApKSB7XG4gICAgICB0aGlzLnRyYWNrKHJlc3VsdCk7XG4gICAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkpIHtcbiAgICAgIHJlc3VsdC5mb3JFYWNoKHIgPT4ge1xuICAgICAgICBpZiAociBpbnN0YW5jZW9mIE5EQXJyYXkgJiZcbiAgICAgICAgICAgICF0aGlzLmlzTkRBcnJheURhdGFJbkxpc3QociwgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwKSkge1xuICAgICAgICAgIHRoaXMudHJhY2socik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMubmRhcnJheXNUb0tlZXAucG9wKCk7XG4gICAgdGhpcy5hY3RpdmVTY29wZU5EQXJyYXlzVG9LZWVwID0gdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggPT09IDAgP1xuICAgICAgICBudWxsISA6XG4gICAgICAgIHRoaXMubmRhcnJheXNUb0tlZXBbdGhpcy5uZGFycmF5c1RvS2VlcC5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHByaXZhdGUgaXNOREFycmF5RGF0YUluTGlzdChuZGFycmF5OiBOREFycmF5LCBuZGFycmF5TGlzdDogTkRBcnJheVtdKSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBuZGFycmF5TGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKG5kYXJyYXlMaXN0W2ldLmdldERhdGEoKSA9PT0gbmRhcnJheS5nZXREYXRhKCkpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBLZWVwcyBhbiBOREFycmF5IGluIHRoZSBjdXJyZW50IHNjb3BlIGZyb20gYmVpbmcgZGlzcG9zZWQgYXV0b21hdGljYWxseS5cbiAgICogQHBhcmFtIHJlc3VsdCBUaGUgTkRBcnJheSB0byBrZWVwIGZyb20gYmVpbmcgZGlzcG9zZWQuXG4gICAqL1xuICBrZWVwPFQgZXh0ZW5kcyBOREFycmF5PihyZXN1bHQ6IFQpOiBUIHtcbiAgICBpZiAodGhpcy5hY3RpdmVTY29wZSA9PSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5zYWZlTW9kZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnWW91IGFyZSB1c2luZyBtYXRoIGluIHNhZmUgbW9kZS4gRW5jbG9zZSBhbGwgJyArXG4gICAgICAgICAgICAnbWF0aC5tZXRob2QoKSBjYWxscyBpbnNpZGUgYSBzY29wZTogJyArXG4gICAgICAgICAgICAnbWF0aC5zY29wZSgoKSA9PiB7bWF0aC5tZXRob2QoKTsuLi59KSB0byBhdm9pZCBtZW1vcnkgJyArXG4gICAgICAgICAgICAnbGVha3MuJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICB0aGlzLmFjdGl2ZVNjb3BlTkRBcnJheXNUb0tlZXAucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogVHJhY2tzIGFuIE5EQXJyYXkgaW4gdGhlIGN1cnJlbnQgc2NvcGUgdG8gYmUgYXV0b21hdGljYWxseSBjbGVhbmVkIHVwIHdoZW5cbiAgICogdGhlIGN1cnJlbnQgc2NvcGUgZW5kcywgYW5kIHJldHVybnMgdGhlIHZhbHVlLlxuICAgKiBAcGFyYW0gcmVzdWx0IFRoZSBOREFycmF5IHRvIHRyYWNrIGluIHRoZSBjdXJyZW50IHNjb3BlLlxuICAgKi9cbiAgdHJhY2s8VCBleHRlbmRzIE5EQXJyYXk+KHJlc3VsdDogVCk6IFQge1xuICAgIGlmICh0aGlzLmFjdGl2ZVNjb3BlID09IG51bGwpIHtcbiAgICAgIGlmICh0aGlzLnNhZmVNb2RlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdZb3UgYXJlIHVzaW5nIG1hdGggaW4gc2FmZSBtb2RlLiBFbmNsb3NlIGFsbCAnICtcbiAgICAgICAgICAgICdtYXRoLm1ldGhvZCgpIGNhbGxzIGluc2lkZSBhIHNjb3BlOiAnICtcbiAgICAgICAgICAgICdtYXRoLnNjb3BlKCgpID0+IHttYXRoLm1ldGhvZCgpOy4uLn0pIHRvIGF2b2lkIG1lbW9yeSAnICtcbiAgICAgICAgICAgICdsZWFrcy4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIHRoaXMuYWN0aXZlU2NvcGUucHVzaChyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGRvdCBwcm9kdWN0IG9mIHR3byBtYXRyaWNlcywgQSAqIEIuIFRoZXNlIG11c3QgYmUgbWF0cmljZXMsXG4gICAqIHVzZSBtYXRyaXhUaW1lc1ZlY3RvciBhbmQgdmVjdG9yVGltZXNNYXRyaXgsIGRvdFByb2R1Y3QsIGFuZCBvdXRlclByb2R1Y3RcbiAgICogaW4gb3RoZXIgY2FzZXMuXG4gICAqIEBwYXJhbSBhIEZpcnN0IG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBiIFNlY29uZCBtYXRyaXggaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gYU9yaWVudGF0aW9uIFRoZSBNYXRyaXhPcmllbnRhdGlvbiBvZiBBLiBJZiB1c2luZyBUUkFOU1BPU0VELCB3aWxsXG4gICAqIGNvbXB1dGUgQV5UICogQi5cbiAgICogQHBhcmFtIGJPcmllbnRhdGlvbiBUaGUgTWF0cml4T3JpZW50YXRpb24gb2YgQi4gSWYgdXNpbmcgVFJBTlNQT1NFRCwgd2lsbFxuICAgKiBjb21wdXRlIEEgKiBCXlQuXG4gICAqL1xuICBtYXRNdWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEFycmF5MkQge1xuICAgIGNvbnN0IGlubmVyU2hhcGVBID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcbiAgICBjb25zdCBpbm5lclNoYXBlQiA9XG4gICAgICAgIChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYi5zaGFwZVswXSA6IGIuc2hhcGVbMV07XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYS5yYW5rID09PSAyICYmIGIucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIG1hdE11bDogaW5wdXRzIG11c3QgYmUgcmFuayAyLCBnb3QgcmFua3MgJHthLnJhbmt9YCArXG4gICAgICAgICAgICBgYW5kICR7Yi5yYW5rfS5gKTtcblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBpbm5lclNoYXBlQSA9PT0gaW5uZXJTaGFwZUIsXG4gICAgICAgIGBFcnJvciBpbiBtYXRNdWw6IGlubmVyIHNoYXBlcyAoJHtpbm5lclNoYXBlQX0pIGFuZCAoYCArXG4gICAgICAgICAgICBgJHtpbm5lclNoYXBlQn0pIG9mIE5EQXJyYXlzIHdpdGggc2hhcGVzICR7YS5zaGFwZX0gYW5kIGAgK1xuICAgICAgICAgICAgYCR7Yi5zaGFwZX0gYW5kIG9yaWVudGF0aW9ucyAke01hdHJpeE9yaWVudGF0aW9uW2FPcmllbnRhdGlvbl19YCArXG4gICAgICAgICAgICBgIGFuZCAke01hdHJpeE9yaWVudGF0aW9uW2JPcmllbnRhdGlvbl19IG11c3QgbWF0Y2guYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1hdE11bEludGVybmFsKGEsIGIsIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1hdE11bEludGVybmFsKFxuICAgICAgYTogQXJyYXkyRCwgYjogQXJyYXkyRCwgYU9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbixcbiAgICAgIGJPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24pOiBBcnJheTJEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSB2ZWN0b3IgYW5kIGEgbWF0cml4LCB2ICogQi5cbiAgICogQHBhcmFtIHYgVGhlIHZlY3RvciBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqL1xuICB2ZWN0b3JUaW1lc01hdHJpeCh2OiBBcnJheTFELCBtYXRyaXg6IEFycmF5MkQpOiBBcnJheTFEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgcmFuayAxLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYHJhbmsgJHt2LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBtYXRyaXgucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCBiZSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMF0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgKCR7di5zaXplfSkgYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBpbm5lciBkaW1lbnNpb24gb2Ygc2Vjb25kIHJhbmsgMiBpbnB1dCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7bWF0cml4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYuYXMyRCgxLCB2LnNpemUpLCBtYXRyaXgpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgYSBtYXRyaXggYW5kIHZlY3RvciwgQSAqIHYuXG4gICAqIEBwYXJhbSBtYXRyaXggVGhlIG1hdHJpeCBpbiBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2IFRoZSB2ZWN0b3IgaW4gZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgbWF0cml4VGltZXNWZWN0b3IobWF0cml4OiBBcnJheTJELCB2OiBBcnJheTFEKTogQXJyYXkxRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHYucmFuayA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHZlY3RvclRpbWVzTWF0cml4OiBzZWNvbmQgaW5wdXQgbXVzdCByYW5rIDEsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke3YucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1hdHJpeC5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gdmVjdG9yVGltZXNNYXRyaXg6IGZpcnN0IGlucHV0IG11c3QgYmUgYSByYW5rIDIsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke21hdHJpeC5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdi5zaXplID09PSBtYXRyaXguc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiB2ZWN0b3JUaW1lc01hdHJpeDogc2l6ZSBvZiBmaXJzdCByYW5rIDEgaW5wdXQgJHt2LnNpemV9IGAgK1xuICAgICAgICAgICAgYG11c3QgbWF0Y2ggaW5uZXIgZGltZW5zaW9uIG9mIHNlY29uZCByYW5rIDIgaW5wdXQsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgc2hhcGUgJHttYXRyaXguc2hhcGV9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKG1hdHJpeCwgdi5hczJEKHYuc2l6ZSwgMSkpLmFzMUQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgZG90IHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxICogdjIuXG4gICAqIEBwYXJhbSB2MSBUaGUgZmlyc3QgdmVjdG9yIGluIHRoZSBkb3QgcHJvZHVjdCBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSB2MiBUaGUgc2Vjb25kIHZlY3RvciBpbiB0aGUgZG90IHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKi9cbiAgZG90UHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB2MS5yYW5rID09PSAxICYmIHYyLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBkb3RQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEuc2l6ZSA9PT0gdjIuc2l6ZSxcbiAgICAgICAgYEVycm9yIGluIGRvdFByb2R1Y3Q6IHNpemUgb2YgaW5wdXRzICgke3YxLnNpemV9KSBhbmQgKGAgK1xuICAgICAgICAgICAgYCR7djIuc2l6ZX0pIG11c3QgbWF0Y2guYCk7XG4gICAgcmV0dXJuIHRoaXMubWF0TXVsKHYxLmFzMkQoMSwgdjEuc2l6ZSksIHYyLmFzMkQodjIuc2l6ZSwgMSkpLmFzU2NhbGFyKCk7XG4gIH1cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG91dGVyIHByb2R1Y3Qgb2YgdHdvIHZlY3RvcnMsIHYxIGFuZCB2Mi5cbiAgICogQHBhcmFtIHYxIFRoZSBmaXJzdCB2ZWN0b3IgaW4gdGhlIG91dGVyIHByb2R1Y3Qgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0gdjIgVGhlIHNlY29uZCB2ZWN0b3IgaW4gdGhlIGRvdCBwcm9kdWN0IG9wZXJhdGlvbi5cbiAgICovXG4gIG91dGVyUHJvZHVjdCh2MTogQXJyYXkxRCwgdjI6IEFycmF5MUQpOiBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdjEucmFuayA9PT0gMSAmJiB2Mi5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gb3V0ZXJQcm9kdWN0OiBpbnB1dHMgbXVzdCBiZSByYW5rIDEsIGJ1dCBnb3QgcmFua3MgYCArXG4gICAgICAgICAgICBgJHt2MS5yYW5rfSBhbmQgJHt2Mi5yYW5rfS5gKTtcblxuICAgIHJldHVybiB0aGlzLm1hdE11bCh2MS5hczJEKHYxLnNpemUsIDEpLCB2Mi5hczJEKDEsIHYyLnNpemUpKTtcbiAgfVxuXG4gIC8vLy8vLy8vLy8vLy8vL1xuICAvLyBTaGFwZSBvcHMgLy9cbiAgLy8vLy8vLy8vLy8vLy8vXG5cbiAgLyoqXG4gICAqIENsb25lcyBhbiBOREFycmF5IG9mIGFueSBzaGFwZS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIE5EQXJyYXkgdG8gY2xvbmUuXG4gICAqL1xuICBjbG9uZTxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBSZXNoYXBlcyBhbiBOREFycmF5IHRvIGEgbmV3IHNoYXBlLiBUaGUgc2l6ZSBvZiB0aGUgaW5wdXQgTkRBcnJheSBtdXN0XG4gICAqIG1hdGNoIHRoZSBzaXplIG9mIHRoZSByZXF1ZXN0ZWQgc2hhcGUuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmV3U2hhcGUgVGhlIG5ldyBzaGFwZSB0byByZXNoYXBlIHRoZSBOREFycmF5IHRvLiBNdXN0IGJlIHRoZSBzYW1lXG4gICAqIHNpemUgYXMgdGhlIE5EQXJyYXkuXG4gICAqL1xuICByZXNoYXBlPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG5kYXJyYXkuc2l6ZSA9PT0gdXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKSxcbiAgICAgICAgYEVycm9yIGluIHJlc2hhcGU6IG9sZCBzaXplICR7bmRhcnJheS5zaXplfSBtdXN0IG1hdGNoIG5ldyBzaXplIGAgK1xuICAgICAgICAgICAgYCR7dXRpbC5zaXplRnJvbVNoYXBlKG5ld1NoYXBlKX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZXNoYXBlSW50ZXJuYWw8VDEsIFQyPihuZGFycmF5LCBuZXdTaGFwZSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNoYXBlSW50ZXJuYWw8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgbmRhcnJheTogVDEsIG5ld1NoYXBlOiBudW1iZXJbXSk6IFQyO1xuXG4gIC8qKlxuICAgKiBFeHRyYWN0cyBhIHNsaWNlIGZyb20gYSBtYXRyaXguIFRoZSBvcGVyYXRpb24gZXh0cmFjZXMgYSBzbGljZSBmcm9tIGlucHV0XG4gICAqIHRoYXQgc3RhcnRzIGF0IGNvb3JkaW5hdGVzIGBiZWdpbmAgYW5kIGlzIG9mIHNpemUgYHNpemVgLlxuICAgKiBAcGFyYW0gaW5wdXQgVGhlIGlucHV0IG1hdHJpeCB0byBzbGljZSBmcm9tLlxuICAgKiBAcGFyYW0gYmVnaW4gVGhlIDJEIGNvb3JkaW5hdGVzIGluIHRoZSBpbnB1dCBtYXRyaXggdG8gc3RhcnQgdGhlIHNsaWNlXG4gICAqIGZyb20uXG4gICAqIEBwYXJhbSBzaXplIFRoZSBzaWNlIG9mIHRoZSAyRCB3aW5kb3cgdG8gc2xpY2UuXG4gICAqL1xuICBzbGljZTJEKGlucHV0OiBBcnJheTJELCBiZWdpbjogW251bWJlciwgbnVtYmVyXSwgc2l6ZTogW251bWJlciwgbnVtYmVyXSk6XG4gICAgICBBcnJheTJEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYmVnaW5bMF0gKyBzaXplWzBdIDw9IGlucHV0LnNoYXBlWzBdICYmXG4gICAgICAgICAgICBiZWdpblsxXSArIHNpemVbMV0gPD0gaW5wdXQuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBzbGljZTJEOiByZXF1ZXN0ZWQgc3RhcnQgcG9zaXRpb24gJHtiZWdpbn0gYW5kIHNpemUgYCArXG4gICAgICAgICAgICBgJHtzaXplfSB3b3VsZCBvdmVyZmxvdyBpbnB1dCBvZiBzaGFwZSAke2lucHV0LnNoYXBlfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNsaWNlMkRJbnRlcm5hbChpbnB1dCwgYmVnaW4sIHNpemUpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luOiBbbnVtYmVyLCBudW1iZXJdLCBzaXplOiBbbnVtYmVyLCBudW1iZXJdKTogQXJyYXkyRDtcblxuICAvKipcbiAgICogQ29waWVzIGEgd2luZG93IGZyb20gdGhlIGBzb3VyY2VgIG1hdHJpeCBzdGFydGluZyBhdCBgc291cmNlQmVnaW5gIGFuZCBpc1xuICAgKiBvZiBzaXplIGBzb3VyY2VTaXplYCB0byBhIHdpbmRvdyBpbiB0aGUgYGRlc3RgIG1hdHJpeCBzdGFydGluZyBhdFxuICAgKiBgZGVzdEJlZ2luYCBhbmQgaXMgb2Ygc2l6ZSBgZGVzdFNpemVgL1xuICAgKiBAcGFyYW0gc291cmNlIFRoZSBzb3VyY2UgbWF0cml4IHRvIGNvcHkgZnJvbS5cbiAgICogQHBhcmFtIHNvdXJjZUJlZ2luIFRoZSBjb29yZGluYXRlcyB0byBzdGFydCB0aGUgY29weSBmcm9tLlxuICAgKiBAcGFyYW0gc291cmNlU2l6ZSBUaGUgc2l6ZSBvZiB0aGUgY29weSB3aW5kb3cuXG4gICAqIEBwYXJhbSBkZXN0IFRoZSBkZXN0aW5hdGlvbiBtYXRyaXggdG8gY29weSB0by5cbiAgICogQHBhcmFtIGRlc3RCZWdpbiBUaGUgY29vcmRpbmF0ZXMgaW4gYGRlc3RgIHRvIGNvcHkgdG8uXG4gICAqIEBwYXJhbSBkZXN0U2l6ZSBUaGUgc2l6ZSBvZiB0aGUgZGVzdGluYXRpb24gd2luZG93LlxuICAgKi9cbiAgY29weTJEKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHNvdXJjZUJlZ2luWzBdICsgc291cmNlU2l6ZVswXSA8PSBzb3VyY2Uuc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIHNvdXJjZUJlZ2luWzFdICsgc291cmNlU2l6ZVsxXSA8PSBzb3VyY2Uuc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBzb3VyY2Ugc3RhcnQgcG9zaXRpb24gJHtzb3VyY2VCZWdpbn0gYCArXG4gICAgICAgICAgICBgYW5kIHNvdXJjZSBzaXplICR7c291cmNlU2l6ZX0gd291bGQgb3ZlcmZsb3cgc291cmNlIE5EQXJyYXlgICtcbiAgICAgICAgICAgIGBvZiBzaGFwZSAke3NvdXJjZS5zaGFwZX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRlc3RCZWdpblswXSArIGRlc3RTaXplWzBdIDw9IGRlc3Quc2hhcGVbMF0gJiZcbiAgICAgICAgICAgIGRlc3RCZWdpblsxXSArIGRlc3RTaXplWzFdIDw9IGRlc3Quc2hhcGVbMV0sXG4gICAgICAgIGBFcnJvciBpbiBjb3B5MkQ6IHJlcXVlc3RlZCBkZXN0IHN0YXJ0IHBvc2l0aW9uICR7ZGVzdEJlZ2lufSBgICtcbiAgICAgICAgICAgIGBhbmQgc291cmNlIHNpemUgJHtkZXN0U2l6ZX0gd291bGQgb3ZlcmZsb3cgZGVzdCBOREFycmF5IG9mYCArXG4gICAgICAgICAgICBgc2hhcGUgJHtkZXN0LnNoYXBlfS5gKTtcbiAgICBjb3B5MmRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplLCBkZXN0U2l6ZSk7XG5cbiAgICByZXR1cm4gdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgc291cmNlLCBzb3VyY2VCZWdpbiwgc291cmNlU2l6ZSwgZGVzdCwgZGVzdEJlZ2luLCBkZXN0U2l6ZSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvcHkyREludGVybmFsKFxuICAgICAgc291cmNlOiBBcnJheTJELCBzb3VyY2VCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNvdXJjZVNpemU6IFtudW1iZXIsIG51bWJlcl0sIGRlc3Q6IEFycmF5MkQsIGRlc3RCZWdpbjogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIGRlc3RTaXplOiBbbnVtYmVyLCBudW1iZXJdKTogdm9pZDtcblxuICAvKipcbiAgICogQ29uY2F0ZW5hdGVzIHR3byAzRCBuZGFycmF5cyBhbG9uZyBhIGdpdmVuIGF4aXMuXG4gICAqXG4gICAqIEZvciBleGFtcGxlLCBpZjpcbiAgICogQTogc2hhcGUoMiwgMSwgMykgPSB8IHIxLCBnMSwgYjEgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjIsIGcyLCBiMiB8XG4gICAqXG4gICAqIEI6IHNoYXBlKDIsIDEsIDMpID0gfCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBDID0gY29uY2F0M0QoQSwgQiwgYXhpcylcbiAgICpcbiAgICogaWYgYXhpcyA9IDA6XG4gICAqIEM6IHNoYXBlKDQsIDEsIDMpID0gfCByMSwgZzEsIGIxIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIgfFxuICAgKiAgICAgICAgICAgICAgICAgICAgIHwgcjMsIGczLCBiMyB8XG4gICAqICAgICAgICAgICAgICAgICAgICAgfCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogaWYgYXhpcyA9IDE6XG4gICAqIEM6IHNoYXBlKDIsIDIsIDMpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICB8IHIyLCBnMiwgYjIsIHI0LCBnNCwgYjQgfFxuICAgKlxuICAgKiBpZiBheGlzID0gMjpcbiAgICogQyA9IHNoYXBlKDIsIDEsIDYpID0gfCByMSwgZzEsIGIxLCByMywgZzMsIGIzIHxcbiAgICogICAgICAgICAgICAgICAgICAgICAgfCByMiwgZzIsIGIyLCByNCwgZzQsIGI0IHxcbiAgICpcbiAgICogQHBhcmFtIG5kYXJyYXkxIFRoZSBmaXJzdCBhcnJheSB0byBjb25jYXQuXG4gICAqIEBwYXJhbSBuZGFycmF5MiBUaGUgc2Vjb25kIGFycmF5IHRvIGNvbmF0LlxuICAgKiBAcGFyYW0gYXhpcyBUaGUgYXhpcyB0byBjb25jYXRlIGFsb25nLlxuICAgKi9cbiAgY29uY2F0M0QobmRhcnJheTE6IEFycmF5M0QsIG5kYXJyYXkyOiBBcnJheTNELCBheGlzOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25jYXQzZF91dGlsLmFzc2VydENvbmNhdDNEU2hhcGVzTWF0Y2goXG4gICAgICAgIG5kYXJyYXkxLnNoYXBlLCBuZGFycmF5Mi5zaGFwZSwgYXhpcywgJ0Vycm9yIGluIGNvbmNhdDNkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbmNhdDNESW50ZXJuYWwobmRhcnJheTEsIG5kYXJyYXkyLCBheGlzKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbmNhdDNESW50ZXJuYWwoXG4gICAgICBuZGFycmF5MTogQXJyYXkzRCwgbmRhcnJheTI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Q7XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBSZWR1Y3Rpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRoZSBsb2coc3VtKGUgXiB4KSkgZm9yIGVhY2ggeCBpbiB0aGUgaW5wdXQgbmRhcnJheS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkgdG8gY29tcHV0ZSB0aGUgbG9nU3VtRXhwIG92ZXIuXG4gICAqL1xuICBsb2dTdW1FeHAobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dTdW1FeHBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzdW0gb2YgYWxsIHRoZSBlbnRyaWVzIGluIHRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheSB0byBjb21wdXRlIHRoZSBzdW0gb3Zlci5cbiAgICovXG4gIHN1bShuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN1bUludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3VtSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWluaW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01pbihuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01pbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGZsYXR0ZW5lZCBpbmRleCBvZiB0aGUgbWF4aW11bSBlbGVtZW50IGluIHRoZSBuZGFycmF5LlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGFyZ01heChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFyZ01heEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJnTWF4SW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogUmV0dXJucyBhIDEgaWYgdGhlIGFyZ01heCBvZiB4MSBhbmQgeDIgYXJlIHRoZSBzYW1lLCBvdGhlcndpc2UgMC5cbiAgICogQHBhcmFtIHgxIFRoZSBmaXJzdCBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0geDIgVGhlIHNlY29uZCBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgYXJnTWF4RXF1YWxzKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaCh4MS5zaGFwZSwgeDIuc2hhcGUsICdFcnJvciBpbiBhcmdNYXhFcXVhbHM6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXJnTWF4RXF1YWxzSW50ZXJuYWwoeDEsIHgyKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhcjtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIHRvcCBLIHZhbHVlcyBhbmQgZmxhdHRlbmVkIGluZGljZXMuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKiBAcGFyYW0gayBIb3cgbWFueSB0b3AgdmFsdWVzIHRvIGNvbXB1dGUuXG4gICAqL1xuICB0b3BLKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6IHt2YWx1ZXM6IEFycmF5MUQsIGluZGljZXM6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgayA8PSBuZGFycmF5LnNpemUsXG4gICAgICAgIGBFcnJvciBpbiB0b3BLOiBrIHZhbHVlICgke2t9KSBtdXN0IGJlIGxlc3MgdGhhbiBzaXplIG9mIGlucHV0IGAgK1xuICAgICAgICAgICAgYG5kYXJyYXksIGdvdCBzaGFwZSAke25kYXJyYXkuc2hhcGV9LmApO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMudG9wS0ludGVybmFsKG5kYXJyYXksIGspO1xuICAgIHRoaXMudHJhY2socmVzdWx0LnZhbHVlcyk7XG4gICAgdGhpcy50cmFjayhyZXN1bHQuaW5kaWNlcyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdG9wS0ludGVybmFsKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6XG4gICAgICB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfTtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIG1pbmltdW0gdmFsdWUgZnJvbSB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbWluKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWluSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgbWF4aW11bSB2YWx1ZSBmcm9tIHRoZSBpbnB1dC5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBtYXgobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSBzb2Z0bWF4IG5vcm1hbGl6ZWQgdmVjdG9yIGZyb20gdGhlIGlucHV0IHZlY3Rvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IHZlY3Rvci5cbiAgICovXG4gIHNvZnRtYXgoeDogQXJyYXkxRCk6IEFycmF5MUQge1xuICAgIHJldHVybiB0aGlzLnNjb3BlKCgpID0+IHtcbiAgICAgIC8vIERvIGl0IGluIGxvZyBzcGFjZSBmb3IgbnVtZXJpY2FsIHN0YWJpbGl0eS5cbiAgICAgIC8vIGV4cChYIC0gbG9nU3VtRXhwKFgpKVxuICAgICAgY29uc3QgbHNlID0gdGhpcy5sb2dTdW1FeHAoeCk7XG4gICAgICBjb25zdCBsb2dSZXN1bHQgPSB0aGlzLmFycmF5TWludXNTY2FsYXIoeCwgbHNlKTtcbiAgICAgIHJldHVybiB0aGlzLmV4cChsb2dSZXN1bHQpO1xuICAgIH0pO1xuICB9XG5cbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyBFbGVtZW50LXdpc2Ugb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAvKipcbiAgICogU3dpdGNoZXMgZGltZW5zaW9ucyBvZiB0aGUgaW5wdXQgTkRBcnJheS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBuZXdEaW0gVGhlIG5ldyBpbmRpY2VzIHRoYXQgZGVmaW5lIHdoaWNoIHNoYXBlcyB2YWx1ZXMgdG8gc3dpdGNoLlxuICAgKi9cbiAgc3dpdGNoRGltPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBuZXdEaW06IG51bWJlcltdKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gbmV3RGltLmxlbmd0aCxcbiAgICAgICAgYEVycm9yIGluIHN3aXRjaERpbTogbGVuZ3RoIG9mIGlucHV0IHNoYXBlICR7YS5zaGFwZX0gYCArXG4gICAgICAgICAgICBgbXVzdCBtYXRjaCBzaXplIG9mIG5ld0RpbSBhcnJheSAke25ld0RpbX0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zd2l0Y2hEaW1JbnRlcm5hbChhLCBuZXdEaW0pKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIHBsdXMgTkRBcnJheSwgYyArIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBjICsgQS5cbiAgICogQHBhcmFtIGEgVGhlIE5EQXJyYXkgQSBpbiBjICsgQS5cbiAgICovXG4gIHNjYWxhclBsdXNBcnJheTxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMuc2l6ZSA9PT0gMSxcbiAgICAgICAgYEVycm9yIGluIHNjYWxhclBsdXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJQbHVzQXJyYXlJbnRlcm5hbChjLCBhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIG1pbnVzIE5EQXJyYXksIGMgLSBBLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGMgaW4gYyAtIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gYyAtIEEuXG4gICAqL1xuICBzY2FsYXJNaW51c0FycmF5PFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYy5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGFyTWludXNBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJNaW51c0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYSBzY2FsYXIgbWludXMgTkRBcnJheSwgQSAtIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IEEgaW4gQSAtIGMuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgYyBpbiBBIC0gYy5cbiAgICovXG4gIGFycmF5TWludXNTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheU1pbnVzU2NhbGFyOiBzZWNvbmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheU1pbnVzU2NhbGFySW50ZXJuYWwoYSwgYykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhcnJheU1pbnVzU2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYTogVCwgYzogU2NhbGFyKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgLTEgKiBBIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGEgVGhlIGlucHV0IGFycmF5LlxuICAgKi9cbiAgbmVnPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5uZWdJbnRlcm5hbChhKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVDtcblxuICAvKipcbiAgICogQWRkcyB0d28gTkRBcnJheXMgZWxlbWVudC13aXNlLCBBICsgQi4gSW5wdXRzIG11c3QgYmUgdGhlIHNhbWUgc2hhcGUuXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IHRvIGFkZCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBhZGQgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgYWRkPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gYWRkOiAnKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmFkZEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBTdWJ0cmFjdHMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSwgQSAtIEIuIElucHV0cyBtdXN0IGJlIHRoZSBzYW1lIHNoYXBlLlxuICAgKiBAcGFyYW0gYSBUaGUgZmlyc3QgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgTkRBcnJheSB0byBzdWJ0cmFjdCBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBzdWI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBzdWI6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuc3ViSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzdWJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIE11bHRpcGxpZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgKiBCLiBJbnB1dHMgbXVzdFxuICAgKiBiZSB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKiBAcGFyYW0gYiBUaGUgc2Vjb25kIE5EQXJyYXkgdG8gbXVsdGlwbHkgZWxlbWVudC13aXNlLlxuICAgKi9cbiAgZWxlbWVudFdpc2VNdWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBlbGVtZW50V2lzZU11bDogJyk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5lbGVtZW50V2lzZU11bEludGVybmFsKGEsIGIpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZWxlbWVudFdpc2VNdWxJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIERpdmlkZXMgdHdvIE5EQXJyYXlzIGVsZW1lbnQtd2lzZSAoaGFkYW1hcmQgcHJvZHVjdCksIEEgLyBCLiBJbnB1dHMgbXVzdCBiZVxuICAgKiB0aGUgc2FtZSBzaGFwZS5cbiAgICogQHBhcmFtIGEgVGhlIGZpcnN0IE5EQXJyYXkgdG8gZGl2aWRlIGVsZW1lbnQtd2lzZS5cbiAgICogQHBhcmFtIGIgVGhlIHNlY29uZCBOREFycmF5IHRvIGRpdmlkZSBlbGVtZW50LXdpc2UuXG4gICAqL1xuICBkaXZpZGU8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUsICdFcnJvciBpbiBkaXZpZGU6ICcpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZGl2aWRlSW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBkaXZpZGVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGFyIGRpdmlkZWQgYnkgYW4gTkRBcnJheSwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAvXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgdmFsdWUgaW4gYyAvIEEuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIGMgLyBBLlxuICAgKi9cbiAgc2NhbGFyRGl2aWRlZEJ5QXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsYXJEaXZpZGVkQnlBcnJheTogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgTkRBcnJheSBvZiByYW5rICR7Yy5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNjYWxhckRpdmlkZWRCeUFycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJEaXZpZGVkQnlBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGM6IFNjYWxhciwgYTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGFuIE5EQXJyYXkgZGl2aWRlZCBieSBhIHNjYWxhciwgYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgQSAvXG4gICAqIGMuXG4gICAqIEBwYXJhbSBhIFRoZSBOREFycmF5IHZhbHVlIGluIEEgLyBjLlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIHZhbHVlIGluIEEgLyBjLlxuICAgKi9cbiAgYXJyYXlEaXZpZGVkQnlTY2FsYXI8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogc2Vjb25kIGFyZ3VtZW50IG11c3QgYmUgcmFuayAwLCBgICtcbiAgICAgICAgICAgIGBidXQgZ290IE5EQXJyYXkgb2YgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5hcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsKGEsIGMpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBhOiBULCBjOiBTY2FsYXIpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBleHBvbmVudGlhbCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuIHkgPSBlIF4geFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIGV4cDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuZXhwSW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBleHBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIG5hdHVyYWwgbG9nYXJpdGhtIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZS4geSA9IGxuKHgpXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgbG9nPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5sb2dJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGxvZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgcmVjdGlmaWVkIGxpbmVhciBlbGVtZW50LXdpc2UsIG1heCh4LCAwKS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICByZWx1PFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5yZWx1SW50ZXJuYWwobmRhcnJheSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzaWdtb2lkIGVsZW1lbnQtd2lzZSwgeSA9IDEgLyAoMSArIGV4cCgteCkpLlxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHNpZ21vaWQ8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpZ21vaWRJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGh5cGVyYm9saWMgdGFuZ2VudCBvZiB0aGUgaW5wdXQgTkRBcnJheSBlbGVtZW50LXdpc2UuXG4gICAqIEBwYXJhbSBuZGFycmF5IFRoZSBpbnB1dCBOREFycmF5LlxuICAgKi9cbiAgdGFuaDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMudGFuaEludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgc2luIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IHNpbih4KS5cbiAgICogQHBhcmFtIG5kYXJyYXkgVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqL1xuICBzaW48VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnNpbkludGVybmFsKG5kYXJyYXkpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2luSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBzdGVwIG9mIHRoZSBpbnB1dCBOREFycmF5IGVsZW1lbnQtd2lzZSwgeSA9IDEgaWYgeCA+IDAgfCAwIGlmIHggPD1cbiAgICogMFxuICAgKiBAcGFyYW0gbmRhcnJheSBUaGUgaW5wdXQgTkRBcnJheS5cbiAgICovXG4gIHN0ZXA8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLnN0ZXBJbnRlcm5hbChuZGFycmF5KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IHN0ZXBJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQ7XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIGEgc2NhbGVkIGFycmF5IGFkZCBvcGVyYXRpb24sIGMxICogQSArIGMyICogQi5cbiAgICogQHBhcmFtIGMxIFRoZSBmaXJzdCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBhIFRoZSBmaXJzdCBOREFycmF5IGluIHRoZSBzY2FsZWQgYXJyYXkgYWRkIGNvbXB1dGF0aW9uLlxuICAgKiBAcGFyYW0gYzIgVGhlIHNlY29uZCBzY2FsYXIgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqIEBwYXJhbSBjYiBUaGUgc2Vjb25kIE5EQXJyYXkgaW4gdGhlIHNjYWxlZCBhcnJheSBhZGQgY29tcHV0YXRpb24uXG4gICAqL1xuICBzY2FsZWRBcnJheUFkZDxUIGV4dGVuZHMgTkRBcnJheT4oYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjMS5zaXplID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6IGZpcnN0IGFyZ3VtZW50IG11c3QgcmFuayAwLCBidXQgZ290IGAgK1xuICAgICAgICAgICAgYCByYW5rICR7YzEucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGMyLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBzY2FsZWRBcnJheUFkZDogdGhpcmQgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBnb3QgYCArXG4gICAgICAgICAgICBgTkRBcnJheSBvZiByYW5rICR7YzIucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChhLnNoYXBlLCBiLnNoYXBlLCAnRXJyb3IgaW4gc2NhbGVkQXJyYXlBZGQ6ICcpO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsKGMxLCBhLCBjMiwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpOiBUO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIHNjYWxhciB0aW1lcyBhcnJheSBvcGVyYXRpb24gYnJvYWRjYXN0ZWQgb3ZlciB0aGUgTkRBcnJheSwgYyAqXG4gICAqIEEuXG4gICAqIEBwYXJhbSBjIFRoZSBzY2FsYXIgaW4gdGhlIG9wZXJhdGlvbi5cbiAgICogQHBhcmFtIEEgdGhlIE5EQXJyYXkgaW4gdGhlIG9wZXJhdGlvbiB0aGF0IHdpbGwgYmUgYnJvYWRjYXN0ZWQgb3Zlci5cbiAgICovXG4gIHNjYWxhclRpbWVzQXJyYXk8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBjLnNpemUgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBhcnJheURpdmlkZWRCeVNjYWxhcjogZmlyc3QgYXJndW1lbnQgbXVzdCBiZSByYW5rIDAsIGJ1dCBgICtcbiAgICAgICAgICAgIGBnb3QgcmFuayAke2MucmFua30uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5zY2FsYXJUaW1lc0FycmF5SW50ZXJuYWwoYywgYSkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBzY2FsYXJUaW1lc0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzogU2NhbGFyLCBhOiBUKTogVDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgYW4gZWxlbWVudC13aXNlIGJyb2FkY2FzdGVkIG11bHRpcGxpY2F0aW9uIG9mIHR3byBtYXRyaWNlcyBBIGFuZFxuICAgKiBCLiBXaWxsIHJldHVybiBhIG5ldyBtYXRyaXggdGhhdCBpcyB0aGUgbWF4IG9mIEEgYW5kIEIsIHdoZXJlIHRoZSBzbWFsbGVyXG4gICAqIG1hdHJpeCB3aWxsIGJyb2FkY2FzdCBvdmVyIHRoZSBsYXJnZXIgbWF0cml4LlxuICAgKiBAcGFyYW0gYyBUaGUgc2NhbGFyIGluIHRoZSBvcGVyYXRpb24uXG4gICAqIEBwYXJhbSBBIHRoZSBOREFycmF5IGluIHRoZSBvcGVyYXRpb24gdGhhdCB3aWxsIGJlIGJyb2FkY2FzdGVkIG92ZXIuXG4gICAqL1xuICBlbGVtZW50V2lzZU11bEJyb2FkY2FzdChhOiBBcnJheTJELCBiOiBBcnJheTJEKTogQXJyYXkyRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGEucmFuayA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIGVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0OiBmaXJzdCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7YS5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgYi5yYW5rID09PSAyLFxuICAgICAgICBgRXJyb3IgaW4gZWxlbWVudFdpc2VNdWxCcm9hZGNhc3Q6IHNlY29uZCBhcmd1bWVudCBtdXN0IGJlIGAgK1xuICAgICAgICAgICAgYHJhbmsgMiwgYnV0IGdvdCByYW5rICR7Yi5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmVsZW1lbnRXaXNlTXVsQnJvYWRjYXN0SW50ZXJuYWwoYSwgYikpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOlxuICAgICAgQXJyYXkyRDtcblxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gQ29udm9sdXRpb24gb3BzIC8vXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyBhIDJEIGNvbnZvbHV0aW9uIG92ZXIgdGhlIGlucHV0IHguXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFtyb3dzLCBjb2xzLCBkZXB0aDFdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIGJpYXNlcyBPcHRpb25hbCBiaWFzZXMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDEgb2Ygc2hhcGUgW2RlcHRoMl0uXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB6ZXJvUGFkIFRoZSB6ZXJvIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZFxuICAgKiBlcXVhbGx5IG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIGNvbnYyZChcbiAgICAgIHg6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIGJpYXNlczogQXJyYXkxRHxudWxsLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZDogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7d2VpZ2h0cy5yYW5rfS5gKTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGJpYXNlcy5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGJpYXNlcyBtdXN0IGJlIHJhbmsgMSwgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgICBgJHtiaWFzZXMucmFua30uYCk7XG4gICAgfVxuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbMl0sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmQ6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IG1hdGNoICBgICtcbiAgICAgICAgICAgIGBpbnB1dCBkZXB0aCBmb3Igd2VpZ2h0cyAke3dlaWdodHMuc2hhcGVbMl19LmApO1xuXG5cbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLmNvbnYyZEludGVybmFsKHgsIHdlaWdodHMsIGJpYXNlcywgc3RyaWRlLCB6ZXJvUGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIGJhY2twcm9wIG9mIGEgMkQgY29udm9sdXRpb24uXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMsIG9mIHNoYXBlIFt4cm93cywgeGNvbHMsIGRlcHRoMV0uXG4gICAqIEBwYXJhbSBkeSBUaGUgZHkgaW1hZ2UsIG11c3QgYmUgcmFuayAzLCBvZiBzaGFwZSBbeXJvd3MsIHljb2xzLCBkZXB0aDJdLlxuICAgKiBAcGFyYW0gd2VpZ2h0cyBUaGUgd2VpZ2h0cyBOREFycmF5LCBtdXN0IGJlIHJhbmsgNCwgb2Ygc2hhcGUgW2YsIGYsIGRlcHRoMSxcbiAgICogZGVwdGgyXS5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBvcmlnaW5hbCBjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiB0aGUgb3JpZ2luYWwgY29udm9sdXRpb24uXG4gICAqL1xuICBjb252MmRCYWNrUHJvcChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt4LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkeSBtdXN0IGJlIHJhbmsgMywgYnV0IGdvdCBzaGFwZSBgICtcbiAgICAgICAgICAgIGAke2R5LnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgd2VpZ2h0cy5yYW5rID09PSA0LFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IHdlaWdodHMgbXVzdCBiZSByYW5rIDQsIGJ1dCBnb3Qgc2hhcGUgYCArXG4gICAgICAgICAgICBgJHt3ZWlnaHRzLnNoYXBlfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5zaGFwZVsyXSA9PT0gd2VpZ2h0cy5zaGFwZVsyXSxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZEJhY2tQcm9wOiBkZXB0aCBvZiB4ICR7eC5zaGFwZVsyXX0pIG11c3QgYCArXG4gICAgICAgICAgICBgbWF0Y2ggaW5wdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVsyXX0uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGR5LnNoYXBlWzJdID09PSB3ZWlnaHRzLnNoYXBlWzNdLFxuICAgICAgICBgRXJyb3IgaW4gY29udjJkQmFja1Byb3A6IGRlcHRoIG9mIGR5ICgke2R5LnNoYXBlWzJdfSkgbXVzdCBgICtcbiAgICAgICAgICAgIGBtYXRjaCBvdXRwdXQgZGVwdGggZm9yIHdlaWdodHMgKCR7d2VpZ2h0cy5zaGFwZVszXX0pLmApO1xuXG4gICAgY29uc3QgYmFja3Byb3BSZXN1bHQgPVxuICAgICAgICB0aGlzLmNvbnYyZEJhY2tQcm9wSW50ZXJuYWwoeCwgZHksIHdlaWdodHMsIHN0cmlkZSwgcGFkKTtcblxuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZGIpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHcpO1xuICAgIHRoaXMudHJhY2soYmFja3Byb3BSZXN1bHQuZHgpO1xuXG4gICAgcmV0dXJuIGJhY2twcm9wUmVzdWx0O1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH07XG5cbiAgLyoqXG4gICAqIENvbXB1dGVzIHRoZSB0cmFuc3Bvc2VkIDJEIGNvbnZvbHV0aW9uIG9mIGFuIGltYWdlLCBhbHNvIGtub3duIGFzIGFcbiAgICogZGVjb252b2x1dGlvbi5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMywgb2Ygc2hhcGUgW3hyb3dzLCB4Y29scywgZGVwdGgxXS5cbiAgICogQHBhcmFtIHdlaWdodHMgVGhlIHdlaWdodHMgTkRBcnJheSwgbXVzdCBiZSByYW5rIDQsIG9mIHNoYXBlIFtmLCBmLCBkZXB0aDEsXG4gICAqIGRlcHRoMl0uXG4gICAqIEBwYXJhbSBiaWFzZXMgT3B0aW9uYWwgYmlhc2VzIE5EQXJyYXksIG11c3QgYmUgcmFuayAxIG9mIHNoYXBlIFtkZXB0aDJdLlxuICAgKiBAcGFyYW0gc3RyaWRlIFRoZSBzdHJpZGUgb2YgdGhlIGNvbnZvbHV0aW9uLlxuICAgKiBAcGFyYW0gcGFkIFRoZSBwYWRkaW5nIG9mIGVhY2ggc2lkZSBvZiB0aGUgaW5wdXQgTkRBcnJheS4gV2lsbCBwYWQgZXF1YWxseVxuICAgKiBvbiBhbGwgc2lkZXMuXG4gICAqL1xuICBjb252MmRUcmFuc3Bvc2UoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB4LnJhbmsgPT09IDMsXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IHggbXVzdCBiZSByYW5rIDMsIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHMucmFuayA9PT0gNCxcbiAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogd2VpZ2h0cyBtdXN0IGJlIHJhbmsgNCwgYnV0IGdvdCBgICtcbiAgICAgICAgICAgIGByYW5rICR7d2VpZ2h0cy5yYW5rfWApO1xuICAgIGlmIChiaWFzZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgYmlhc2VzLnJhbmsgPT09IDEsXG4gICAgICAgICAgYEVycm9yIGluIGNvbnYyZFRyYW5zcG9zZTogYmlhc2VzIG11c3QgYmUgcmFuayAxLCBidXQgZ290ICcgK1xuICAgICAgICAgICAgICAncmFuayAke2JpYXNlcy5yYW5rfS5gKTtcbiAgICB9XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHguc2hhcGVbMl0gPT09IHdlaWdodHMuc2hhcGVbM10sXG4gICAgICAgIGBFcnJvciBpbiBjb252MmRUcmFuc3Bvc2U6IGRlcHRoIG9mIGlucHV0ICgke3guc2hhcGVbMl19KSBtdXN0IGAgK1xuICAgICAgICAgICAgYG1hdGNoIGlucHV0IGRlcHRoIGZvciB3ZWlnaHRzICR7d2VpZ2h0cy5zaGFwZVszXX0uYCk7XG5cbiAgICByZXR1cm4gdGhpcy50cmFjayhcbiAgICAgICAgdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbCh4LCB3ZWlnaHRzLCBiaWFzZXMsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWF4IHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1heFBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgJ0Vycm9yIGluIG1heFBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICcgKyB4LnJhbmsgKyAnLicpO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMubWF4UG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgYmFja3Byb3Agb2YgYSBtYXggcG9vbC5cbiAgICogQHBhcmFtIGR5IFRoZSBkeSBlcnJvci5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgbWF4UG9vbEJhY2twcm9wKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgZHkucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogZHkgbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rIGAgK1xuICAgICAgICAgICAgYCR7ZHkucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1heFBvb2xCYWNrcHJvcDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgYCArXG4gICAgICAgICAgICBgJHt4LnJhbmt9LmApO1xuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5tYXhQb29sQmFja3Byb3BJbnRlcm5hbChkeSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IG1heFBvb2xCYWNrcHJvcEludGVybmFsKFxuICAgICAgZHk6IEFycmF5M0QsIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBDb21wdXRlcyB0aGUgMkQgbWluIHBvb2xpbmcgb2YgYW4gaW1hZ2UuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBpbWFnZSwgbXVzdCBiZSByYW5rIDMuXG4gICAqIEBwYXJhbSBmU2l6ZSBUaGUgZmllbGQgc2l6ZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBzdHJpZGUgVGhlIHN0cmlkZSBvZiB0aGUgbWF4IHBvb2wuXG4gICAqIEBwYXJhbSBwYWQgVGhlIHBhZGRpbmcgb2YgZWFjaCBzaWRlIG9mIHRoZSBpbnB1dCBOREFycmF5LiBXaWxsIHBhZCBlcXVhbGx5XG4gICAqIG9uIGFsbCBzaWRlcy5cbiAgICovXG4gIG1pblBvb2woeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIG1pblBvb2w6IHggbXVzdCBiZSByYW5rIDMgYnV0IGdvdCByYW5rICR7eC5yYW5rfS5gKTtcbiAgICByZXR1cm4gdGhpcy50cmFjayh0aGlzLm1pblBvb2xJbnRlcm5hbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpKTtcbiAgfVxuICBwcm90ZWN0ZWQgYWJzdHJhY3QgbWluUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRDtcblxuICAvKipcbiAgICogQ29tcHV0ZXMgdGhlIDJEIGF2ZXJhZ2UgcG9vbGluZyBvZiBhbiBpbWFnZS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IGltYWdlLCBtdXN0IGJlIHJhbmsgMy5cbiAgICogQHBhcmFtIGZTaXplIFRoZSBmaWVsZCBzaXplIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHN0cmlkZSBUaGUgc3RyaWRlIG9mIHRoZSBtYXggcG9vbC5cbiAgICogQHBhcmFtIHBhZCBUaGUgcGFkZGluZyBvZiBlYWNoIHNpZGUgb2YgdGhlIGlucHV0IE5EQXJyYXkuIFdpbGwgcGFkIGVxdWFsbHlcbiAgICogb24gYWxsIHNpZGVzLlxuICAgKi9cbiAgYXZnUG9vbCh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gYXZnUG9vbDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHJldHVybiB0aGlzLnRyYWNrKHRoaXMuYXZnUG9vbEludGVybmFsKHgsIGZTaXplLCBzdHJpZGUsIHBhZCkpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCBhdmdQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEO1xuXG4gIC8qXG4gICAqIEJpbGluZWFyIHJlc2l6ZSBhIDNEIGFycmF5IHBlciBlYWNoIGNoYW5uZWwgdG8gYSBuZXcgMkQgc2hhcGUuXG4gICAqIEBwYXJhbSB4IFRoZSBpbnB1dCBBcnJheTNELlxuICAgKiBAcGFyYW0gbmV3U2hhcGUyRCBUaGUgbmV3IHNoYXBlIHRvIHJlc2l6ZSB0aGUgQXJyYXkzRCB0by4gRWFjaCBjaGFubmVsIGlzXG4gICAqIHJlc2l6ZWQgaW5kaXZpZHVhbGx5LlxuICAgKiBAcGFyYW0gYWxpZ25Db3JuZXJzIEFuIG9wdGlvbmFsIGJvb2wuIERlZmF1bHRzIHRvIEZhbHNlLiBJZiB0cnVlLCByZXNjYWxlXG4gICAqIGlucHV0IGJ5IChuZXdfaGVpZ2h0IC0gMSkgLyAoaGVpZ2h0IC0gMSksIHdoaWNoIGV4YWN0bHkgYWxpZ25zIHRoZSA0XG4gICAqIGNvcm5lcnMgb2YgaW1hZ2VzIGFuZCByZXNpemVkIGltYWdlcy4gSWYgZmFsc2UsIHJlc2NhbGUgYnkgbmV3X2hlaWdodCAvXG4gICAqIGhlaWdodC4gVHJlYXQgc2ltaWxhcmx5IHRoZSB3aWR0aCBkaW1lbnNpb24uXG4gICAqL1xuICByZXNpemVCaWxpbmVhcjNEKFxuICAgICAgeDogQXJyYXkzRCwgbmV3U2hhcGUyRDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzID0gZmFsc2UpOiBBcnJheTNEIHtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeC5yYW5rID09PSAzLFxuICAgICAgICBgRXJyb3IgaW4gcmVzaXplQmlsaW5lYXIzRDogeCBtdXN0IGJlIHJhbmsgMyBidXQgZ290IHJhbmsgJHt4LnJhbmt9LmApO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICBuZXdTaGFwZTJELmxlbmd0aCA9PT0gMixcbiAgICAgICAgYEVycm9yIGluIHJlc2l6ZUJpbGluZWFyM0Q6IG5ldyBzaGFwZSBtdXN0IDJELCBidXQgZ290IHNoYXBlIGAgK1xuICAgICAgICAgICAgYCR7bmV3U2hhcGUyRH0uYCk7XG4gICAgcmV0dXJuIHRoaXMudHJhY2soXG4gICAgICAgIHRoaXMucmVzaXplQmlsaW5lYXIzREludGVybmFsKHgsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVycykpO1xuICB9XG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLCBhbGlnbkNvcm5lcnM6IGJvb2xlYW4pOiBBcnJheTNEO1xuXG4gIC8qKlxuICAgKiBCYXRjaCBub3JtYWxpemF0aW9uIDNELiBNZWFuLCB2YXJpYW5jZSwgc2NhbGUsIGFuZCBvZmZzZXQgY2FuIGJlIG9mIHR3b1xuICAgKiBzaGFwZXM6IDEpIFRoZSBzYW1lIHNoYXBlIGFzIHRoZSBpbnB1dDogYW4gQXJyYXkzRC4gMikgSW4gdGhlIGNvbW1vbiBjYXNlLFxuICAgKiB0aGUgZGVwdGggZGltZW5zaW9uIGlzIHRoZSBsYXN0IGRpbWVuc2lvbiBvZiB4LCBzbyB0aGUgdmFsdWVzIHdvdWxkIGJlIGFuXG4gICAqIEFycmF5MUQgb2Ygc2hhcGUgW2RlcHRoXS5cbiAgICogQHBhcmFtIHggVGhlIGlucHV0IE5EQXJyYXkuXG4gICAqIEBwYXJhbSBtZWFuIEEgbWVhbiBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2UgQSB2YXJpYW5jZSBOREFycmF5LlxuICAgKiBAcGFyYW0gdmFyaWFuY2VFcHNpbG9uIEEgc21hbGwgZmxvYXQgbnVtYmVyIHRvIGF2b2lkIGRpdmlkaW5nIGJ5IDAuXG4gICAqIEBwYXJhbSBzY2FsZSBBIHNjYWxlIE5EQXJyYXkuXG4gICAqIEBwYXJhbSBvZmZzZXQgQW4gb2Zmc2V0IE5EQXJyYXkuXG4gICAqL1xuICBiYXRjaE5vcm1hbGl6YXRpb24zRChcbiAgICAgIHg6IEFycmF5M0QsIG1lYW46IEFycmF5M0R8QXJyYXkxRCwgdmFyaWFuY2U6IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIHZhcmlhbmNlRXBzaWxvbiA9IC4wMDEsIHNjYWxlPzogQXJyYXkzRHxBcnJheTFELFxuICAgICAgb2Zmc2V0PzogQXJyYXkzRHxBcnJheTFEKTogQXJyYXkzRCB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHgucmFuayA9PT0gMyxcbiAgICAgICAgYEVycm9yIGluIGJhdGNoTm9ybWFsaXphdGlvbjNEOiB4IG11c3QgYmUgcmFuayAzIGJ1dCBnb3QgcmFuayBgICtcbiAgICAgICAgICAgIGAke3gucmFua30uYCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIG1lYW4ucmFuayA9PT0gMyB8fCBtZWFuLnJhbmsgPT09IDEsXG4gICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogbWVhbiBtdXN0IGJlIHJhbmsgMyBvciByYW5rIDEgYnV0IGAgK1xuICAgICAgICAgICAgYGdvdCByYW5rICR7bWVhbi5yYW5rfS5gKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdmFyaWFuY2UucmFuayA9PT0gMyB8fCB2YXJpYW5jZS5yYW5rID09PSAxLFxuICAgICAgICBgRXJyb3IgaW4gYmF0Y2hOb3JtYWxpemF0aW9uM0Q6IHZhcmlhbmNlIG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgIGBidXQgZ290IHJhbmsgJHt2YXJpYW5jZS5yYW5rfS5gKTtcbiAgICBpZiAoc2NhbGUgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgc2NhbGUucmFuayA9PT0gMyB8fCBzY2FsZS5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogc2NhbGUgbXVzdCBiZSByYW5rIDMgb3IgcmFuayAxIGAgK1xuICAgICAgICAgICAgICBgYnV0IGdvdCByYW5rICR7c2NhbGUhLnJhbmt9LmApO1xuICAgIH1cbiAgICBpZiAob2Zmc2V0ICE9IG51bGwpIHtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIG9mZnNldC5yYW5rID09PSAzIHx8IG9mZnNldC5yYW5rID09PSAxLFxuICAgICAgICAgIGBFcnJvciBpbiBiYXRjaE5vcm1hbGl6YXRpb24zRDogb2Zmc2V0IG11c3QgYmUgcmFuayAzIG9yIHJhbmsgMSBgICtcbiAgICAgICAgICAgICAgYGJ1dCBnb3QgcmFuayAke29mZnNldCEucmFua30uYCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMudHJhY2sodGhpcy5iYXRjaE5vcm1hbGl6YXRpb24zREludGVybmFsKFxuICAgICAgICB4LCBtZWFuLCB2YXJpYW5jZSwgdmFyaWFuY2VFcHNpbG9uLCBzY2FsZSwgb2Zmc2V0KSk7XG4gIH1cbiAgcHJvdGVjdGVkIGFic3RyYWN0IGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb246IG51bWJlciwgc2NhbGU/OiBBcnJheTNEfEFycmF5MUQsXG4gICAgICBvZmZzZXQ/OiBBcnJheTNEfEFycmF5MUQpOiBBcnJheTNEO1xufVxuXG5leHBvcnQgZW51bSBNYXRyaXhPcmllbnRhdGlvbiB7XG4gIFJFR1VMQVIsXG4gIFRSQU5TUE9TRURcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL21hdGgvY29udl91dGlsJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi9jb25jYXQzZF91dGlsJztcbmltcG9ydCAqIGFzIGNvcHkyRF91dGlsIGZyb20gJy4vY29weTJkX3V0aWwnO1xuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbiwgTkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi9uZGFycmF5JztcblxuZXhwb3J0IGNsYXNzIE5EQXJyYXlNYXRoQ1BVIGV4dGVuZHMgTkRBcnJheU1hdGgge1xuICBjb25zdHJ1Y3RvcihzYWZlTW9kZSA9IGZhbHNlKSB7XG4gICAgc3VwZXIoc2FmZU1vZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KG5kYXJyYXkuZ2V0VmFsdWVzKCkpfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgcmVzaGFwZUludGVybmFsPFQxIGV4dGVuZHMgTkRBcnJheSwgVDIgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIG5kYXJyYXk6IFQxLCBuZXdTaGFwZTogbnVtYmVyW10pOiBUMiB7XG4gICAgcmV0dXJuIHRoaXMuY2xvbmVJbnRlcm5hbChuZGFycmF5KS5yZXNoYXBlPFQyPihuZXdTaGFwZSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2xpY2UyREludGVybmFsKFxuICAgICAgaW5wdXQ6IEFycmF5MkQsIGJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgc2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IEFycmF5MkQge1xuICAgIGNvbnN0IHJlc3VsdCA9IEFycmF5MkQuemVyb3Moc2l6ZVJvd0NvbCk7XG4gICAgdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgaW5wdXQsIGJlZ2luUm93Q29sLCBzaXplUm93Q29sLCByZXN1bHQsIFswLCAwXSwgc2l6ZVJvd0NvbCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELFxuICAgICAgZGVzdEJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb3B5MkRfdXRpbC52YWxpZGF0ZVNoYXBlcyhzb3VyY2VTaXplUm93Q29sLCBkZXN0U2l6ZVJvd0NvbCk7XG4gICAgY29uc3Qgc3JjVmFsdWVzID0gc291cmNlLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGRzdFZhbHVlcyA9IGRlc3QuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgbiA9IHNvdXJjZVNpemVSb3dDb2xbMF0gKiBzb3VyY2VTaXplUm93Q29sWzFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgICBjb25zdCBzcmNSb3cgPSBzb3VyY2VCZWdpblJvd0NvbFswXSArIE1hdGguZmxvb3IoaSAvIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjQ29sID0gc291cmNlQmVnaW5Sb3dDb2xbMV0gKyAoaSAlIHNvdXJjZVNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3Qgc3JjT2ZmID0gc3JjUm93ICogc291cmNlLnNoYXBlWzFdICsgc3JjQ29sO1xuICAgICAgY29uc3QgZHN0Um93ID0gZGVzdEJlZ2luUm93Q29sWzBdICsgTWF0aC5mbG9vcihpIC8gZGVzdFNpemVSb3dDb2xbMV0pO1xuICAgICAgY29uc3QgZHN0Q29sID0gZGVzdEJlZ2luUm93Q29sWzFdICsgKGkgJSBkZXN0U2l6ZVJvd0NvbFsxXSk7XG4gICAgICBjb25zdCBkc3RPZmYgPSBkc3RSb3cgKiBkZXN0LnNoYXBlWzFdICsgZHN0Q29sO1xuICAgICAgZHN0VmFsdWVzW2RzdE9mZl0gPSBzcmNWYWx1ZXNbc3JjT2ZmXTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgY29uY2F0M0RJbnRlcm5hbCh4MTogQXJyYXkzRCwgeDI6IEFycmF5M0QsIGF4aXM6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZSh4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpO1xuXG4gICAgY29uc3QgdmFsdWVzID0gTkRBcnJheS56ZXJvczxBcnJheTNEPihvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG91dHB1dFNoYXBlWzBdOyBpKyspIHtcbiAgICAgIGZvciAobGV0IGogPSAwOyBqIDwgb3V0cHV0U2hhcGVbMV07IGorKykge1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IG91dHB1dFNoYXBlWzJdOyBrKyspIHtcbiAgICAgICAgICAvLyBTaGFkZXIgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IGluZGV4OiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbaSwgaiwga107XG4gICAgICAgICAgbGV0IHZhbHVlOiBudW1iZXI7XG4gICAgICAgICAgaWYgKGluZGV4W2F4aXNdIDwgeDEuc2hhcGVbYXhpc10pIHtcbiAgICAgICAgICAgIHZhbHVlID0geDEuZ2V0KGksIGosIGspO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpbmRleFtheGlzXSAtPSB4MS5zaGFwZVtheGlzXTtcbiAgICAgICAgICAgIGNvbnN0IFtpMiwgajIsIGsyXSA9IGluZGV4O1xuICAgICAgICAgICAgdmFsdWUgPSB4Mi5nZXQoaTIsIGoyLCBrMik7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdmFsdWVzLnNldCh2YWx1ZSwgaSwgaiwgayk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWVzO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxhclBsdXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXN1bHRWYWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IGNWYWwgKyBhVmFsdWVzW2ldO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KFxuICAgICAgYzE6IFNjYWxhciwgYTogVCwgYzI6IFNjYWxhciwgYjogVCkge1xuICAgIGNvbnN0IGNWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYzFWYWwgPSBjMS5nZXQoKTtcbiAgICBjb25zdCBjMlZhbCA9IGMyLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY1ZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY1ZhbHVlc1tpXSA9IGMxVmFsICogYVZhbHVlc1tpXSArIGMyVmFsICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBjVmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgY1ZhbCA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBjVmFsICogYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIGNvbnN0IG5lZ0EgPSB0aGlzLm5lZ0ludGVybmFsKGEpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuc2NhbGFyUGx1c0FycmF5SW50ZXJuYWwoYywgbmVnQSk7XG5cbiAgICBuZWdBLmRpc3Bvc2UoKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlNaW51c1NjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOiBUIHtcbiAgICBjb25zdCBuZWdDID0gdGhpcy5uZWdJbnRlcm5hbChjKTtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLnNjYWxhclBsdXNBcnJheUludGVybmFsKG5lZ0MsIGEpO1xuXG4gICAgbmVnQy5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIG5lZ0ludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBUKTogVCB7XG4gICAgcmV0dXJuIHRoaXMuc2NhbGFyVGltZXNBcnJheUludGVybmFsKFNjYWxhci5ORUdfT05FLCBhKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhZGRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLnNjYWxlZEFycmF5QWRkSW50ZXJuYWw8VD4oU2NhbGFyLk9ORSwgYSwgU2NhbGFyLk9ORSwgYik7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zY2FsZWRBcnJheUFkZEludGVybmFsPFQ+KFNjYWxhci5PTkUsIGEsIFNjYWxhci5ORUdfT05FLCBiKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXRNdWxJbnRlcm5hbChcbiAgICAgIGE6IEFycmF5MkQsIGI6IEFycmF5MkQsIGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsXG4gICAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogQXJyYXkyRCB7XG4gICAgY29uc3Qgc2hhcmVkRGltID1cbiAgICAgICAgKGFPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBhLnNoYXBlWzFdIDogYS5zaGFwZVswXTtcblxuICAgIGNvbnN0IGxlZnREaW0gPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMF0gOiBhLnNoYXBlWzFdO1xuICAgIGNvbnN0IHJpZ2h0RGltID1cbiAgICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBiLnNoYXBlWzFdIDogYi5zaGFwZVswXTtcblxuICAgIGNvbnN0IG5vcm1hbEdldHRlciA9IChtYXRyaXg6IEFycmF5MkQsIGk6IG51bWJlciwgajogbnVtYmVyKSA9PlxuICAgICAgICBtYXRyaXguZ2V0KGksIGopO1xuICAgIGNvbnN0IHRyYW5zcG9zZWRHZXR0ZXIgPSAobWF0cml4OiBBcnJheTJELCBpOiBudW1iZXIsIGo6IG51bWJlcikgPT5cbiAgICAgICAgbWF0cml4LmdldChqLCBpKTtcblxuICAgIGNvbnN0IGFHZXR0ZXIgPSAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/XG4gICAgICAgIG5vcm1hbEdldHRlciA6XG4gICAgICAgIHRyYW5zcG9zZWRHZXR0ZXI7XG4gICAgY29uc3QgYkdldHRlciA9IChiT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID9cbiAgICAgICAgbm9ybWFsR2V0dGVyIDpcbiAgICAgICAgdHJhbnNwb3NlZEdldHRlcjtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGxlZnREaW0gKiByaWdodERpbSk7XG4gICAgbGV0IGluZGV4ID0gMDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVmdERpbTsgKytpKSB7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IHJpZ2h0RGltOyArK2opIHtcbiAgICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICAgIGZvciAobGV0IGsgPSAwOyBrIDwgc2hhcmVkRGltOyArK2spIHtcbiAgICAgICAgICAvLyBUT0RPOiBvcHRpbWl6ZSBDUFUgbWF0bXVsLlxuICAgICAgICAgIHN1bSArPSBhR2V0dGVyKGEsIGksIGspICogYkdldHRlcihiLCBrLCBqKTtcbiAgICAgICAgfVxuICAgICAgICB2YWx1ZXNbaW5kZXgrK10gPSBzdW07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbGVmdERpbSwgcmlnaHREaW1dLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGVsZW1lbnRXaXNlTXVsSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICBjb25zdCBuZXdWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGEuc2l6ZSk7XG4gICAgY29uc3QgYVZhbHVlcyA9IGEuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgYlZhbHVlcyA9IGIuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldICogYlZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOiBBcnJheTJEIHtcbiAgICBjb25zdCBtYXhSb3cgPSBNYXRoLm1heChhLnNoYXBlWzBdLCBiLnNoYXBlWzBdKTtcbiAgICBjb25zdCBtYXhDb2wgPSBNYXRoLm1heChhLnNoYXBlWzFdLCBiLnNoYXBlWzFdKTtcblxuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobWF4Um93ICogbWF4Q29sKTtcbiAgICBsZXQgaW5kZXggPSAwO1xuICAgIGZvciAobGV0IHJvdyA9IDA7IHJvdyA8IG1heFJvdzsgcm93KyspIHtcbiAgICAgIGZvciAobGV0IGNvbCA9IDA7IGNvbCA8IG1heENvbDsgY29sKyspIHtcbiAgICAgICAgdmFsdWVzW2luZGV4KytdID0gYS5nZXQocm93ICUgYS5zaGFwZVswXSwgY29sICUgYS5zaGFwZVsxXSkgKlxuICAgICAgICAgICAgYi5nZXQocm93ICUgYi5zaGFwZVswXSwgY29sICUgYi5zaGFwZVsxXSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBBcnJheTJELm5ldyhbbWF4Um93LCBtYXhDb2xdLCB2YWx1ZXMpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGRpdmlkZUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBiOiBUKTogVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGJWYWx1ZXMgPSBiLmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gYVZhbHVlc1tpXSAvIGJWYWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoYS5zaXplKTtcbiAgICBjb25zdCBhVmFsdWVzID0gYS5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCBjVmFsdWUgPSBjLmdldCgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgbmV3VmFsdWVzW2ldID0gY1ZhbHVlIC8gYVZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihhLnNoYXBlLCB7dmFsdWVzOiBuZXdWYWx1ZXN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcnJheURpdmlkZWRCeVNjYWxhckludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihhOiBULCBjOiBTY2FsYXIpOlxuICAgICAgVCB7XG4gICAgY29uc3QgbmV3VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShhLnNpemUpO1xuICAgIGNvbnN0IGFWYWx1ZXMgPSBhLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IGNWYWx1ZSA9IGMuZ2V0KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhVmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICBuZXdWYWx1ZXNbaV0gPSBhVmFsdWVzW2ldIC8gY1ZhbHVlO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHN1bUludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBzdW0gPSAwO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHN1bSArPSB2YWx1ZXNbaV07XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KHN1bSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWluSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgbGV0IG1pbiA9IE51bWJlci5NQVhfVkFMVUU7XG4gICAgbGV0IG1pbkluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPCBtaW4pIHtcbiAgICAgICAgbWluID0gdmFsdWU7XG4gICAgICAgIG1pbkluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWluSW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGxldCBtYXggPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IG1heEluZGV4ID0gLTE7XG4gICAgY29uc3QgdmFsdWVzID0gbmRhcnJheS5nZXRWYWx1ZXMoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICAgIG1heEluZGV4ID0gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFNjYWxhci5uZXcobWF4SW5kZXgpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01heEVxdWFsc0ludGVybmFsKHgxOiBOREFycmF5LCB4MjogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgYXJnTWF4MSA9IHRoaXMuYXJnTWF4SW50ZXJuYWwoeDEpLmdldCgpO1xuICAgIGNvbnN0IGFyZ01heDIgPSB0aGlzLmFyZ01heEludGVybmFsKHgyKS5nZXQoKTtcbiAgICBpZiAoaXNOYU4oYXJnTWF4MSkgfHwgaXNOYU4oYXJnTWF4MikpIHtcbiAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KCsoYXJnTWF4MSA9PT0gYXJnTWF4MikpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHRvcEtJbnRlcm5hbChuZGFycmF5OiBOREFycmF5LCBrOiBudW1iZXIpOlxuICAgICAge3ZhbHVlczogQXJyYXkxRCwgaW5kaWNlczogQXJyYXkxRH0ge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3QgdmFsdWVzQW5kSW5kaWNlczogQXJyYXk8e3ZhbHVlOiBudW1iZXIsIGluZGV4OiBudW1iZXJ9PiA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YWx1ZXNBbmRJbmRpY2VzLnB1c2goe3ZhbHVlOiB2YWx1ZXNbaV0sIGluZGV4OiBpfSk7XG4gICAgfVxuICAgIHZhbHVlc0FuZEluZGljZXMuc29ydCgoYSwgYikgPT4ge1xuICAgICAgcmV0dXJuIGIudmFsdWUgLSBhLnZhbHVlO1xuICAgIH0pO1xuICAgIGNvbnN0IHRvcGtWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KGspO1xuICAgIGNvbnN0IHRvcGtJbmRpY2VzID0gbmV3IEZsb2F0MzJBcnJheShrKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGs7IGkrKykge1xuICAgICAgdG9wa1ZhbHVlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0udmFsdWU7XG4gICAgICB0b3BrSW5kaWNlc1tpXSA9IHZhbHVlc0FuZEluZGljZXNbaV0uaW5kZXg7XG4gICAgfVxuICAgIHJldHVybiB7dmFsdWVzOiBBcnJheTFELm5ldyh0b3BrVmFsdWVzKSwgaW5kaWNlczogQXJyYXkxRC5uZXcodG9wa0luZGljZXMpfTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5JbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGxldCBtaW4gPSB2YWx1ZXNbMF07XG4gICAgZm9yIChsZXQgaSA9IDE7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICByZXR1cm4gU2NhbGFyLm5ldyhOYU4pO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIDwgbWluKSB7XG4gICAgICAgIG1pbiA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gU2NhbGFyLm5ldyhtaW4pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1heEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgbGV0IG1heCA9IHZhbHVlc1swXTtcbiAgICBmb3IgKGxldCBpID0gMTsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgICAgY29uc3QgdmFsdWUgPSB2YWx1ZXNbaV07XG4gICAgICBpZiAoaXNOYU4odmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBTY2FsYXIubmV3KE5hTik7XG4gICAgICB9XG4gICAgICBpZiAodmFsdWUgPiBtYXgpIHtcbiAgICAgICAgbWF4ID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBTY2FsYXIubmV3KG1heCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIG5ld1ZhbHVlc1tpXSA9IE1hdGguZXhwKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogbmV3VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG5ld1ZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzLmxlbmd0aCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgbmV3VmFsdWVzW2ldID0gTWF0aC5sb2codmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IG5ld1ZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGxvZ1N1bUV4cEludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHhNYXggPSB0aGlzLm1heChuZGFycmF5KTtcbiAgICBjb25zdCBhID0gdGhpcy5hcnJheU1pbnVzU2NhbGFyKG5kYXJyYXksIHhNYXgpO1xuICAgIGNvbnN0IGIgPSB0aGlzLmV4cChhKTtcbiAgICBjb25zdCBjID0gdGhpcy5zdW0oYik7XG4gICAgY29uc3QgZCA9IHRoaXMubG9nKGMpO1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuYWRkKHhNYXgsIGQpO1xuXG4gICAgeE1heC5kaXNwb3NlKCk7XG4gICAgYS5kaXNwb3NlKCk7XG4gICAgYi5kaXNwb3NlKCk7XG4gICAgYy5kaXNwb3NlKCk7XG4gICAgZC5kaXNwb3NlKCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlbHVJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSBNYXRoLm1heCgwLCB2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpZ21vaWRJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHJlc3VsdFZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkobmRhcnJheS5zaXplKTtcbiAgICBjb25zdCB2YWx1ZXMgPSBuZGFycmF5LmdldFZhbHVlcygpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgKytpKSB7XG4gICAgICByZXN1bHRWYWx1ZXNbaV0gPSAxIC8gKDEgKyBNYXRoLmV4cCgtdmFsdWVzW2ldKSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdGFuaEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IHV0aWwudGFuaCh2YWx1ZXNbaV0pO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIHJlc3VsdFZhbHVlc1tpXSA9IE1hdGguc2luKHZhbHVlc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmRhcnJheS5zaGFwZSwge3ZhbHVlczogcmVzdWx0VmFsdWVzfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3RlcEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcmVzdWx0VmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheShuZGFycmF5LnNpemUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5kYXJyYXkuZ2V0VmFsdWVzKCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyArK2kpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdmFsdWVzW2ldO1xuICAgICAgcmVzdWx0VmFsdWVzW2ldID0gdmFsdWUgPiAwID8gMSA6ICh2YWx1ZSA8IDAgPyAwIDogdmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KG5kYXJyYXkuc2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICB9XG5cbiAgLyoqXG4gICAqIGltYWdlIGlzIG9mIHNoYXBlIFtyLCBjLCBkMV0uXG4gICAqIHdlaWdodHMgaXMgb2Ygc2hhcGUgW0YsIEYsIGQxLCBkMl0uXG4gICAqL1xuICBwcm90ZWN0ZWQgY29udjJkSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdID0geC5zaGFwZTtcbiAgICBjb25zdCBmaWVsZFNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzLCB4Q29scywgaW5wdXREZXB0aF0sIGZpZWxkU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG4gICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICBmb3IgKGxldCB5UiA9IDA7IHlSIDwgeS5zaGFwZVswXTsgKyt5Uikge1xuICAgICAgICBjb25zdCB4UkNvcm5lciA9IHlSICogc3RyaWRlIC0gcGFkO1xuICAgICAgICBjb25zdCB4Uk1pbiA9IE1hdGgubWF4KDAsIHhSQ29ybmVyKTtcbiAgICAgICAgY29uc3QgeFJNYXggPSBNYXRoLm1pbih4Um93cywgZmllbGRTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZmllbGRTaXplICsgeENDb3JuZXIpO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSIC0geFJDb3JuZXI7XG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgLSB4Q0Nvcm5lcjtcbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IGlucHV0RGVwdGg7ICsrZDEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwaXhlbCA9IHguZ2V0KHhSLCB4QywgZDEpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHdlaWdodCA9IHdlaWdodHMuZ2V0KHdSLCB3QywgZDEsIGQyKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGJpYXMgPSAoYmlhc2VzICE9IG51bGwpID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb252MmRCYWNrUHJvcEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZHk6IEFycmF5M0QsIHdlaWdodHM6IEFycmF5NEQsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgcGFkOiBudW1iZXIpOiB7ZHg6IEFycmF5M0QsIGR3OiBBcnJheTRELCBkYjogQXJyYXkxRH0ge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBkdyA9IHRoaXMuY29udjJkRGVyV2VpZ2h0cyh4LCBkeSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCBkYiA9IHRoaXMuY29udjJkRGVyQmlhcyhkeSk7XG4gICAgY29uc3QgZHggPSB0aGlzLmNvbnYyZFRyYW5zcG9zZUludGVybmFsKGR5LCB3ZWlnaHRzLCBudWxsLCBzdHJpZGUsIHBhZCk7XG4gICAgcmV0dXJuIHtkeCwgZGIsIGR3fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZUludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgd2VpZ2h0czogQXJyYXk0RCwgYmlhc2VzOiBBcnJheTFEfG51bGwsIG9yaWdTdHJpZGU6IG51bWJlcixcbiAgICAgIG9yaWdQYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGZTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVsyXTtcbiAgICBjb25zdCBvcmlnT3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIHhEZXB0aF0gPSB4LnNoYXBlO1xuXG4gICAgLy8gRGlsYXRlIHRoZSBpbnB1dC5cbiAgICBjb25zdCB4Um93c0RpbGF0ZWQgPSAoeFJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IHhDb2xzRGlsYXRlZCA9ICh4Q29scyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG5cbiAgICBjb25zdCBvdXRwdXRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW3hSb3dzRGlsYXRlZCwgeENvbHNEaWxhdGVkLCBvcmlnT3V0cHV0RGVwdGhdLCBmU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIDEsXG4gICAgICAgIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvcmlnSW5wdXREZXB0aDsgKytkMikge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCBNYXRoLmNlaWwoeFJDb3JuZXIgLyBvcmlnU3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIChmU2l6ZSArIHhSQ29ybmVyKSAvIG9yaWdTdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIE1hdGguY2VpbCh4Q0Nvcm5lciAvIG9yaWdTdHJpZGUpKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCAoZlNpemUgKyB4Q0Nvcm5lcikgLyBvcmlnU3RyaWRlKTtcblxuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB4UiA9IHhSTWluOyB4UiA8IHhSTWF4OyArK3hSKSB7XG4gICAgICAgICAgICBjb25zdCB3UiA9IHhSICogb3JpZ1N0cmlkZSAtIHhSQ29ybmVyO1xuXG4gICAgICAgICAgICBmb3IgKGxldCB4QyA9IHhDTWluOyB4QyA8IHhDTWF4OyArK3hDKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHdDID0geEMgKiBvcmlnU3RyaWRlIC0geENDb3JuZXI7XG5cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cy5nZXQoZlNpemUgLSAxIC0gd1IsIGZTaXplIC0gMSAtIHdDLCBkMiwgZDEpO1xuICAgICAgICAgICAgICAgIGRvdFByb2QgKz0gcGl4ZWwgKiB3ZWlnaHQ7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgYmlhcyA9IGJpYXNlcyAhPSBudWxsID8gYmlhc2VzLmdldChkMikgOiAwO1xuICAgICAgICAgIHkuc2V0KGRvdFByb2QgKyBiaWFzLCB5UiwgeUMsIGQyKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIC8qKlxuICAgKiBpbWFnZSBpcyBvZiBzaGFwZSBbciwgYywgZDFdLlxuICAgKiB3ZWlnaHRzIGlzIG9mIHNoYXBlIFtGLCBGLCBkMSwgZDJdLlxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnYyZFRyYW5zcG9zZVNoYWRlckxpa2UoXG4gICAgICB4OiBBcnJheTNELCBvcmlnV2VpZ2h0czogQXJyYXk0RCwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgZlNpemUgPSBvcmlnV2VpZ2h0cy5zaGFwZVswXTtcbiAgICBjb25zdCBwYWQgPSBmU2l6ZSAtIDEgLSBvcmlnUGFkO1xuICAgIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3JpZ091dHB1dERlcHRoID0gb3JpZ1dlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgW3hSb3dzLCB4Q29scywgeERlcHRoXSA9IHguc2hhcGU7XG5cbiAgICAvLyBEaWxhdGUgdGhlIGlucHV0LlxuICAgIGNvbnN0IHhSb3dzRGlsYXRlZCA9ICh4Um93cyAtIDEpICogb3JpZ1N0cmlkZSArIDE7XG4gICAgY29uc3QgeENvbHNEaWxhdGVkID0gKHhDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbeFJvd3NEaWxhdGVkLCB4Q29sc0RpbGF0ZWQsIG9yaWdPdXRwdXREZXB0aF0sIGZTaXplLCBvcmlnSW5wdXREZXB0aCwgMSxcbiAgICAgICAgcGFkKTtcbiAgICBjb25zdCB5ID0gQXJyYXkzRC56ZXJvcyhvdXRwdXRTaGFwZSk7XG5cbiAgICBmb3IgKGxldCBkMiA9IDA7IGQyIDwgb3JpZ0lucHV0RGVwdGg7ICsrZDIpIHtcbiAgICAgIGZvciAobGV0IHlSID0gMDsgeVIgPCB5LnNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGZvciAobGV0IHlDID0gMDsgeUMgPCB5LnNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgLy8gU2hhZGVyIGNvZGUgYmVnaW5zLlxuICAgICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAtIHBhZDtcbiAgICAgICAgICBsZXQgZG90UHJvZCA9IDA7XG4gICAgICAgICAgZm9yIChsZXQgd1IgPSAwOyB3UiA8IGZTaXplOyArK3dSKSB7XG4gICAgICAgICAgICBjb25zdCB4UiA9ICh4UkNvcm5lciArIHdSKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICBpZiAoeFIgPCAwIHx8IHhSID49IHhSb3dzIHx8IE1hdGguZmxvb3IoeFIpICE9PSB4Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCB4QyA9ICh4Q0Nvcm5lciArIHdDKSAvIG9yaWdTdHJpZGU7XG4gICAgICAgICAgICAgIGlmICh4QyA8IDAgfHwgeEMgPj0geENvbHMgfHwgTWF0aC5mbG9vcih4QykgIT09IHhDKSB7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZm9yIChsZXQgZDEgPSAwOyBkMSA8IG9yaWdPdXRwdXREZXB0aDsgKytkMSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkMSk7XG4gICAgICAgICAgICAgICAgY29uc3Qgd2VpZ2h0ID1cbiAgICAgICAgICAgICAgICAgICAgb3JpZ1dlaWdodHMuZ2V0KGZTaXplIC0gMSAtIHdSLCBmU2l6ZSAtIDEgLSB3QywgZDIsIGQxKTtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHBpeGVsICogd2VpZ2h0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHkuc2V0KGRvdFByb2QsIHlSLCB5QywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB5O1xuICB9XG5cbiAgY29udjJkRGVyV2VpZ2h0cyhcbiAgICAgIHg6IEFycmF5M0QsIGRZOiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHplcm9QYWQ6IG51bWJlcik6IEFycmF5NEQge1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB4LnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3Qgd2VpZ2h0c1NoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzU2hhcGU0RChpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuICAgIGNvbnN0IGRXID0gQXJyYXk0RC56ZXJvcyh3ZWlnaHRzU2hhcGUpO1xuXG4gICAgY29uc3QgeU51bVJvd3MgPSBkWS5zaGFwZVswXTtcbiAgICBjb25zdCB5TnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHhOdW1Sb3dzID0geC5zaGFwZVswXTtcbiAgICBjb25zdCB4TnVtQ29scyA9IHguc2hhcGVbMV07XG5cbiAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgIGNvbnN0IHlSTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKSk7XG4gICAgICBjb25zdCB5Uk1heCA9IE1hdGgubWluKHlOdW1Sb3dzLCAoeE51bVJvd3MgKyB6ZXJvUGFkIC0gd1IpIC8gc3RyaWRlKTtcblxuICAgICAgZm9yIChsZXQgd0MgPSAwOyB3QyA8IGZTaXplOyArK3dDKSB7XG4gICAgICAgIGNvbnN0IHlDTWluID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKCh6ZXJvUGFkIC0gd0MpIC8gc3RyaWRlKSk7XG4gICAgICAgIGNvbnN0IHlDTWF4ID0gTWF0aC5taW4oeU51bUNvbHMsICh4TnVtQ29scyArIHplcm9QYWQgLSB3QykgLyBzdHJpZGUpO1xuXG4gICAgICAgIGZvciAobGV0IGQxID0gMDsgZDEgPCBpbnB1dERlcHRoOyArK2QxKSB7XG4gICAgICAgICAgZm9yIChsZXQgZDIgPSAwOyBkMiA8IG91dHB1dERlcHRoOyArK2QyKSB7XG4gICAgICAgICAgICAvLyBOZWVkIHRvIGNvbnZvbHZlLlxuICAgICAgICAgICAgbGV0IGRvdFByb2QgPSAwO1xuICAgICAgICAgICAgZm9yIChsZXQgeVIgPSB5Uk1pbjsgeVIgPCB5Uk1heDsgKyt5Uikge1xuICAgICAgICAgICAgICBjb25zdCB4UiA9IHdSICsgeVIgKiBzdHJpZGUgLSB6ZXJvUGFkO1xuICAgICAgICAgICAgICBmb3IgKGxldCB5QyA9IHlDTWluOyB5QyA8IHlDTWF4OyArK3lDKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgeEMgPSB3QyArIHlDICogc3RyaWRlIC0gemVyb1BhZDtcbiAgICAgICAgICAgICAgICBkb3RQcm9kICs9IHguZ2V0KHhSLCB4QywgZDEpICogZFkuZ2V0KHlSLCB5QywgZDIpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkVy5zZXQoZG90UHJvZCwgd1IsIHdDLCBkMSwgZDIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZFc7XG4gIH1cblxuICBjb252MmREZXJCaWFzKGRZOiBBcnJheTNEKTogQXJyYXkxRCB7XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSBkWS5zaGFwZVsyXTtcbiAgICBjb25zdCBudW1Sb3dzID0gZFkuc2hhcGVbMF07XG4gICAgY29uc3QgbnVtQ29scyA9IGRZLnNoYXBlWzFdO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkob3V0cHV0RGVwdGgpO1xuICAgIGZvciAobGV0IGQyID0gMDsgZDIgPCBvdXRwdXREZXB0aDsgKytkMikge1xuICAgICAgbGV0IHN1bSA9IDA7XG4gICAgICBmb3IgKGxldCByID0gMDsgciA8IG51bVJvd3M7ICsrcikge1xuICAgICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG51bUNvbHM7ICsrYykge1xuICAgICAgICAgIHN1bSArPSBkWS5nZXQociwgYywgZDIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB2YWx1ZXNbZDJdID0gc3VtO1xuICAgIH1cbiAgICByZXR1cm4gQXJyYXkxRC5uZXcodmFsdWVzKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzd2l0Y2hEaW1JbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4odDogVCwgbmV3RGltOiBudW1iZXJbXSk6IFQge1xuICAgIGNvbnN0IG5ld1NoYXBlOiBudW1iZXJbXSA9IG5ldyBBcnJheSh0LnJhbmspO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmV3U2hhcGUubGVuZ3RoOyBpKyspIHtcbiAgICAgIG5ld1NoYXBlW2ldID0gdC5zaGFwZVtuZXdEaW1baV1dO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHQuc2l6ZSk7XG4gICAgY29uc3QgdmFsdWVzID0gdC5nZXRWYWx1ZXMoKTtcbiAgICBjb25zdCByZXN1bHQgPSBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHt2YWx1ZXM6IHJlc3VsdFZhbHVlc30pO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdC5zaXplOyArK2kpIHtcbiAgICAgIGNvbnN0IGxvYyA9IHQuaW5kZXhUb0xvYyhpKTtcblxuICAgICAgLy8gUGVybXV0ZSBsb2NhdGlvbi5cbiAgICAgIGNvbnN0IG5ld0xvYzogbnVtYmVyW10gPSBuZXcgQXJyYXkobG9jLmxlbmd0aCk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld0xvYy5sZW5ndGg7IGkrKykge1xuICAgICAgICBuZXdMb2NbaV0gPSBsb2NbbmV3RGltW2ldXTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbmV3SW5kZXggPSByZXN1bHQubG9jVG9JbmRleChuZXdMb2MpO1xuICAgICAgcmVzdWx0VmFsdWVzW25ld0luZGV4XSA9IHZhbHVlc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByaXZhdGUgcG9vbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcixcbiAgICAgIHBvb2xUeXBlOiAnbWF4J3wnbWluJ3wnYXZnJykge1xuICAgIGNvbnN0IFt4Um93cywgeENvbHMsIGRlcHRoXSA9IHguc2hhcGU7XG4gICAgY29uc3Qgb3V0cHV0U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFt4Um93cywgeENvbHMsIGRlcHRoXSwgZlNpemUsIGRlcHRoLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgeSA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IHkuc2hhcGVbMF07ICsreVIpIHtcbiAgICAgICAgY29uc3QgeFJDb3JuZXIgPSB5UiAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgY29uc3QgeFJNaW4gPSBNYXRoLm1heCgwLCB4UkNvcm5lcik7XG4gICAgICAgIGNvbnN0IHhSTWF4ID0gTWF0aC5taW4oeFJvd3MsIGZTaXplICsgeFJDb3JuZXIpO1xuICAgICAgICBmb3IgKGxldCB5QyA9IDA7IHlDIDwgeS5zaGFwZVsxXTsgKyt5Qykge1xuICAgICAgICAgIGNvbnN0IHhDQ29ybmVyID0geUMgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgeENNaW4gPSBNYXRoLm1heCgwLCB4Q0Nvcm5lcik7XG4gICAgICAgICAgY29uc3QgeENNYXggPSBNYXRoLm1pbih4Q29scywgZlNpemUgKyB4Q0Nvcm5lcik7XG5cblxuICAgICAgICAgIGxldCBtaW5NYXhWYWx1ZSA9XG4gICAgICAgICAgICAgIChwb29sVHlwZSA9PT0gJ21heCcgPyBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFkgOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgICAgICAgICBsZXQgYXZnVmFsdWUgPSAwO1xuXG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKGlzTmFOKHBpeGVsKSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGF2Z1ZhbHVlID0gTmFOO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmICgocG9vbFR5cGUgPT09ICdtYXgnICYmIHBpeGVsID4gbWluTWF4VmFsdWUpIHx8XG4gICAgICAgICAgICAgICAgICAocG9vbFR5cGUgPT09ICdtaW4nICYmIHBpeGVsIDwgbWluTWF4VmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgbWluTWF4VmFsdWUgPSBwaXhlbDtcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycpIHtcbiAgICAgICAgICAgICAgICBhdmdWYWx1ZSArPSBwaXhlbCAvIChmU2l6ZSAqIGZTaXplKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlzTmFOKG1pbk1heFZhbHVlKSkge1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgeS5zZXQocG9vbFR5cGUgPT09ICdhdmcnID8gYXZnVmFsdWUgOiBtaW5NYXhWYWx1ZSwgeVIsIHlDLCBkKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICByZXR1cm4gdGhpcy5wb29sKHgsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21heCcpO1xuICB9XG5cbiAgbWF4UG9vbFBvc2l0aW9ucyh4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpIHtcbiAgICBjb25zdCBbeFJvd3MsIHhDb2xzLCBkZXB0aF0gPSB4LnNoYXBlO1xuICAgIGNvbnN0IG91dHB1dFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKHguc2hhcGUsIGZTaXplLCBkZXB0aCwgc3RyaWRlLCBwYWQpO1xuICAgIGNvbnN0IG1heFBvc2l0aW9ucyA9IEFycmF5M0QuemVyb3Mob3V0cHV0U2hhcGUpO1xuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgeVIgPSAwOyB5UiA8IG91dHB1dFNoYXBlWzBdOyArK3lSKSB7XG4gICAgICAgIGNvbnN0IHhSQ29ybmVyID0geVIgKiBzdHJpZGUgLSBwYWQ7XG4gICAgICAgIGNvbnN0IHhSTWluID0gTWF0aC5tYXgoMCwgeFJDb3JuZXIpO1xuICAgICAgICBjb25zdCB4Uk1heCA9IE1hdGgubWluKHhSb3dzLCBmU2l6ZSArIHhSQ29ybmVyKTtcbiAgICAgICAgZm9yIChsZXQgeUMgPSAwOyB5QyA8IG91dHB1dFNoYXBlWzFdOyArK3lDKSB7XG4gICAgICAgICAgY29uc3QgeENDb3JuZXIgPSB5QyAqIHN0cmlkZSAtIHBhZDtcbiAgICAgICAgICBjb25zdCB4Q01pbiA9IE1hdGgubWF4KDAsIHhDQ29ybmVyKTtcbiAgICAgICAgICBjb25zdCB4Q01heCA9IE1hdGgubWluKHhDb2xzLCBmU2l6ZSArIHhDQ29ybmVyKTtcbiAgICAgICAgICBsZXQgbWF4VmFsdWUgPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgICAgICAgbGV0IG1heFBvc2l0aW9uID0gLTE7XG4gICAgICAgICAgZm9yIChsZXQgeFIgPSB4Uk1pbjsgeFIgPCB4Uk1heDsgKyt4Uikge1xuICAgICAgICAgICAgY29uc3Qgd1IgPSB4UiAtIHhSQ29ybmVyO1xuICAgICAgICAgICAgZm9yIChsZXQgeEMgPSB4Q01pbjsgeEMgPCB4Q01heDsgKyt4Qykge1xuICAgICAgICAgICAgICBjb25zdCB3QyA9IHhDIC0geENDb3JuZXI7XG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0geC5nZXQoeFIsIHhDLCBkKTtcbiAgICAgICAgICAgICAgaWYgKHBpeGVsID4gbWF4VmFsdWUpIHtcbiAgICAgICAgICAgICAgICBtYXhWYWx1ZSA9IHBpeGVsO1xuICAgICAgICAgICAgICAgIG1heFBvc2l0aW9uID0gd1IgKiBmU2l6ZSArIHdDO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIG1heFBvc2l0aW9ucy5zZXQobWF4UG9zaXRpb24sIHlSLCB5QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1heFBvc2l0aW9ucztcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhQb29sQmFja3Byb3BJbnRlcm5hbChcbiAgICAgIGR5OiBBcnJheTNELCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgICBvcmlnUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBtYXhQb3NpdGlvbnMgPSB0aGlzLm1heFBvb2xQb3NpdGlvbnMoeCwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgW2R5Um93cywgZHlDb2xzLCBkZXB0aF0gPSBkeS5zaGFwZTtcblxuICAgIC8vIERpbGF0ZSB0aGUgaW5wdXQuXG4gICAgY29uc3QgZHlSb3dzRGlsYXRlZCA9IChkeVJvd3MgLSAxKSAqIG9yaWdTdHJpZGUgKyAxO1xuICAgIGNvbnN0IGR4Q29sc0RpbGF0ZWQgPSAoZHlDb2xzIC0gMSkgKiBvcmlnU3RyaWRlICsgMTtcblxuICAgIGNvbnN0IG91dHB1dFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICBbZHlSb3dzRGlsYXRlZCwgZHhDb2xzRGlsYXRlZCwgZGVwdGhdLCBmU2l6ZSwgZGVwdGgsIDEsIHBhZCk7XG4gICAgY29uc3QgZHggPSBBcnJheTNELnplcm9zKG91dHB1dFNoYXBlKTtcblxuICAgIGZvciAobGV0IGQgPSAwOyBkIDwgZGVwdGg7ICsrZCkge1xuICAgICAgZm9yIChsZXQgZHhSID0gMDsgZHhSIDwgZHguc2hhcGVbMF07ICsrZHhSKSB7XG4gICAgICAgIGZvciAobGV0IGR4QyA9IDA7IGR4QyA8IGR4LnNoYXBlWzFdOyArK2R4Qykge1xuICAgICAgICAgIC8vIFNoYWRlciBjb2RlIGJlZ2lucy5cbiAgICAgICAgICBjb25zdCBkeVJDb3JuZXIgPSBkeFIgLSBwYWQ7XG4gICAgICAgICAgY29uc3QgZHlDQ29ybmVyID0gZHhDIC0gcGFkO1xuICAgICAgICAgIGxldCBkb3RQcm9kID0gMDtcbiAgICAgICAgICBmb3IgKGxldCB3UiA9IDA7IHdSIDwgZlNpemU7ICsrd1IpIHtcbiAgICAgICAgICAgIGNvbnN0IGR5UiA9IChkeVJDb3JuZXIgKyB3UikgLyBvcmlnU3RyaWRlO1xuICAgICAgICAgICAgaWYgKGR5UiA8IDAgfHwgZHlSID49IGR5Um93cyB8fCBNYXRoLmZsb29yKGR5UikgIT09IGR5Uikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGZvciAobGV0IHdDID0gMDsgd0MgPCBmU2l6ZTsgKyt3Qykge1xuICAgICAgICAgICAgICBjb25zdCBkeUMgPSAoZHlDQ29ybmVyICsgd0MpIC8gb3JpZ1N0cmlkZTtcbiAgICAgICAgICAgICAgaWYgKGR5QyA8IDAgfHwgZHlDID49IGR5Q29scyB8fCBNYXRoLmZsb29yKGR5QykgIT09IGR5Qykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IG1heFBvcyA9IGZTaXplICogZlNpemUgLSAxIC0gbWF4UG9zaXRpb25zLmdldChkeVIsIGR5QywgZCk7XG4gICAgICAgICAgICAgIGNvbnN0IGN1clBvcyA9IHdSICogZlNpemUgKyB3QztcblxuICAgICAgICAgICAgICBjb25zdCBtYXNrID0gbWF4UG9zID09PSBjdXJQb3MgPyAxIDogMDtcbiAgICAgICAgICAgICAgaWYgKG1hc2sgPT09IDApIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGNvbnN0IHBpeGVsID0gZHkuZ2V0KGR5UiwgZHlDLCBkKTtcbiAgICAgICAgICAgICAgZG90UHJvZCArPSBwaXhlbCAqIG1hc2s7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGR4LnNldChkb3RQcm9kLCBkeFIsIGR4QywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGR4O1xuICB9XG5cbiAgcHJvdGVjdGVkIG1pblBvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnBvb2woeCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnbWluJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXZnUG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIHRoaXMucG9vbCh4LCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdhdmcnKTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRCB7XG4gICAgY29uc3Qgb3V0cHV0ID0gQXJyYXkzRC56ZXJvcyhbbmV3U2hhcGUyRFswXSwgbmV3U2hhcGUyRFsxXSwgeC5zaGFwZVsyXV0pO1xuXG4gICAgY29uc3QgZWZmZWN0aXZlSW5wdXRTaXplID1cbiAgICAgICAgYWxpZ25Db3JuZXJzID8gW3guc2hhcGVbMF0gLSAxLCB4LnNoYXBlWzFdIC0gMSwgeC5zaGFwZVsyXV0gOiB4LnNoYXBlO1xuICAgIGNvbnN0IGVmZmVjdGl2ZU91dHB1dFNpemUgPSBhbGlnbkNvcm5lcnMgP1xuICAgICAgICBbb3V0cHV0LnNoYXBlWzBdIC0gMSwgb3V0cHV0LnNoYXBlWzFdIC0gMSwgb3V0cHV0LnNoYXBlWzJdXSA6XG4gICAgICAgIG91dHB1dC5zaGFwZTtcbiAgICBmb3IgKGxldCByID0gMDsgciA8IG91dHB1dC5zaGFwZVswXTsgcisrKSB7XG4gICAgICBmb3IgKGxldCBjID0gMDsgYyA8IG91dHB1dC5zaGFwZVsxXTsgYysrKSB7XG4gICAgICAgIGZvciAobGV0IGQgPSAwOyBkIDwgb3V0cHV0LnNoYXBlWzJdOyBkKyspIHtcbiAgICAgICAgICAvLyBCZWdpbiBzaGFkZXIuXG5cbiAgICAgICAgICAvLyBDb21wdXRlIHRoZSBmcmFjdGlvbmFsIGluZGV4IG9mIHRoZSBzb3VyY2UuXG4gICAgICAgICAgY29uc3Qgc291cmNlRnJhY1JvdyA9XG4gICAgICAgICAgICAgIChlZmZlY3RpdmVJbnB1dFNpemVbMF0pICogciAvIChlZmZlY3RpdmVPdXRwdXRTaXplWzBdKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VGcmFjQ29sID1cbiAgICAgICAgICAgICAgKGVmZmVjdGl2ZUlucHV0U2l6ZVsxXSkgKiBjIC8gKGVmZmVjdGl2ZU91dHB1dFNpemVbMV0pO1xuXG4gICAgICAgICAgY29uc3Qgc291cmNlUm93Rmxvb3IgPSBNYXRoLmZsb29yKHNvdXJjZUZyYWNSb3cpO1xuICAgICAgICAgIGNvbnN0IHNvdXJjZVJvd0NlaWwgPVxuICAgICAgICAgICAgICBNYXRoLm1pbih4LnNoYXBlWzBdIC0gMSwgTWF0aC5jZWlsKHNvdXJjZUZyYWNSb3cpKTtcbiAgICAgICAgICBjb25zdCBzb3VyY2VDb2xGbG9vciA9IE1hdGguZmxvb3Ioc291cmNlRnJhY0NvbCk7XG4gICAgICAgICAgY29uc3Qgc291cmNlQ29sQ2VpbCA9XG4gICAgICAgICAgICAgIE1hdGgubWluKHguc2hhcGVbMV0gLSAxLCBNYXRoLmNlaWwoc291cmNlRnJhY0NvbCkpO1xuXG4gICAgICAgICAgY29uc3QgdG9wTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xGbG9vciwgZCk7XG4gICAgICAgICAgY29uc3QgYm90dG9tTGVmdCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbEZsb29yLCBkKTtcbiAgICAgICAgICBjb25zdCB0b3BSaWdodCA9IHguZ2V0KHNvdXJjZVJvd0Zsb29yLCBzb3VyY2VDb2xDZWlsLCBkKTtcbiAgICAgICAgICBjb25zdCBib3R0b21SaWdodCA9IHguZ2V0KHNvdXJjZVJvd0NlaWwsIHNvdXJjZUNvbENlaWwsIGQpO1xuXG4gICAgICAgICAgY29uc3Qgcm93RnJhYyA9IHNvdXJjZUZyYWNSb3cgLSBzb3VyY2VSb3dGbG9vcjtcbiAgICAgICAgICBjb25zdCBjb2xGcmFjID0gc291cmNlRnJhY0NvbCAtIHNvdXJjZUNvbEZsb29yO1xuXG4gICAgICAgICAgY29uc3QgdG9wID0gdG9wTGVmdCArICh0b3BSaWdodCAtIHRvcExlZnQpICogY29sRnJhYztcbiAgICAgICAgICBjb25zdCBib3R0b20gPSBib3R0b21MZWZ0ICsgKGJvdHRvbVJpZ2h0IC0gYm90dG9tTGVmdCkgKiBjb2xGcmFjO1xuICAgICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gdG9wICsgKGJvdHRvbSAtIHRvcCkgKiByb3dGcmFjO1xuXG4gICAgICAgICAgb3V0cHV0LnNldChuZXdWYWx1ZSwgciwgYywgZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG5cbiAgcHJvdGVjdGVkIGJhdGNoTm9ybWFsaXphdGlvbjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBtZWFuOiBBcnJheTNEfEFycmF5MUQsIHZhcmlhbmNlOiBBcnJheTNEfEFycmF5MUQsXG4gICAgICB2YXJpYW5jZUVwc2lsb24gPSAuMDAxLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhWYWx1ZXMgPSB4LmdldFZhbHVlcygpO1xuICAgIGNvbnN0IG1lYW5WYWx1ZXMgPSBtZWFuLmdldFZhbHVlcygpO1xuICAgIGNvbnN0IHZhcmlhbmNlVmFsdWVzID0gdmFyaWFuY2UuZ2V0VmFsdWVzKCk7XG4gICAgY29uc3Qgc2NhbGVWYWx1ZXMgPSBzY2FsZSA/IHNjYWxlLmdldFZhbHVlcygpIDogbmV3IEZsb2F0MzJBcnJheShbMV0pO1xuICAgIGNvbnN0IG9mZnNldFZhbHVlcyA9IG9mZnNldCA/IG9mZnNldC5nZXRWYWx1ZXMoKSA6IG5ldyBGbG9hdDMyQXJyYXkoWzBdKTtcbiAgICBjb25zdCBvdXRWYWx1ZXMgPSBuZXcgRmxvYXQzMkFycmF5KHhWYWx1ZXMubGVuZ3RoKTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgeFZhbHVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgb3V0VmFsdWVzW2ldID0gb2Zmc2V0VmFsdWVzW2kgJSBvZmZzZXRWYWx1ZXMubGVuZ3RoXSArXG4gICAgICAgICAgKHhWYWx1ZXNbaV0gLSBtZWFuVmFsdWVzW2kgJSBtZWFuVmFsdWVzLmxlbmd0aF0pICpcbiAgICAgICAgICAgICAgc2NhbGVWYWx1ZXNbaSAlIHNjYWxlVmFsdWVzLmxlbmd0aF0gL1xuICAgICAgICAgICAgICBNYXRoLnNxcnQoXG4gICAgICAgICAgICAgICAgICB2YXJpYW5jZVZhbHVlc1tpICUgdmFyaWFuY2VWYWx1ZXMubGVuZ3RoXSArIHZhcmlhbmNlRXBzaWxvbik7XG4gICAgfVxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oeC5zaGFwZSwge3ZhbHVlczogb3V0VmFsdWVzfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0ICogYXMgY29uY2F0M2RfdXRpbCBmcm9tICcuL2NvbmNhdDNkX3V0aWwnO1xuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4vY29udl91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuL21hdGgnO1xuaW1wb3J0ICogYXMgbmRhcnJheSBmcm9tICcuL25kYXJyYXknO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBBcnJheTNELCBBcnJheTRELCBOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4vbmRhcnJheSc7XG5pbXBvcnQgKiBhcyBhZGRzY2FsZWRtYXRfZ3B1IGZyb20gJy4vd2ViZ2wvYWRkc2NhbGVkbWF0X2dwdSc7XG5pbXBvcnQgKiBhcyBhZGRzdWJtdWxkaXZfZ3B1IGZyb20gJy4vd2ViZ2wvYWRkc3VibXVsZGl2X2dwdSc7XG5pbXBvcnQge09wZXJhbmRUeXBlfSBmcm9tICcuL3dlYmdsL2FkZHN1Ym11bGRpdl9ncHUnO1xuaW1wb3J0ICogYXMgYXJnbWF4ZXF1YWxzX2dwdSBmcm9tICcuL3dlYmdsL2FyZ21heGVxdWFsc19ncHUnO1xuaW1wb3J0ICogYXMgYXJnbWlubWF4X2dwdSBmcm9tICcuL3dlYmdsL2FyZ21pbm1heF9ncHUnO1xuaW1wb3J0ICogYXMgYXZnX3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvYXZnX3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIGJhdGNobm9ybV9ncHUgZnJvbSAnLi93ZWJnbC9iYXRjaG5vcm1fZ3B1JztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX2dwdSBmcm9tICcuL3dlYmdsL2NvbmNhdDNkX2dwdSc7XG5pbXBvcnQgKiBhcyBjb252X2JhY2twcm9wX2dwdSBmcm9tICcuL3dlYmdsL2NvbnZfYmFja3Byb3BfZ3B1JztcbmltcG9ydCAqIGFzIGNvbnZfZ3B1IGZyb20gJy4vd2ViZ2wvY29udl9ncHUnO1xuaW1wb3J0ICogYXMgY29weV9ncHUgZnJvbSAnLi93ZWJnbC9jb3B5X2dwdSc7XG5pbXBvcnQgKiBhcyBleHBfZ3B1IGZyb20gJy4vd2ViZ2wvZXhwX2dwdSc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi93ZWJnbC9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIGdwZ3B1X3V0aWwgZnJvbSAnLi93ZWJnbC9ncGdwdV91dGlsJztcbmltcG9ydCAqIGFzIGxvZ19ncHUgZnJvbSAnLi93ZWJnbC9sb2dfZ3B1JztcbmltcG9ydCAqIGFzIGxvZ3N1bWV4cF9ncHUgZnJvbSAnLi93ZWJnbC9sb2dzdW1leHBfZ3B1JztcbmltcG9ydCAqIGFzIG1heF9wb29sX2JhY2twcm9wX2dwdSBmcm9tICcuL3dlYmdsL21heF9wb29sX2JhY2twcm9wX2dwdSc7XG5pbXBvcnQgKiBhcyBtYXhfcG9vbF9ncHUgZnJvbSAnLi93ZWJnbC9tYXhfcG9vbF9ncHUnO1xuaW1wb3J0ICogYXMgbWluX3Bvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvbWluX3Bvb2xfZ3B1JztcbmltcG9ydCAqIGFzIG1pbm1heF9ncHUgZnJvbSAnLi93ZWJnbC9taW5tYXhfZ3B1JztcbmltcG9ydCAqIGFzIG11bG1hdF9ncHUgZnJvbSAnLi93ZWJnbC9tdWxtYXRfZ3B1JztcbmltcG9ydCAqIGFzIG5lZ19ncHUgZnJvbSAnLi93ZWJnbC9uZWdfZ3B1JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vd2ViZ2wvcG9vbF9ncHUnO1xuaW1wb3J0ICogYXMgcmVkdWNlc3VtX2dwdSBmcm9tICcuL3dlYmdsL3JlZHVjZXN1bV9ncHUnO1xuaW1wb3J0ICogYXMgcmVsdV9ncHUgZnJvbSAnLi93ZWJnbC9yZWx1X2dwdSc7XG5pbXBvcnQgKiBhcyByZXNoYXBlX2dwdSBmcm9tICcuL3dlYmdsL3Jlc2hhcGVfZ3B1JztcbmltcG9ydCAqIGFzIHJlc2l6ZV9iaWxpbmVhcl9ncHUgZnJvbSAnLi93ZWJnbC9yZXNpemVfYmlsaW5lYXJfZ3B1JztcbmltcG9ydCAqIGFzIHNoYWRlcl9jb21waWxlciBmcm9tICcuL3dlYmdsL3NoYWRlcl9jb21waWxlcic7XG5pbXBvcnQgKiBhcyBzaWdtb2lkX2dwdSBmcm9tICcuL3dlYmdsL3NpZ21vaWRfZ3B1JztcbmltcG9ydCAqIGFzIHN0ZXBfZ3B1IGZyb20gJy4vd2ViZ2wvc3RlcF9ncHUnO1xuaW1wb3J0IHtUZXh0dXJlTWFuYWdlcn0gZnJvbSAnLi93ZWJnbC90ZXh0dXJlX21hbmFnZXInO1xuaW1wb3J0ICogYXMgdHJpZ19ncHUgZnJvbSAnLi93ZWJnbC90cmlnX2dwdSc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbmNvbnN0IEFSR01BWF9QUk9HID0gJ2FyZ21heCc7XG5jb25zdCBBUkdNQVhfRVFVQUxTX1BST0cgPSAnYXJnbWF4ZXF1YWxzJztcbmNvbnN0IEFSR01JTl9QUk9HID0gJ2FyZ21pbic7XG5cbmNvbnN0IEJBVENITk9STV9QUk9HID0gJ2JhdGNobm9ybSc7XG5cbmNvbnN0IENPUFlfUFJPRyA9ICdjb3B5JztcbmNvbnN0IENPTkNBVF9QUk9HID0gJ2NvbmNhdCc7XG5cbi8vIE1hdHJpeCBhbGdlYnJhLlxuY29uc3QgQUREX1NDQUxFRF9NQVRfUFJPRyA9ICdhZGRzY2FsZWRtYXQnO1xuY29uc3QgTUFUTVVMX1BST0cgPSAnbWF0bXVsJztcblxuLy8gRWxlbWVudC13aXNlIG9wcy5cbmNvbnN0IFJFTFVfUFJPRyA9ICdyZWx1JztcbmNvbnN0IFRBTkhfUFJPRyA9ICd0YW5oJztcbmNvbnN0IFNJTl9QUk9HID0gJ3Npbic7XG5jb25zdCBTSUdNT0lEX1BST0cgPSAnc2lnbW9pZCc7XG5jb25zdCBNQVhfUFJPRyA9ICdtYXgnO1xuY29uc3QgTUlOX1BST0cgPSAnbWluJztcbmNvbnN0IE5FR19QUk9HID0gJ25lZyc7XG5jb25zdCBFWFBfUFJPRyA9ICdleHAnO1xuY29uc3QgTE9HX1BST0cgPSAnbG9nJztcbmNvbnN0IFNVTV9QUk9HID0gJ3N1bSc7XG5jb25zdCBTVEVQX1BST0cgPSAnc3RlcCc7XG5jb25zdCBMT0dTVU1FWFBfUFJPRyA9ICdsb2dzdW1leHAnO1xuY29uc3QgUkVTSEFQRV9QUk9HID0gJ3Jlc2hhcGUnO1xuY29uc3QgQUREX1NVTV9NVUxfRElWX1BST0cgPSAnYWRkc3VtbXVsZGl2JztcblxuLy8gQ29udm9sdXRpb24uXG5jb25zdCBDT05WMkRfUFJPRyA9ICdjb252JztcbmNvbnN0IENPTlYyRF9UUkFOU1BPU0VfUFJPRyA9ICdjb252X3RyYW5zcG9zZSc7XG5jb25zdCBDT05WMkRfREVSV19QUk9HID0gJ2NvbnZfZGVydyc7XG5jb25zdCBDT05WMkRfREVSQl9QUk9HID0gJ2NvbnZfZGVyYic7XG5jb25zdCBNQVhfUE9PTF9QUk9HID0gJ21heHBvb2wnO1xuY29uc3QgTUFYX1BPT0xfUE9TSVRJT05TX1BST0cgPSAnbWF4cG9vbF9wb3NuJztcbmNvbnN0IE1BWF9QT09MX0JBQ0tQUk9QX1BST0cgPSAnbWF4cG9vbF9iYWNrcHJvcCc7XG5jb25zdCBNSU5fUE9PTF9QUk9HID0gJ21pbnBvb2wnO1xuY29uc3QgQVZHX1BPT0xfUFJPRyA9ICdhdmdwb29sJztcblxuY29uc3QgUkVTSVpFX0JJTElORUFSX1BST0cgPSAncmVzaXplYmlsaW4nO1xuXG5mdW5jdGlvbiBtYWtlQ29weVByb2dyYW1OYW1lKFxuICAgIHNvdXJjZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGRlc3RTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgY29uc3Qgc2hhcGVOYW1lID0gYCR7c291cmNlU2hhcGVSb3dDb2xbMF19XyR7c291cmNlU2hhcGVSb3dDb2xbMV19YDtcbiAgY29uc3Qgc3JjU2l6ZU5hbWUgPSBgJHtzb3VyY2VTaXplUm93Q29sWzBdfV8ke3NvdXJjZVNpemVSb3dDb2xbMV19YDtcbiAgY29uc3QgZHN0U2l6ZU5hbWUgPSBgJHtkZXN0U2l6ZVJvd0NvbFswXX1fJHtkZXN0U2l6ZVJvd0NvbFsxXX1gO1xuICByZXR1cm4gYCR7Q09QWV9QUk9HfV8ke3NoYXBlTmFtZX1fJHtzcmNTaXplTmFtZX1fJHtkc3RTaXplTmFtZX1gO1xufVxuXG5leHBvcnQgY2xhc3MgTkRBcnJheU1hdGhHUFUgZXh0ZW5kcyBOREFycmF5TWF0aCB7XG4gIHByaXZhdGUgZ3BncHU6IEdQR1BVQ29udGV4dDtcbiAgcHJpdmF0ZSB0ZXh0dXJlTWFuYWdlcjogVGV4dHVyZU1hbmFnZXI7XG4gIHByaXZhdGUgcHJvZ3JhbUNhY2hlOiB7W2tleTogc3RyaW5nXTogV2ViR0xQcm9ncmFtfSA9IHt9O1xuICBwcml2YXRlIGdwZ3B1Q3JlYXRlZExvY2FsbHk6IGJvb2xlYW47XG5cbiAgY29uc3RydWN0b3IoZ3BncHU/OiBHUEdQVUNvbnRleHQsIHNhZmVNb2RlID0gdHJ1ZSkge1xuICAgIHN1cGVyKHNhZmVNb2RlKTtcbiAgICBpZiAoZ3BncHUgPT0gbnVsbCkge1xuICAgICAgY29uc3QgZ2wgPSBncGdwdV91dGlsLmNyZWF0ZVdlYkdMQ29udGV4dCgpO1xuICAgICAgdGhpcy5ncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoZ2wpO1xuICAgICAgdGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5ID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5ncGdwdSA9IGdwZ3B1O1xuICAgICAgdGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5ID0gZmFsc2U7XG4gICAgfVxuXG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlciA9IG5ldyBUZXh0dXJlTWFuYWdlcih0aGlzLmdwZ3B1KTtcblxuICAgIG5kYXJyYXkuaW5pdGlhbGl6ZUdQVSh0aGlzLmdwZ3B1LCB0aGlzLnRleHR1cmVNYW5hZ2VyKTtcbiAgfVxuXG4gIGdldEdQR1BVQ29udGV4dCgpOiBHUEdQVUNvbnRleHQge1xuICAgIHJldHVybiB0aGlzLmdwZ3B1O1xuICB9XG5cbiAgcHJvdGVjdGVkIGNsb25lSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgbWFrZUNvcHlQcm9ncmFtTmFtZSh0ZXh0dXJlU2hhcGVSQywgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDKSxcbiAgICAgICAgKCkgPT4gY29weV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB0ZXh0dXJlU2hhcGVSQywgdGV4dHVyZVNoYXBlUkMsIHRleHR1cmVTaGFwZVJDKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBjb3B5X2dwdS5jb3B5KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkMsIFswLCAwXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkMsIHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDLCBbMCwgMF0sIHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHJlc2hhcGVJbnRlcm5hbDxUMSBleHRlbmRzIE5EQXJyYXksIFQyIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBuZGFycmF5OiBUMSwgbmV3U2hhcGU6IG51bWJlcltdKTogVDIge1xuICAgIGxldCBuZXdUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXTtcblxuICAgIHN3aXRjaCAobmV3U2hhcGUubGVuZ3RoKSB7XG4gICAgICBjYXNlIDA6XG4gICAgICAgIG5ld1RleFNoYXBlID0gWzEsIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMTpcbiAgICAgICAgbmV3VGV4U2hhcGUgPSBbbmV3U2hhcGVbMF0sIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgbmV3VGV4U2hhcGUgPSBbbmV3U2hhcGVbMF0sIG5ld1NoYXBlWzFdXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDM6XG4gICAgICAgIG5ld1RleFNoYXBlID0gW25ld1NoYXBlWzBdLCBuZXdTaGFwZVsxXSAqIG5ld1NoYXBlWzJdXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBFcnJvcihcbiAgICAgICAgICAgIGBSZXNoYXBlcyBpbnRvICR7bmV3U2hhcGUubGVuZ3RofS1kaW0gbmRhcnJheSBpcyBub3QgeWV0IGAgK1xuICAgICAgICAgICAgYHN1cHBvcnRlZCBvbiBHUFVgKTtcbiAgICB9XG5cbiAgICBjb25zdCBhY3R1YWxUZXhTaGFwZSA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMobmV3VGV4U2hhcGUpO1xuICAgIGxldCBjbG9uZWRBcnJheTogVDE7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFRleFNoYXBlLCBuZXdUZXhTaGFwZSkpIHtcbiAgICAgIGNsb25lZEFycmF5ID0gdGhpcy5yZXNoYXBlVGV4dHVyZShuZGFycmF5LCBuZXdUZXhTaGFwZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNsb25lZEFycmF5ID0gdGhpcy5jbG9uZUludGVybmFsKG5kYXJyYXkpO1xuICAgIH1cbiAgICByZXR1cm4gY2xvbmVkQXJyYXkucmVzaGFwZTxUMj4obmV3U2hhcGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNsaWNlMkRJbnRlcm5hbChcbiAgICAgIGlucHV0OiBBcnJheTJELCBiZWdpblJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICAgIHNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICBjb25zdCByZXN1bHQgPSBOREFycmF5Lm1ha2U8QXJyYXkyRD4oc2l6ZVJvd0NvbCwge1xuICAgICAgdGV4dHVyZTogdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShzaXplUm93Q29sKSxcbiAgICAgIHRleHR1cmVTaGFwZVJDOiBzaXplUm93Q29sXG4gICAgfSk7XG4gICAgdGhpcy5jb3B5MkRJbnRlcm5hbChcbiAgICAgICAgaW5wdXQsIGJlZ2luUm93Q29sLCBzaXplUm93Q29sLCByZXN1bHQsIFswLCAwXSwgc2l6ZVJvd0NvbCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb3B5MkRJbnRlcm5hbChcbiAgICAgIHNvdXJjZTogQXJyYXkyRCwgc291cmNlQmVnaW5Sb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBBcnJheTJELFxuICAgICAgZGVzdEJlZ2luUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pOiB2b2lkIHtcbiAgICBjb25zdCBzb3VyY2VTaGFwZVJDID0gc291cmNlLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgZGVzdFNoYXBlUkMgPSBkZXN0LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIG1ha2VDb3B5UHJvZ3JhbU5hbWUoc291cmNlU2hhcGVSQywgc291cmNlU2l6ZVJvd0NvbCwgZGVzdFNpemVSb3dDb2wpLFxuICAgICAgICAoKSA9PiBjb3B5X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHNvdXJjZVNoYXBlUkMsIHNvdXJjZVNpemVSb3dDb2wsIGRlc3RTaXplUm93Q29sKSk7XG5cbiAgICBjb3B5X2dwdS5jb3B5KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBzb3VyY2UuZ2V0VGV4dHVyZSgpLCBzb3VyY2VTaGFwZVJDLFxuICAgICAgICBzb3VyY2VCZWdpblJvd0NvbCwgc291cmNlU2l6ZVJvd0NvbCwgZGVzdC5nZXRUZXh0dXJlKCksIGRlc3RTaGFwZVJDLFxuICAgICAgICBkZXN0QmVnaW5Sb3dDb2wsIGRlc3RTaXplUm93Q29sKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBjb25jYXQzREludGVybmFsKHgxOiBBcnJheTNELCB4MjogQXJyYXkzRCwgYXhpczogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgeDFUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4MS5zaGFwZSk7XG4gICAgY29uc3QgeDJUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4Mi5zaGFwZSk7XG5cbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCB0aGUgc2hhcGVzIHRoYXQgc2hhZGVycyBleHBlY3QsXG4gICAgLy8gZG8gcGh5c2ljYWwgdGV4dHVyZSByZXNoYXBlcyBvbiB0aGUgR1BVLlxuICAgIGNvbnN0IGFjdHVhbFgxVGV4U2hhcGUgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQyh4MVRleFNoYXBlUkMpO1xuICAgIGxldCBjbGVhbnVwWDEgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWDFUZXhTaGFwZSwgeDFUZXhTaGFwZVJDKSkge1xuICAgICAgeDEgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgxLCB4MVRleFNoYXBlUkMpO1xuICAgICAgY2xlYW51cFgxID0gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3QgYWN0dWFsWDJUZXhTaGFwZSA9IHgyLmdldFRleHR1cmVTaGFwZVJDKHgyVGV4U2hhcGVSQyk7XG4gICAgbGV0IGNsZWFudXBYMiA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYMlRleFNoYXBlLCB4MlRleFNoYXBlUkMpKSB7XG4gICAgICB4MiA9IHRoaXMucmVzaGFwZVRleHR1cmUoeDIsIHgyVGV4U2hhcGVSQyk7XG4gICAgICBjbGVhbnVwWDIgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlUkNEID1cbiAgICAgICAgY29uY2F0M2RfdXRpbC5jb21wdXRlQ29uY2F0M0RPdXRwdXRTaGFwZSh4MS5zaGFwZSwgeDIuc2hhcGUsIGF4aXMpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0NPTkNBVF9QUk9HfV8ke3gxLnNoYXBlfV8ke3gyLnNoYXBlfV8ke2F4aXN9YCxcbiAgICAgICAgKCkgPT4gY29uY2F0M2RfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgeDEuc2hhcGUsIHgyLnNoYXBlLCByZXN1bHRTaGFwZVJDRCwgYXhpcykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHJlc3VsdFNoYXBlUkNEKTtcbiAgICBjb25zdCByZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGNvbmNhdDNkX2dwdS5jb25jYXQzRChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeDEuZ2V0VGV4dHVyZSgpLCB4Mi5nZXRUZXh0dXJlKCksIHJlc3VsdFRleCxcbiAgICAgICAgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYMSkge1xuICAgICAgeDEuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGlmIChjbGVhbnVwWDIpIHtcbiAgICAgIHgyLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZVJDRCwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyUGx1c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYywgYSwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuU0NBTEFSLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcnJheU1pbnVzU2NhbGFySW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGM6IFNjYWxhcik6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYSwgYywgYS5zaGFwZSwgT3BlcmFuZFR5cGUuTUFUUklYLCAnLScsIE9wZXJhbmRUeXBlLlNDQUxBUikgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsYXJNaW51c0FycmF5SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGM6IFNjYWxhciwgYTogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYywgYSwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuU0NBTEFSLCAnLScsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBzY2FsZWRBcnJheUFkZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihcbiAgICAgIGMxOiBTY2FsYXIsIGE6IFQsIGMyOiBTY2FsYXIsIGI6IFQpIHtcbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoIXRoaXMuZG9HUFVTaGFwZXNNYXRjaChhLCBiKSkge1xuICAgICAgYiA9IHRoaXMucmVzaGFwZVRleHR1cmUoYiwgYS5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgICAgIGNsZWFudXBCID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgQUREX1NDQUxFRF9NQVRfUFJPRywgKCkgPT4gYWRkc2NhbGVkbWF0X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIGFkZHNjYWxlZG1hdF9ncHUuYWRkU2NhbGVkTWF0cmljZXMoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCBiLmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCBjMS5nZXRUZXh0dXJlKCksIGMyLmdldFRleHR1cmUoKSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICBpZiAoY2xlYW51cEIpIHtcbiAgICAgIGIuZGlzcG9zZSgpO1xuICAgIH1cbiAgICAvLyBCcmluZyB0aGUgcmVzdWx0IGJhY2sgdG8gdGhlIG9yaWdpbmFsIHNoYXBlLlxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oYS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyVGltZXNBcnJheUludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihjOiBTY2FsYXIsIGE6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJyonLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgbmVnSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgTkVHX1BST0csICgpID0+IG5lZ19ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBuZWdfZ3B1Lm5lZyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgYS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJpdmF0ZSByZXNoYXBlVGV4dHVyZTxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgbmV3VGV4dHVyZVNoYXBlOiBbXG4gICAgbnVtYmVyLCBudW1iZXJcbiAgXSk6IFQge1xuICAgIGNvbnN0IGFUZXhTaGFwZSA9IGEuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBSRVNIQVBFX1BST0csICgpID0+IHJlc2hhcGVfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUobmV3VGV4dHVyZVNoYXBlKTtcbiAgICByZXNoYXBlX2dwdS5yZXNoYXBlKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYVRleFNoYXBlWzBdLCBhVGV4U2hhcGVbMV0sXG4gICAgICAgIHJlc3VsdFRleHR1cmUsIG5ld1RleHR1cmVTaGFwZVswXSwgbmV3VGV4dHVyZVNoYXBlWzFdKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIGEuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQzogbmV3VGV4dHVyZVNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF0TXVsSW50ZXJuYWwoXG4gICAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBhT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uLFxuICAgICAgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IEFycmF5MkQge1xuICAgIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAgIChhT3JpZW50YXRpb24gPT09IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF07XG4gICAgY29uc3Qgb3V0ZXJTaGFwZUEgPVxuICAgICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/IGEuc2hhcGVbMF0gOiBhLnNoYXBlWzFdO1xuICAgIGNvbnN0IG91dGVyU2hhcGVCID1cbiAgICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyBiLnNoYXBlWzFdIDogYi5zaGFwZVswXTtcbiAgICBjb25zdCBvdXRTaGFwZTogW251bWJlciwgbnVtYmVyXSA9IFtvdXRlclNoYXBlQSwgb3V0ZXJTaGFwZUJdO1xuICAgIGNvbnN0IG91dFRleFNoYXBlID1cbiAgICAgICAgd2ViZ2xfdXRpbC5nZXRUZXh0dXJlU2hhcGVGcm9tTG9naWNhbFNoYXBlKHRoaXMuZ3BncHUuZ2wsIG91dFNoYXBlKTtcbiAgICBjb25zdCBvdXRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShvdXRUZXhTaGFwZSk7XG4gICAgY29uc3Qgb3V0ID0gbmV3IEFycmF5MkQoXG4gICAgICAgIG91dFNoYXBlLCB7dGV4dHVyZTogb3V0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IG91dFRleFNoYXBlfSk7XG5cbiAgICBjb25zdCBrZXkgPSBzaGFkZXJfY29tcGlsZXIubWFrZVNoYWRlcktleShbYSwgYl0sIG91dCk7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke01BVE1VTF9QUk9HfV8ke2tleX1fJHthT3JpZW50YXRpb259XyR7Yk9yaWVudGF0aW9ufWAsXG4gICAgICAgICgpID0+IG11bG1hdF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXIoXG4gICAgICAgICAgICBhLCBiLCBvdXQsIGFPcmllbnRhdGlvbiwgYk9yaWVudGF0aW9uKSk7XG5cbiAgICBtdWxtYXRfZ3B1Lm11bHRpcGx5TWF0cml4KFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBhLmdldFRleHR1cmUoKSwgYi5nZXRUZXh0dXJlKCksIG91dFRleHR1cmUsXG4gICAgICAgIG91dFRleFNoYXBlKTtcblxuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZWxlbWVudFdpc2VNdWxJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYjogVCk6IFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgYSwgYiwgYS5zaGFwZSwgT3BlcmFuZFR5cGUuTUFUUklYLCAnKicsIE9wZXJhbmRUeXBlLk1BVFJJWCkgYXMgVDtcbiAgfVxuXG4gIHByb3RlY3RlZCBlbGVtZW50V2lzZU11bEJyb2FkY2FzdEludGVybmFsKGE6IEFycmF5MkQsIGI6IEFycmF5MkQpOiBBcnJheTJEIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCB5ZXQgaW1wbGVtZW50ZWQhJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYmF0Y2hOb3JtYWxpemF0aW9uM0RJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIG1lYW46IEFycmF5M0R8QXJyYXkxRCwgdmFyaWFuY2U6IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIHZhcmlhbmNlRXBzaWxvbjogbnVtYmVyLCBzY2FsZT86IEFycmF5M0R8QXJyYXkxRCxcbiAgICAgIG9mZnNldD86IEFycmF5M0R8QXJyYXkxRCk6IEFycmF5M0Qge1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IHguZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGxldCBjbGVhbnVwTWVhbiA9IGZhbHNlO1xuICAgIGNvbnN0IHByZWZlcnJlZE1lYW5UZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIG1lYW4ucmFuayA9PT0gMSA/IFsxLCBtZWFuLnNpemVdIDogeFRleFNoYXBlO1xuICAgIGxldCBtZWFuVGV4U2hhcGUgPSBtZWFuLmdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZE1lYW5UZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKG1lYW5UZXhTaGFwZSwgcHJlZmVycmVkTWVhblRleFNoYXBlKSkge1xuICAgICAgbWVhbiA9IHRoaXMucmVzaGFwZVRleHR1cmUobWVhbiwgcHJlZmVycmVkTWVhblRleFNoYXBlKTtcbiAgICAgIG1lYW5UZXhTaGFwZSA9IHByZWZlcnJlZE1lYW5UZXhTaGFwZTtcbiAgICAgIGNsZWFudXBNZWFuID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFZhcmlhbmNlID0gZmFsc2U7XG4gICAgY29uc3QgcHJlZmVycmVkVmFyaWFuY2VUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSA9XG4gICAgICAgIHZhcmlhbmNlLnJhbmsgPT09IDEgPyBbMSwgdmFyaWFuY2Uuc2l6ZV0gOiB4VGV4U2hhcGU7XG4gICAgbGV0IHZhcmlhbmNlVGV4U2hhcGUgPSB2YXJpYW5jZS5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRNZWFuVGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbCh2YXJpYW5jZVRleFNoYXBlLCBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlKSkge1xuICAgICAgdmFyaWFuY2UgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHZhcmlhbmNlLCBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlKTtcbiAgICAgIHZhcmlhbmNlVGV4U2hhcGUgPSBwcmVmZXJyZWRWYXJpYW5jZVRleFNoYXBlO1xuICAgICAgY2xlYW51cFZhcmlhbmNlID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgc2NhbGVUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXXxudWxsID0gbnVsbDtcbiAgICBsZXQgY2xlYW51cFNjYWxlID0gZmFsc2U7XG4gICAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IHByZWZlcnJlZFNjYWxlVGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPVxuICAgICAgICAgIHNjYWxlLnJhbmsgPT09IDEgPyBbMSwgc2NhbGUuc2l6ZV0gOiB4VGV4U2hhcGU7XG5cbiAgICAgIHNjYWxlVGV4U2hhcGUgPSBzY2FsZS5nZXRUZXh0dXJlU2hhcGVSQyhwcmVmZXJyZWRTY2FsZVRleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChzY2FsZVRleFNoYXBlLCBwcmVmZXJyZWRTY2FsZVRleFNoYXBlKSkge1xuICAgICAgICBzY2FsZSA9IHRoaXMucmVzaGFwZVRleHR1cmUoc2NhbGUsIHByZWZlcnJlZFNjYWxlVGV4U2hhcGUpO1xuICAgICAgICBzY2FsZVRleFNoYXBlID0gcHJlZmVycmVkU2NhbGVUZXhTaGFwZTtcbiAgICAgICAgY2xlYW51cFNjYWxlID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsZXQgb2Zmc2V0VGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl18bnVsbCA9IG51bGw7XG4gICAgbGV0IGNsZWFudXBPZmZzZXQgPSBmYWxzZTtcbiAgICBpZiAob2Zmc2V0ICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IHByZWZlcnJlZE9mZnNldFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgICBvZmZzZXQucmFuayA9PT0gMSA/IFsxLCBvZmZzZXQuc2l6ZV0gOiB4VGV4U2hhcGU7XG5cbiAgICAgIG9mZnNldFRleFNoYXBlID0gb2Zmc2V0LmdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZE9mZnNldFRleFNoYXBlKTtcbiAgICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChvZmZzZXRUZXhTaGFwZSwgcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGUpKSB7XG4gICAgICAgIG9mZnNldCA9IHRoaXMucmVzaGFwZVRleHR1cmUob2Zmc2V0LCBwcmVmZXJyZWRPZmZzZXRUZXhTaGFwZSk7XG4gICAgICAgIG9mZnNldFRleFNoYXBlID0gcHJlZmVycmVkT2Zmc2V0VGV4U2hhcGU7XG4gICAgICAgIGNsZWFudXBPZmZzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID0geC5nZXRUZXh0dXJlU2hhcGVSQygpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0JBVENITk9STV9QUk9HfV8ke3hUZXhTaGFwZX1fJHttZWFuVGV4U2hhcGV9XyR7dmFyaWFuY2VUZXhTaGFwZX1fYCArXG4gICAgICAgICAgICBgJHtzY2FsZVRleFNoYXBlIX1fJHtvZmZzZXRUZXhTaGFwZSF9XyR7dmFyaWFuY2VFcHNpbG9ufWAsXG4gICAgICAgICgpID0+IGJhdGNobm9ybV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgICB4VGV4U2hhcGUsIG1lYW5UZXhTaGFwZSwgdmFyaWFuY2VUZXhTaGFwZSwgb2Zmc2V0VGV4U2hhcGUsXG4gICAgICAgICAgICBzY2FsZVRleFNoYXBlLCB2YXJpYW5jZUVwc2lsb24pKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGJhdGNobm9ybV9ncHUuYmF0Y2hOb3JtYWxpemF0aW9uKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgeFRleFNoYXBlLCBtZWFuLmdldFRleHR1cmUoKSxcbiAgICAgICAgbWVhblRleFNoYXBlLCB2YXJpYW5jZS5nZXRUZXh0dXJlKCksIHZhcmlhbmNlVGV4U2hhcGUsXG4gICAgICAgIG9mZnNldCAhPSBudWxsID8gb2Zmc2V0LmdldFRleHR1cmUoKSA6IG51bGwsXG4gICAgICAgIG9mZnNldCAhPSBudWxsID8gb2Zmc2V0VGV4U2hhcGUgOiBudWxsLFxuICAgICAgICBzY2FsZSAhPSBudWxsID8gc2NhbGUuZ2V0VGV4dHVyZSgpIDogbnVsbCxcbiAgICAgICAgc2NhbGUgIT0gbnVsbCA/IHNjYWxlVGV4U2hhcGUgOiBudWxsLCByZXN1bHRUZXh0dXJlLCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cE1lYW4pIHtcbiAgICAgIG1lYW4uZGlzcG9zZSgpO1xuICAgIH1cbiAgICBpZiAoY2xlYW51cFZhcmlhbmNlKSB7XG4gICAgICB2YXJpYW5jZS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwU2NhbGUpIHtcbiAgICAgIHNjYWxlIS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwT2Zmc2V0KSB7XG4gICAgICBvZmZzZXQhLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICB4LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3dpdGNoRGltSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIG5ld0RpbTogbnVtYmVyW10pOiBUIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCB5ZXQgaW1wbGVtZW50ZWQhJyk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3VtSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gdGV4dHVyZVNoYXBlUkM7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgYCR7U1VNX1BST0d9XyR7bnVtUm93c31fJHtudW1Db2x1bW5zfWAsXG4gICAgICAgICgpID0+IHJlZHVjZXN1bV9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UobnVtUm93cywgbnVtQ29sdW1ucykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKTtcblxuICAgIHJlZHVjZXN1bV9ncHUucmVkdWNlU3VtKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgbnVtUm93cywgbnVtQ29sdW1ucyxcbiAgICAgICAgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dGV4dHVyZTogcmVzdWx0VGV4dHVyZX0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGFyZ01pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0FSR01JTl9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBhcmdtaW5tYXhfZ3B1LmdldEFyZ01pbkZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBhcmdtaW5tYXhfZ3B1LmFyZ01pbk1heChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBhcmdNYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtBUkdNQVhfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWlubWF4X2dwdS5nZXRBcmdNYXhGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgYXJnbWlubWF4X2dwdS5hcmdNaW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJnTWF4RXF1YWxzSW50ZXJuYWwoeDE6IE5EQXJyYXksIHgyOiBOREFycmF5KTogU2NhbGFyIHtcbiAgICAvLyBJZiB0aGUgdGV4dHVyZSBzaGFwZXMgZG9lc24ndCBtYXRjaCwgZG8gYSBwaHlzaWNhbCByZXNoYXBlIHNvIHRoZXkgZG8uXG4gICAgY29uc3QgYWN0dWFsWDFUZXhTaGFwZSA9IHgxLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgYWN0dWFsWDJUZXhTaGFwZSA9IHgyLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgbGV0IGNsZWFudXBYMiA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYMVRleFNoYXBlLCBhY3R1YWxYMlRleFNoYXBlKSkge1xuICAgICAgeDIgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgyLCBhY3R1YWxYMVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYMiA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSB4MS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0FSR01BWF9FUVVBTFNfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gYXJnbWF4ZXF1YWxzX2dwdS5nZXRBcmdNYXhFcXVhbHNGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBhcmdtYXhlcXVhbHNfZ3B1LmFyZ01heEVxdWFscyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeDEuZ2V0VGV4dHVyZSgpLCB4Mi5nZXRUZXh0dXJlKCksIG51bVJvd3MsXG4gICAgICAgIG51bUNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgaWYgKGNsZWFudXBYMikge1xuICAgICAgeDIuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgdG9wS0ludGVybmFsKG5kYXJyYXk6IE5EQXJyYXksIGs6IG51bWJlcik6XG4gICAgICB7dmFsdWVzOiBBcnJheTFELCBpbmRpY2VzOiBBcnJheTFEfSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCd0b3BLIEdQVSBub3QgeWV0IGltcGxlbWVudGVkIScpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG1pbkludGVybmFsKG5kYXJyYXk6IE5EQXJyYXkpOiBTY2FsYXIge1xuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IFtudW1Sb3dzLCBudW1Db2x1bW5zXSA9IHRleHR1cmVTaGFwZVJDO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke01JTl9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBtaW5tYXhfZ3B1LmdldE1pbkZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKFsxLCAxXSk7XG5cbiAgICBtaW5tYXhfZ3B1Lm1pbk1heChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIG51bVJvd3MsIG51bUNvbHVtbnMsXG4gICAgICAgIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIG5ldyBTY2FsYXIoe3RleHR1cmU6IHJlc3VsdFRleHR1cmV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtYXhJbnRlcm5hbChuZGFycmF5OiBOREFycmF5KTogU2NhbGFyIHtcbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCBbbnVtUm93cywgbnVtQ29sdW1uc10gPSB0ZXh0dXJlU2hhcGVSQztcblxuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBgJHtNQVhfUFJPR31fJHtudW1Sb3dzfV8ke251bUNvbHVtbnN9YCxcbiAgICAgICAgKCkgPT4gbWlubWF4X2dwdS5nZXRNYXhGcmFnbWVudFNoYWRlclNvdXJjZShudW1Sb3dzLCBudW1Db2x1bW5zKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShbMSwgMV0pO1xuXG4gICAgbWlubWF4X2dwdS5taW5NYXgoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBuZXcgU2NhbGFyKHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgZGl2aWRlSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy8nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2NhbGFyRGl2aWRlZEJ5QXJyYXlJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYzogU2NhbGFyLCBhOiBUKTpcbiAgICAgIFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgICAgICAgIGMsIGEsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLlNDQUxBUiwgJy8nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYXJyYXlEaXZpZGVkQnlTY2FsYXJJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4oYTogVCwgYzogU2NhbGFyKTpcbiAgICAgIFQge1xuICAgIHJldHVybiB0aGlzLmFkZFN1Yk11bERpdihcbiAgICAgICAgICAgICAgIGEsIGMsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy8nLCBPcGVyYW5kVHlwZS5TQ0FMQVIpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgYWRkSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJysnLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgc3ViSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KGE6IFQsIGI6IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5hZGRTdWJNdWxEaXYoXG4gICAgICAgIGEsIGIsIGEuc2hhcGUsIE9wZXJhbmRUeXBlLk1BVFJJWCwgJy0nLCBPcGVyYW5kVHlwZS5NQVRSSVgpIGFzIFQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgbG9nU3VtRXhwSW50ZXJuYWwobmRhcnJheTogTkRBcnJheSk6IFNjYWxhciB7XG4gICAgY29uc3QgW251bVJvd3MsIG51bUNvbHVtbnNdID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuXG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIGAke0xPR1NVTUVYUF9QUk9HfV8ke251bVJvd3N9XyR7bnVtQ29sdW1uc31gLFxuICAgICAgICAoKSA9PiBsb2dzdW1leHBfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKG51bVJvd3MsIG51bUNvbHVtbnMpKTtcblxuICAgIGNvbnN0IHJlc3VsdCA9XG4gICAgICAgIG5ldyBTY2FsYXIoe3RleHR1cmU6IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUoWzEsIDFdKX0pO1xuXG4gICAgcmVkdWNlc3VtX2dwdS5yZWR1Y2VTdW0oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCBudW1Sb3dzLCBudW1Db2x1bW5zLFxuICAgICAgICByZXN1bHQuZ2V0VGV4dHVyZSgpKTtcblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICBwcm90ZWN0ZWQgZXhwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgRVhQX1BST0csICgpID0+IGV4cF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICBleHBfZ3B1LmV4cChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBsb2dJbnRlcm5hbDxUIGV4dGVuZHMgTkRBcnJheT4obmRhcnJheTogVCk6IFQge1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBMT0dfUFJPRywgKCkgPT4gbG9nX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcbiAgICBsb2dfZ3B1LmxvZyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZWx1SW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgUkVMVV9QUk9HLCAoKSA9PiByZWx1X2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHJlbHVfZ3B1LnJlbHUoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2lnbW9pZEludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNJR01PSURfUFJPRywgKCkgPT4gc2lnbW9pZF9ncHUuZ2V0U2lnbW9pZEZyYWdtZW50U2hhZGVyU291cmNlKCkpO1xuXG4gICAgY29uc3QgdGV4dHVyZVNoYXBlUkMgPSBuZGFycmF5LmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUodGV4dHVyZVNoYXBlUkMpO1xuXG4gICAgc2lnbW9pZF9ncHUuc2lnbW9pZChcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCB0YW5oSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgVEFOSF9QUk9HLCAoKSA9PiB0cmlnX2dwdS5nZXRUYW5oRnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgICBjb25zdCB0ZXh0dXJlU2hhcGVSQyA9IG5kYXJyYXkuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZSh0ZXh0dXJlU2hhcGVSQyk7XG5cbiAgICB0cmlnX2dwdS50YW5oKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBuZGFycmF5LmdldFRleHR1cmUoKSwgdGV4dHVyZVNoYXBlUkNbMF0sXG4gICAgICAgIHRleHR1cmVTaGFwZVJDWzFdLCByZXN1bHRUZXh0dXJlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oXG4gICAgICAgIG5kYXJyYXkuc2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXh0dXJlLCB0ZXh0dXJlU2hhcGVSQ30pO1xuICB9XG5cbiAgcHJvdGVjdGVkIHNpbkludGVybmFsPFQgZXh0ZW5kcyBOREFycmF5PihuZGFycmF5OiBUKTogVCB7XG4gICAgY29uc3QgcHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0oXG4gICAgICAgIFNJTl9QUk9HLCAoKSA9PiB0cmlnX2dwdS5nZXRTaW5GcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHRyaWdfZ3B1LnNpbihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgbmRhcnJheS5nZXRUZXh0dXJlKCksIHRleHR1cmVTaGFwZVJDWzBdLFxuICAgICAgICB0ZXh0dXJlU2hhcGVSQ1sxXSwgcmVzdWx0VGV4dHVyZSk7XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KFxuICAgICAgICBuZGFycmF5LnNoYXBlLCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkN9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBzdGVwSW50ZXJuYWw8VCBleHRlbmRzIE5EQXJyYXk+KG5kYXJyYXk6IFQpOiBUIHtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgU1RFUF9QUk9HLCAoKSA9PiBzdGVwX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpKTtcblxuICAgIGNvbnN0IHRleHR1cmVTaGFwZVJDID0gbmRhcnJheS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IHJlc3VsdFRleHR1cmUgPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHRleHR1cmVTaGFwZVJDKTtcblxuICAgIHN0ZXBfZ3B1LnN0ZXAoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIG5kYXJyYXkuZ2V0VGV4dHVyZSgpLCB0ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGV4dHVyZVNoYXBlUkNbMV0sIHJlc3VsdFRleHR1cmUpO1xuXG4gICAgcmV0dXJuIE5EQXJyYXkubWFrZTxUPihcbiAgICAgICAgbmRhcnJheS5zaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleHR1cmUsIHRleHR1cmVTaGFwZVJDfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgc3RyaWRlOiBudW1iZXIsXG4gICAgICB6ZXJvUGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBmaWVsZFNpemUgPSB3ZWlnaHRzLnNoYXBlWzBdO1xuICAgIGNvbnN0IGlucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gd2VpZ2h0cy5zaGFwZVszXTtcbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX1BST0csIHguc2hhcGUsIG91dHB1dERlcHRoLCBmaWVsZFNpemUsIHN0cmlkZSwgYmlhc2VzICE9IG51bGxcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgb3V0cHV0RGVwdGgsIGZpZWxkU2l6ZSwgc3RyaWRlLCB6ZXJvUGFkLCBiaWFzZXMgIT0gbnVsbCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmaWVsZFNpemUpO1xuICAgIGNvbnN0IGJpYXNUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGgpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFcgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxXVGV4U2hhcGUgPSB3ZWlnaHRzLmdldFRleHR1cmVTaGFwZVJDKHdUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFdUZXhTaGFwZSwgd1RleFNoYXBlKSkge1xuICAgICAgd2VpZ2h0cyA9IHRoaXMucmVzaGFwZVRleHR1cmUod2VpZ2h0cywgd1RleFNoYXBlKTtcbiAgICAgIGNsZWFudXBXID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IGFjdHVhbEJUZXhTaGFwZSA9IGJpYXNlcy5nZXRUZXh0dXJlU2hhcGVSQyhiaWFzVGV4U2hhcGUpO1xuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbEJUZXhTaGFwZSwgYmlhc1RleFNoYXBlKSkge1xuICAgICAgICBiaWFzZXMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGJpYXNlcywgYmlhc1RleFNoYXBlKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHJlc3VsdFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICB4LnNoYXBlLCBmaWVsZFNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2dwdS5jb252b2x2ZShcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHdlaWdodHMuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiaWFzZXMgIT0gbnVsbCA/IGJpYXNlcy5nZXRUZXh0dXJlKCkgOiBudWxsLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwVykge1xuICAgICAgd2VpZ2h0cy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwQiAmJiBiaWFzZXMgIT0gbnVsbCkge1xuICAgICAgYmlhc2VzLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkQmFja1Byb3BJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGR5OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKToge2R4OiBBcnJheTNELCBkdzogQXJyYXk0RCwgZGI6IEFycmF5MUR9IHtcbiAgICBjb25zdCBmU2l6ZSA9IHdlaWdodHMuc2hhcGVbMF07XG4gICAgY29uc3QgaW5wdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbMl07XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzNdO1xuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3Qgd1RleFNoYXBlID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICBjb25zdCB5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBXID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsV1RleFNoYXBlID0gd2VpZ2h0cy5nZXRUZXh0dXJlU2hhcGVSQyh3VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxXVGV4U2hhcGUsIHdUZXhTaGFwZSkpIHtcbiAgICAgIHdlaWdodHMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHdlaWdodHMsIHdUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwVyA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGNsZWFudXBZID0gZmFsc2U7XG4gICAgY29uc3QgYWN0dWFsWVRleFNoYXBlID0gZHkuZ2V0VGV4dHVyZVNoYXBlUkMoeVRleFNoYXBlKTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsWVRleFNoYXBlLCB5VGV4U2hhcGUpKSB7XG4gICAgICBkeSA9IHRoaXMucmVzaGFwZVRleHR1cmUoZHksIHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWSA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgZHcgPSB0aGlzLmNvbnYyZERlcldlaWdodHMoeCwgZHksIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgY29uc3QgZGIgPSB0aGlzLmNvbnYyZERlckJpYXMoZHkpO1xuICAgIGNvbnN0IGR4ID0gdGhpcy5jb252MmRUcmFuc3Bvc2VJbnRlcm5hbChcbiAgICAgICAgZHksIHdlaWdodHMsIG51bGwgLyoqIGJpYXNlcyAqLywgc3RyaWRlLCBwYWQpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBXKSB7XG4gICAgICB3ZWlnaHRzLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBZKSB7XG4gICAgICBkeS5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHJldHVybiB7ZHgsIGRiLCBkd307XG4gIH1cblxuICBwcm90ZWN0ZWQgY29udjJkVHJhbnNwb3NlSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCB3ZWlnaHRzOiBBcnJheTRELCBiaWFzZXM6IEFycmF5MUR8bnVsbCwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3Qgb3JpZ0lucHV0RGVwdGggPSB3ZWlnaHRzLnNoYXBlWzJdO1xuICAgIGNvbnN0IG9yaWdPdXRwdXREZXB0aCA9IHdlaWdodHMuc2hhcGVbM107XG4gICAgY29uc3QgZmllbGRTaXplID0gd2VpZ2h0cy5zaGFwZVswXTtcblxuICAgIGNvbnN0IHByb2dLZXkgPSBbXG4gICAgICBDT05WMkRfVFJBTlNQT1NFX1BST0csIHguc2hhcGUsIGZpZWxkU2l6ZSwgb3JpZ0lucHV0RGVwdGgsIG9yaWdTdHJpZGUsXG4gICAgICBvcmlnUGFkLCBiaWFzZXMgIT0gbnVsbFxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKHByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBjb252X2JhY2twcm9wX2dwdS5nZXRGcmFnbWVudFNoYWRlckNvbnZUcmFuc3Bvc2VTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZmllbGRTaXplLCBvcmlnSW5wdXREZXB0aCwgb3JpZ1N0cmlkZSwgb3JpZ1BhZCxcbiAgICAgICAgICBiaWFzZXMgIT0gbnVsbCk7XG4gICAgfSk7XG5cbiAgICBjb25zdCB4VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHguc2hhcGUpO1xuICAgIGNvbnN0IHdUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKFxuICAgICAgICBvcmlnSW5wdXREZXB0aCwgb3JpZ091dHB1dERlcHRoLCBmaWVsZFNpemUpO1xuICAgIGNvbnN0IGJpYXNUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3JpZ0lucHV0RGVwdGgpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFcgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxXVGV4U2hhcGUgPSB3ZWlnaHRzLmdldFRleHR1cmVTaGFwZVJDKHdUZXhTaGFwZSk7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFdUZXhTaGFwZSwgd1RleFNoYXBlKSkge1xuICAgICAgd2VpZ2h0cyA9IHRoaXMucmVzaGFwZVRleHR1cmUod2VpZ2h0cywgd1RleFNoYXBlKTtcbiAgICAgIGNsZWFudXBXID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcbiAgICBpZiAoYmlhc2VzICE9IG51bGwpIHtcbiAgICAgIGNvbnN0IGFjdHVhbEJpYXNUZXhTaGFwZSA9IGJpYXNlcy5nZXRUZXh0dXJlU2hhcGVSQyhiaWFzVGV4U2hhcGUpO1xuICAgICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbEJpYXNUZXhTaGFwZSwgYmlhc1RleFNoYXBlKSkge1xuICAgICAgICBiaWFzZXMgPSB0aGlzLnJlc2hhcGVUZXh0dXJlKGJpYXNlcywgYmlhc1RleFNoYXBlKTtcbiAgICAgICAgY2xlYW51cEIgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEZpZ3VyZSBvdXQgdGhlIG91dHB1dCBzaGFwZSBieSBkaWxhdGluZyB0aGUgaW5wdXQuXG4gICAgY29uc3QgZGlsYXRlZFJDID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEaWxhdGVkUkMoW3guc2hhcGVbMF0sIHguc2hhcGVbMV1dLCBvcmlnU3RyaWRlKTtcbiAgICBjb25zdCBwYWQgPSBmaWVsZFNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgICBjb25zdCByZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgW2RpbGF0ZWRSQ1swXSwgZGlsYXRlZFJDWzFdLCBvcmlnT3V0cHV0RGVwdGhdLCBmaWVsZFNpemUsXG4gICAgICAgIG9yaWdJbnB1dERlcHRoLCAxLCBwYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2JhY2twcm9wX2dwdS5jb252VHJhbnNwb3NlKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgd2VpZ2h0cy5nZXRUZXh0dXJlKCksXG4gICAgICAgIGJpYXNlcyAhPSBudWxsID8gYmlhc2VzLmdldFRleHR1cmUoKSA6IG51bGwsIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBYKSB7XG4gICAgICB4LmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBXKSB7XG4gICAgICB3ZWlnaHRzLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgaWYgKGNsZWFudXBCKSB7XG4gICAgICBiaWFzZXMhLmRpc3Bvc2UoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5M0Q+KFxuICAgICAgICByZXN1bHRTaGFwZSwge3RleHR1cmU6IHJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBjb252MmREZXJXZWlnaHRzKFxuICAgICAgeDogQXJyYXkzRCwgZFk6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgICAgemVyb1BhZDogbnVtYmVyKTogQXJyYXk0RCB7XG4gICAgY29uc3QgaW5wdXREZXB0aCA9IHguc2hhcGVbMl07XG4gICAgY29uc3Qgb3V0cHV0RGVwdGggPSBkWS5zaGFwZVsyXTtcbiAgICBjb25zdCBwcm9nS2V5ID0gW1xuICAgICAgQ09OVjJEX0RFUldfUFJPRywgeC5zaGFwZSwgZlNpemUsIG91dHB1dERlcHRoLCBzdHJpZGUsIHplcm9QYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShwcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gY29udl9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJEZXJXZWlnaHRzU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IHhUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeC5zaGFwZSk7XG4gICAgY29uc3QgeVNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgb3V0cHV0RGVwdGgsIHN0cmlkZSwgemVyb1BhZCk7XG4gICAgY29uc3QgeVRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh5U2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBsZXQgY2xlYW51cFkgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxZVGV4U2hhcGUgPSBkWS5nZXRUZXh0dXJlU2hhcGVSQyh5VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxZVGV4U2hhcGUsIHlUZXhTaGFwZSkpIHtcbiAgICAgIGRZID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkWSwgeVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBZID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1RleFNoYXBlKGlucHV0RGVwdGgsIG91dHB1dERlcHRoLCBmU2l6ZSk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBjb252X2JhY2twcm9wX2dwdS5kZXJXZWlnaHRzKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCB4LmdldFRleHR1cmUoKSwgZFkuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZFkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGNvbnN0IHdlaWdodHNTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlV2VpZ2h0c1NoYXBlNEQoaW5wdXREZXB0aCwgb3V0cHV0RGVwdGgsIGZTaXplKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPEFycmF5NEQ+KFxuICAgICAgICB3ZWlnaHRzU2hhcGUsIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgY29udjJkRGVyQmlhcyhkWTogQXJyYXkzRCk6IEFycmF5MUQge1xuICAgIGNvbnN0IG91dHB1dERlcHRoID0gZFkuc2hhcGVbMl07XG4gICAgY29uc3QgcHJvZ0tleSA9IFtDT05WMkRfREVSQl9QUk9HLCBkWS5zaGFwZV0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKHByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBjb252X2JhY2twcm9wX2dwdS5nZXRGcmFnbWVudFNoYWRlckRlckJpYXNTb3VyY2UoZFkuc2hhcGUpO1xuICAgIH0pO1xuICAgIGNvbnN0IHlUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZFkuc2hhcGUpO1xuXG4gICAgLy8gSWYgdGhlIHRleHR1cmUgc2hhcGVzIGRvZXNuJ3QgbWF0Y2ggdGhlIHNoYXBlcyB0aGF0IHNoYWRlcnMgZXhwZWN0LFxuICAgIC8vIGRvIHBoeXNpY2FsIHRleHR1cmUgcmVzaGFwZXMgb24gdGhlIEdQVS5cbiAgICBsZXQgY2xlYW51cFkgPSBmYWxzZTtcbiAgICBjb25zdCBhY3R1YWxZVGV4U2hhcGUgPSBkWS5nZXRUZXh0dXJlU2hhcGVSQyh5VGV4U2hhcGUpO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxZVGV4U2hhcGUsIHlUZXhTaGFwZSkpIHtcbiAgICAgIGRZID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkWSwgeVRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBZID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlQmlhc2VzVGV4U2hhcGUob3V0cHV0RGVwdGgpO1xuICAgIGNvbnN0IHJlc3VsdFRleCA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29udl9iYWNrcHJvcF9ncHUuZGVyQmlhcyhcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgZFkuZ2V0VGV4dHVyZSgpLCByZXN1bHRUZXgsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIGlmIChjbGVhbnVwWSkge1xuICAgICAgZFkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkxRD4oXG4gICAgICAgIFtvdXRwdXREZXB0aF0sIHt0ZXh0dXJlOiByZXN1bHRUZXgsIHRleHR1cmVTaGFwZVJDOiByZXN1bHRUZXhTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBwb29sKFxuICAgICAgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICAgIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsWFRleFNoYXBlID0geC5nZXRUZXh0dXJlU2hhcGVSQyh4VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwWCA9IGZhbHNlO1xuICAgIGlmICghdXRpbC5hcnJheXNFcXVhbChhY3R1YWxYVGV4U2hhcGUsIHhUZXhTaGFwZSkpIHtcbiAgICAgIHggPSB0aGlzLnJlc2hhcGVUZXh0dXJlKHgsIHhUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwWCA9IHRydWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0U2hhcGUgPVxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoeC5zaGFwZSwgZlNpemUsIHguc2hhcGVbMl0sIHN0cmlkZSwgcGFkKTtcbiAgICBjb25zdCByZXN1bHRUZXhTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QocmVzdWx0U2hhcGUpO1xuICAgIGNvbnN0IHBvb2xSZXN1bHRUZXggPSB0aGlzLnRleHR1cmVNYW5hZ2VyLmFjcXVpcmVUZXh0dXJlKHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHBvb2xfZ3B1LnBvb2xDb21tb24oXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIHguZ2V0VGV4dHVyZSgpLCBwb29sUmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cFgpIHtcbiAgICAgIHguZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIHJlc3VsdFNoYXBlLCB7dGV4dHVyZTogcG9vbFJlc3VsdFRleCwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEludGVybmFsKFxuICAgICAgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9vbFByb2dLZXkgPVxuICAgICAgICBbTUFYX1BPT0xfUFJPRywgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkXS5qb2luKCdfJyk7XG4gICAgY29uc3QgbWF4UG9vbFByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xQcm9nS2V5LCAoKSA9PiB7XG4gICAgICByZXR1cm4gbWF4X3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFNvdXJjZShcbiAgICAgICAgICB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHRoaXMucG9vbChtYXhQb29sUHJvZ3JhbSwgeCwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBtaW5Qb29sSW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgcGFkOiBudW1iZXIpOiBBcnJheTNEIHtcbiAgICBjb25zdCBtaW5Qb29sUHJvZ0tleSA9XG4gICAgICAgIFtNSU5fUE9PTF9QUk9HLCB4LnNoYXBlLCBmU2l6ZSwgc3RyaWRlLCBwYWRdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBtaW5Qb29sUHJvZ3JhbSA9IHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0obWluUG9vbFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBtaW5fcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJNaW5Qb29sU291cmNlKFxuICAgICAgICAgIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdGhpcy5wb29sKG1pblBvb2xQcm9ncmFtLCB4LCBmU2l6ZSwgc3RyaWRlLCBwYWQpO1xuICB9XG5cbiAgcHJvdGVjdGVkIGF2Z1Bvb2xJbnRlcm5hbChcbiAgICAgIHg6IEFycmF5M0QsIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCBwYWQ6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIGNvbnN0IGF2Z1Bvb2xQcm9nS2V5ID1cbiAgICAgICAgW0FWR19QT09MX1BST0csIHguc2hhcGUsIGZTaXplLCBzdHJpZGUsIHBhZF0uam9pbignXycpO1xuICAgIGNvbnN0IGF2Z1Bvb2xQcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShhdmdQb29sUHJvZ0tleSwgKCkgPT4ge1xuICAgICAgcmV0dXJuIGF2Z19wb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlckF2Z1Bvb2xTb3VyY2UoXG4gICAgICAgICAgeC5zaGFwZSwgZlNpemUsIHN0cmlkZSwgcGFkKTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLnBvb2woYXZnUG9vbFByb2dyYW0sIHgsIGZTaXplLCBzdHJpZGUsIHBhZCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgbWF4UG9vbEJhY2twcm9wSW50ZXJuYWwoXG4gICAgICBkeTogQXJyYXkzRCwgeDogQXJyYXkzRCwgZlNpemU6IG51bWJlciwgb3JpZ1N0cmlkZTogbnVtYmVyLFxuICAgICAgb3JpZ1BhZDogbnVtYmVyKTogQXJyYXkzRCB7XG4gICAgY29uc3QgbWF4UG9vbFBvc2l0aW9uc1Byb2dLZXkgPSBbXG4gICAgICBNQVhfUE9PTF9QT1NJVElPTlNfUFJPRywgeC5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWRcbiAgICBdLmpvaW4oJ18nKTtcbiAgICBjb25zdCBtYXhQb29sUG9zaXRpb25zUHJvZ3JhbSA9XG4gICAgICAgIHRoaXMuZ2V0QW5kU2F2ZVByb2dyYW0obWF4UG9vbFBvc2l0aW9uc1Byb2dLZXksICgpID0+IHtcbiAgICAgICAgICByZXR1cm4gbWF4X3Bvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFBvc2l0aW9uc1NvdXJjZShcbiAgICAgICAgICAgICAgeC5zaGFwZSwgZlNpemUsIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgICAgICB9KTtcblxuICAgIGNvbnN0IG1heFBvb2xSZXN1bHRTaGFwZSA9IGNvbnZfdXRpbC5jb21wdXRlT3V0cHV0U2hhcGUzRChcbiAgICAgICAgeC5zaGFwZSwgZlNpemUsIHguc2hhcGVbMl0sIG9yaWdTdHJpZGUsIG9yaWdQYWQpO1xuICAgIGNvbnN0IG1heFBvb2xSZXN1bHRUZXhTaGFwZSA9XG4gICAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QobWF4UG9vbFJlc3VsdFNoYXBlKTtcbiAgICBjb25zdCBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4ID1cbiAgICAgICAgdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShtYXhQb29sUmVzdWx0VGV4U2hhcGUpO1xuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgeFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4LnNoYXBlKTtcbiAgICBjb25zdCBhY3R1YWxYVGV4U2hhcGUgPSB4LmdldFRleHR1cmVTaGFwZVJDKHhUZXhTaGFwZSk7XG4gICAgbGV0IGNsZWFudXBYID0gZmFsc2U7XG4gICAgaWYgKCF1dGlsLmFycmF5c0VxdWFsKGFjdHVhbFhUZXhTaGFwZSwgeFRleFNoYXBlKSkge1xuICAgICAgeCA9IHRoaXMucmVzaGFwZVRleHR1cmUoeCwgeFRleFNoYXBlKTtcbiAgICAgIGNsZWFudXBYID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBtYXhfcG9vbF9ncHUubWF4UG9vbENvbW1vbihcbiAgICAgICAgdGhpcy5ncGdwdSwgbWF4UG9vbFBvc2l0aW9uc1Byb2dyYW0sIHguZ2V0VGV4dHVyZSgpLFxuICAgICAgICBtYXhQb29sUG9zaXRpb25zUmVzdWx0VGV4LCBtYXhQb29sUmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgY29uc3QgbWF4UG9vbEJhY2twcm9wUHJvZ0tleSA9IFtcbiAgICAgIE1BWF9QT09MX0JBQ0tQUk9QX1BST0csIGR5LnNoYXBlLCBmU2l6ZSwgb3JpZ1N0cmlkZSwgb3JpZ1BhZFxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKG1heFBvb2xCYWNrcHJvcFByb2dLZXksICgpID0+IHtcbiAgICAgIHJldHVybiBtYXhfcG9vbF9iYWNrcHJvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQmFja3Byb3AoXG4gICAgICAgICAgZHkuc2hhcGUsIGZTaXplLCBvcmlnU3RyaWRlLCBvcmlnUGFkKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IGR5VGV4U2hhcGUgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKGR5LnNoYXBlKTtcblxuICAgIC8vIElmIHRoZSB0ZXh0dXJlIHNoYXBlcyBkb2Vzbid0IG1hdGNoIHRoZSBzaGFwZXMgdGhhdCBzaGFkZXJzIGV4cGVjdCxcbiAgICAvLyBkbyBwaHlzaWNhbCB0ZXh0dXJlIHJlc2hhcGVzIG9uIHRoZSBHUFUuXG4gICAgY29uc3QgYWN0dWFsRHlUZXhTaGFwZSA9IGR5LmdldFRleHR1cmVTaGFwZVJDKGR5VGV4U2hhcGUpO1xuICAgIGxldCBjbGVhbnVwRHkgPSBmYWxzZTtcbiAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYWN0dWFsRHlUZXhTaGFwZSwgZHlUZXhTaGFwZSkpIHtcbiAgICAgIGR5ID0gdGhpcy5yZXNoYXBlVGV4dHVyZShkeSwgZHlUZXhTaGFwZSk7XG4gICAgICBjbGVhbnVwRHkgPSB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGRpbGF0ZWREeVJDID1cbiAgICAgICAgY29udl91dGlsLmNvbXB1dGVEaWxhdGVkUkMoW2R5LnNoYXBlWzBdLCBkeS5zaGFwZVsxXV0sIG9yaWdTdHJpZGUpO1xuICAgIGNvbnN0IHBhZCA9IGZTaXplIC0gMSAtIG9yaWdQYWQ7XG4gICAgY29uc3QgcmVzdWx0U2hhcGVSQ0QgPSBjb252X3V0aWwuY29tcHV0ZU91dHB1dFNoYXBlM0QoXG4gICAgICAgIFtkaWxhdGVkRHlSQ1swXSwgZGlsYXRlZER5UkNbMV0sIGR5LnNoYXBlWzJdXSwgZlNpemUsIGR5LnNoYXBlWzJdLCAxLFxuICAgICAgICBwYWQpO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChyZXN1bHRTaGFwZVJDRCk7XG4gICAgY29uc3QgcmVzdWx0VGV4ID0gdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXhTaGFwZSk7XG5cbiAgICBtYXhfcG9vbF9iYWNrcHJvcF9ncHUubWF4UG9vbEJhY2twcm9wKFxuICAgICAgICB0aGlzLmdwZ3B1LCBwcm9ncmFtLCBkeS5nZXRUZXh0dXJlKCksIG1heFBvb2xQb3NpdGlvbnNSZXN1bHRUZXgsXG4gICAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgaWYgKGNsZWFudXBEeSkge1xuICAgICAgZHkuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIGlmIChjbGVhbnVwWCkge1xuICAgICAgeC5kaXNwb3NlKCk7XG4gICAgfVxuXG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlci5yZWxlYXNlVGV4dHVyZShcbiAgICAgICAgbWF4UG9vbFBvc2l0aW9uc1Jlc3VsdFRleCwgbWF4UG9vbFJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIHJlc3VsdFNoYXBlUkNELCB7dGV4dHVyZTogcmVzdWx0VGV4LCB0ZXh0dXJlU2hhcGVSQzogcmVzdWx0VGV4U2hhcGV9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCByZXNpemVCaWxpbmVhcjNESW50ZXJuYWwoXG4gICAgICB4OiBBcnJheTNELCBuZXdTaGFwZTJEOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgICAgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogQXJyYXkzRCB7XG4gICAgY29uc3QgcHJvZ3JhbUtleSA9XG4gICAgICAgIFtSRVNJWkVfQklMSU5FQVJfUFJPRywgeC5zaGFwZSwgbmV3U2hhcGUyRCwgYWxpZ25Db3JuZXJzXS5qb2luKCdfJyk7XG5cbiAgICBjb25zdCBuZXdTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdID1cbiAgICAgICAgW25ld1NoYXBlMkRbMF0sIG5ld1NoYXBlMkRbMV0sIHguc2hhcGVbMl1dO1xuICAgIGNvbnN0IHJlc3VsdFRleFNoYXBlID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRChuZXdTaGFwZVJDRCk7XG5cbiAgICBjb25zdCBwcm9ncmFtID0gdGhpcy5nZXRBbmRTYXZlUHJvZ3JhbShcbiAgICAgICAgcHJvZ3JhbUtleSxcbiAgICAgICAgKCkgPT4gcmVzaXplX2JpbGluZWFyX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgICAgICAgIHguc2hhcGUsIG5ld1NoYXBlMkQsIGFsaWduQ29ybmVycykpO1xuXG4gICAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IHRoaXMudGV4dHVyZU1hbmFnZXIuYWNxdWlyZVRleHR1cmUocmVzdWx0VGV4U2hhcGUpO1xuXG4gICAgcmVzaXplX2JpbGluZWFyX2dwdS5yZXNpemVCaWxpbmVhcihcbiAgICAgICAgdGhpcy5ncGdwdSwgcHJvZ3JhbSwgeC5nZXRUZXh0dXJlKCksIHJlc3VsdFRleHR1cmUsIHJlc3VsdFRleFNoYXBlKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8QXJyYXkzRD4oXG4gICAgICAgIG5ld1NoYXBlUkNELCB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleFNoYXBlfSk7XG4gIH1cblxuICBwcml2YXRlIGdldEFuZFNhdmVQcm9ncmFtKHByb2dyYW1LZXk6IHN0cmluZywgZ2V0U2hhZGVyU291cmNlOiAoKSA9PiBzdHJpbmcpOlxuICAgICAgV2ViR0xQcm9ncmFtIHtcbiAgICBpZiAoIShwcm9ncmFtS2V5IGluIHRoaXMucHJvZ3JhbUNhY2hlKSkge1xuICAgICAgdGhpcy5wcm9ncmFtQ2FjaGVbcHJvZ3JhbUtleV0gPVxuICAgICAgICAgIHRoaXMuZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRTaGFkZXJTb3VyY2UoKSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLnByb2dyYW1DYWNoZVtwcm9ncmFtS2V5XTtcbiAgfVxuXG4gIHByaXZhdGUgYWRkU3ViTXVsRGl2KFxuICAgICAgYTogTkRBcnJheSwgYjogTkRBcnJheSwgcmVzdWx0U2hhcGU6IG51bWJlcltdLFxuICAgICAgb3BlcmFuZEE6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmFuZFR5cGUsXG4gICAgICBvcFR5cGU6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmF0aW9uLFxuICAgICAgb3BlcmFuZEI6IGFkZHN1Ym11bGRpdl9ncHUuT3BlcmFuZFR5cGUpOiBOREFycmF5IHtcbiAgICBsZXQgY2xlYW51cEIgPSBmYWxzZTtcblxuICAgIGNvbnN0IGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVI7XG4gICAgbGV0IGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVI7XG5cbiAgICBsZXQgbG9naWNhbEJUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXTtcblxuICAgIGlmIChvcGVyYW5kQSA9PT0gT3BlcmFuZFR5cGUuTUFUUklYICYmIG9wZXJhbmRCID09PSBPcGVyYW5kVHlwZS5NQVRSSVgpIHtcbiAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goYS5zaGFwZSwgYi5zaGFwZSk7XG5cbiAgICAgIGlmIChhLmluR1BVKCkpIHtcbiAgICAgICAgLy8gUHJlZmVyIEIgdG8gaGF2ZSB0aGUgc2hhcGUgb2YgQS5cbiAgICAgICAgYi5nZXRUZXh0dXJlU2hhcGVSQyhhLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgICAgfSBlbHNlIGlmIChiLmluR1BVKCkpIHtcbiAgICAgICAgLy8gUHJlZmVyIEEgdG8gaGF2ZSB0aGUgc2hhcGUgb2YgQi5cbiAgICAgICAgYS5nZXRUZXh0dXJlU2hhcGVSQyhiLmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBhVGV4U2hhcGUgPSBhLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgICBjb25zdCBiVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgICBsb2dpY2FsQlRleFNoYXBlID0gYlRleFNoYXBlO1xuXG4gICAgICBpZiAoYS5yYW5rID09PSAxKSB7XG4gICAgICAgIC8vIFdoZW4gZGVhbGluZyB3aXRoIHZlY3RvcnMsIHdlIGNhbiBzYW1wbGUgaW4gdHJhbnNwb3NlZCB3YXkgd2l0aG91dFxuICAgICAgICAvLyB0aGUgbmVlZCB0byBkbyBwaHlzaWNhbCByZXNoYXBlLlxuICAgICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYlRleFNoYXBlLCBhVGV4U2hhcGUpKSB7XG4gICAgICAgICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRDtcbiAgICAgICAgICBsb2dpY2FsQlRleFNoYXBlID0gW2JUZXhTaGFwZVsxXSwgYlRleFNoYXBlWzBdXTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoIXV0aWwuYXJyYXlzRXF1YWwoYVRleFNoYXBlLCBsb2dpY2FsQlRleFNoYXBlKSkge1xuICAgICAgICBiID0gdGhpcy5yZXNoYXBlVGV4dHVyZShiLCBhVGV4U2hhcGUpO1xuICAgICAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSO1xuICAgICAgICBsb2dpY2FsQlRleFNoYXBlID0gYi5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgICAgICBjbGVhbnVwQiA9IHRydWU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ2ljYWxCVGV4U2hhcGUgPSBiLmdldFRleHR1cmVTaGFwZVJDKCk7XG4gICAgfVxuXG4gICAgY29uc3QgYVRleFNoYXBlID0gYS5nZXRUZXh0dXJlU2hhcGVSQygpO1xuICAgIGNvbnN0IGJUZXhTaGFwZSA9IGIuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcblxuICAgIGNvbnN0IHByb2dyYW1LZXkgPSBbXG4gICAgICBBRERfU1VNX01VTF9ESVZfUFJPRywgb3BlcmFuZEEsIGFPcmllbnRhdGlvbiwgb3BUeXBlLCBvcGVyYW5kQixcbiAgICAgIGJPcmllbnRhdGlvblxuICAgIF0uam9pbignXycpO1xuICAgIGNvbnN0IHByb2dyYW0gPSB0aGlzLmdldEFuZFNhdmVQcm9ncmFtKFxuICAgICAgICBwcm9ncmFtS2V5LFxuICAgICAgICAoKSA9PiBhZGRzdWJtdWxkaXZfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgICAgICAgb3BlcmFuZEEsIGFPcmllbnRhdGlvbiwgb3BUeXBlLCBvcGVyYW5kQiwgYk9yaWVudGF0aW9uKSk7XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlU2hhcGU6IFtudW1iZXIsIG51bWJlcl0gPSBbXG4gICAgICBNYXRoLm1heChhVGV4U2hhcGVbMF0sIGxvZ2ljYWxCVGV4U2hhcGVbMF0pLFxuICAgICAgTWF0aC5tYXgoYVRleFNoYXBlWzFdLCBsb2dpY2FsQlRleFNoYXBlWzFdKVxuICAgIF07XG5cbiAgICBjb25zdCByZXN1bHRUZXh0dXJlID1cbiAgICAgICAgdGhpcy50ZXh0dXJlTWFuYWdlci5hY3F1aXJlVGV4dHVyZShyZXN1bHRUZXh0dXJlU2hhcGUpO1xuXG4gICAgYWRkc3VibXVsZGl2X2dwdS5hZGRTdWJNdWxEaXYoXG4gICAgICAgIHRoaXMuZ3BncHUsIHByb2dyYW0sIGEuZ2V0VGV4dHVyZSgpLCBhVGV4U2hhcGUsIGIuZ2V0VGV4dHVyZSgpLFxuICAgICAgICBiVGV4U2hhcGUsIHJlc3VsdFRleHR1cmUsIHJlc3VsdFRleHR1cmVTaGFwZSk7XG5cbiAgICBpZiAoY2xlYW51cEIpIHtcbiAgICAgIGIuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2UoXG4gICAgICAgIHJlc3VsdFNoYXBlLFxuICAgICAgICB7dGV4dHVyZTogcmVzdWx0VGV4dHVyZSwgdGV4dHVyZVNoYXBlUkM6IHJlc3VsdFRleHR1cmVTaGFwZX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBkb0dQVVNoYXBlc01hdGNoKGE6IE5EQXJyYXksIGI6IE5EQXJyYXkpOiBib29sZWFuIHtcbiAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKGEuc2hhcGUsIGIuc2hhcGUpO1xuICAgIGlmIChhLmluR1BVKCkpIHtcbiAgICAgIC8vIFByZWZlciBCIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEEuXG4gICAgICBiLmdldFRleHR1cmVTaGFwZVJDKGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgfSBlbHNlIGlmIChiLmluR1BVKCkpIHtcbiAgICAgIC8vIFByZWZlciBBIHRvIGhhdmUgdGhlIHNoYXBlIG9mIEIuXG4gICAgICBhLmdldFRleHR1cmVTaGFwZVJDKGIuZ2V0VGV4dHVyZVNoYXBlUkMoKSk7XG4gICAgfVxuICAgIHJldHVybiB1dGlsLmFycmF5c0VxdWFsKGEuZ2V0VGV4dHVyZVNoYXBlUkMoKSwgYi5nZXRUZXh0dXJlU2hhcGVSQygpKTtcbiAgfVxuXG4gIGdldFRleHR1cmVNYW5hZ2VyKCk6IFRleHR1cmVNYW5hZ2VyIHtcbiAgICByZXR1cm4gdGhpcy50ZXh0dXJlTWFuYWdlcjtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgZm9yIChjb25zdCBwcm9ncmFtS2V5IGluIHRoaXMucHJvZ3JhbUNhY2hlKSB7XG4gICAgICBpZiAodGhpcy5wcm9ncmFtQ2FjaGUuaGFzT3duUHJvcGVydHkocHJvZ3JhbUtleSkpIHtcbiAgICAgICAgdGhpcy5ncGdwdS5kZWxldGVQcm9ncmFtKHRoaXMucHJvZ3JhbUNhY2hlW3Byb2dyYW1LZXldKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy50ZXh0dXJlTWFuYWdlci5kaXNwb3NlKCk7XG5cbiAgICBpZiAodGhpcy5ncGdwdUNyZWF0ZWRMb2NhbGx5KSB7XG4gICAgICB0aGlzLmdwZ3B1LmRpc3Bvc2UoKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vd2ViZ2wvZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge1RleHR1cmVNYW5hZ2VyfSBmcm9tICcuL3dlYmdsL3RleHR1cmVfbWFuYWdlcic7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2wvd2ViZ2xfdXRpbCc7XG5cbi8vIFRoZXNlIGdsb2JhbCB2YXJpYWJsZXMgbmVlZCB0byBiZSBpbml0aWFsaXplZCB0byBudWxsIHNvIHRoYXQgY2xvc3VyZSBrbm93c1xuLy8gbm90IHRvIHNlYWwgdGhlbS5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IEdQR1BVOiBHUEdQVUNvbnRleHQgPSBudWxsITtcbi8qKiBAaGlkZGVuICovXG5leHBvcnQgbGV0IFRFWFRVUkVfTUFOQUdFUjogVGV4dHVyZU1hbmFnZXIgPSBudWxsITtcblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTkRBcnJheURhdGEge1xuICB2YWx1ZXM/OiBGbG9hdDMyQXJyYXk7XG4gIHRleHR1cmU/OiBXZWJHTFRleHR1cmU7XG4gIC8qKiBbcm93cywgY29sdW1uc10gc2hhcGUgb2YgdGhlIHRleHR1cmUuICovXG4gIHRleHR1cmVTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXTtcbn1cblxuLyoqIEBoaWRkZW4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0aWFsaXplR1BVKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHRleHR1cmVNYW5hZ2VyOiBUZXh0dXJlTWFuYWdlcikge1xuICBHUEdQVSA9IGdwZ3B1O1xuICBURVhUVVJFX01BTkFHRVIgPSB0ZXh0dXJlTWFuYWdlcjtcbn1cblxuZnVuY3Rpb24gdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCkge1xuICBpZiAoR1BHUFUgPT0gbnVsbCB8fCBURVhUVVJFX01BTkFHRVIgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignR1BVIG5vdCBpbnRpYWxpemVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBOREFycmF5IHtcbiAgLyoqIFRoZSBzaGFwZSBvZiB0aGUgbmRhcnJheS4gKi9cbiAgc2hhcGU6IG51bWJlcltdO1xuICAvKiogTnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSBuZGFycmF5LiAqL1xuICBzaXplOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE51bWJlciBvZiBlbGVtZW50cyB0byBza2lwIGluIGVhY2ggZGltZW5zaW9uIHdoZW4gaW5kZXhpbmcuIFNlZVxuICAgKiBodHRwczovL2RvY3Muc2NpcHkub3JnL2RvYy9udW1weS9yZWZlcmVuY2UvZ2VuZXJhdGVkL251bXB5Lm5kYXJyYXkuc3RyaWRlcy5odG1sXG4gICAqL1xuICBwcm90ZWN0ZWQgc3RyaWRlczogbnVtYmVyW107XG5cbiAgcHJpdmF0ZSBkYXRhOiBOREFycmF5RGF0YTtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3Ioc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIC8vIFNhbml0eSBjaGVja3MuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudmFsdWVzICE9IG51bGwgfHwgZGF0YS50ZXh0dXJlICE9IG51bGwsXG4gICAgICAgICdFaXRoZXIgYHZhbHVlc2Agb3IgYHRleHR1cmVgIG11c3QgYmUgZGVmaW5lZCcpO1xuXG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIGRhdGEudGV4dHVyZSA9PSBudWxsIHx8IChkYXRhLnRleHR1cmVTaGFwZVJDICE9IG51bGwpLFxuICAgICAgICAnYHRleHR1cmVTaGFwZWAgbXVzdCBiZSBkZWZpbmVkIHdoZW4gYHRleHR1cmVgIGlzIGRlZmluZWQnKTtcblxuICAgIHRoaXMuc2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZShzaGFwZSk7XG5cbiAgICBpZiAoZGF0YS52YWx1ZXMgIT0gbnVsbCkge1xuICAgICAgdXRpbC5hc3NlcnQoXG4gICAgICAgICAgdGhpcy5zaXplID09PSBkYXRhLnZhbHVlcy5sZW5ndGgsXG4gICAgICAgICAgJ0NvbnN0cnVjdGluZyBuZGFycmF5IG9mIHNoYXBlICgnICsgdGhpcy5zaXplICsgJykgc2hvdWxkIG1hdGNoIHRoZScgK1xuICAgICAgICAgICAgICAnIGxlbmd0aCBvZiB2YWx1ZXMgKCcgKyBkYXRhLnZhbHVlcy5sZW5ndGggKyAnKScpO1xuICAgIH1cblxuICAgIHRoaXMuc2hhcGUgPSBzaGFwZTtcbiAgICB0aGlzLmRhdGEgPSBkYXRhO1xuICAgIGNvbnN0IGRpbSA9IHRoaXMuc2hhcGUubGVuZ3RoO1xuXG4gICAgaWYgKGRpbSA8IDIpIHtcbiAgICAgIHRoaXMuc3RyaWRlcyA9IFtdO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBMYXN0IGRpbWVuc2lvbiBoYXMgaW1wbGljaXQgc3RyaWRlIG9mIDEsIHRodXMgaGF2aW5nIEQtMSAoaW5zdGVhZCBvZiBEKVxuICAgICAgLy8gc3RyaWRlcy5cbiAgICAgIHRoaXMuc3RyaWRlcyA9IG5ldyBBcnJheShkaW0gLSAxKTtcbiAgICAgIHRoaXMuc3RyaWRlc1tkaW0gLSAyXSA9IHRoaXMuc2hhcGVbZGltIC0gMV07XG4gICAgICBmb3IgKGxldCBpID0gZGltIC0gMzsgaSA+PSAwOyAtLWkpIHtcbiAgICAgICAgdGhpcy5zdHJpZGVzW2ldID0gdGhpcy5zdHJpZGVzW2kgKyAxXSAqIHRoaXMuc2hhcGVbaSArIDFdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSBvZiB6ZXJvcyB3aXRoIHRoZSBzcGVjaWZpZWQgc2hhcGUuICovXG4gIHN0YXRpYyB6ZXJvczxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gbmV3IEZsb2F0MzJBcnJheSh1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KHNoYXBlLCB7dmFsdWVzfSk7XG4gIH1cblxuICAvKiogQ3JlYXRlcyBhIG5kYXJyYXkgb2YgemVyb3Mgd2l0aCB0aGUgc2FtZSBzaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgemVyb3NMaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3MoYW5vdGhlci5zaGFwZSkgYXMgVDtcbiAgfVxuXG4gIC8qKiBDcmVhdGVzIGEgbmRhcnJheSB3aXRoIHRoZSBzYW1lIHZhbHVlcy9zaGFwZSBhcyB0aGUgc3BlY2lmaWVkIG5kYXJyYXkuICovXG4gIHN0YXRpYyBsaWtlPFQgZXh0ZW5kcyBOREFycmF5Pihhbm90aGVyOiBUKTogVCB7XG4gICAgY29uc3QgdmFsdWVzID0gYW5vdGhlci5nZXRWYWx1ZXMoKTtcbiAgICByZXR1cm4gTkRBcnJheS5tYWtlPFQ+KGFub3RoZXIuc2hhcGUsIHt2YWx1ZXM6IG5ldyBGbG9hdDMyQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE1ha2VzIGEgbmV3IG5kYXJyYXkgd2l0aCB0aGUgcHJvdmlkZWQgc2hhcGUgYW5kIHZhbHVlcy4gVmFsdWVzIHNob3VsZCBiZSBpblxuICAgKiBhIGZsYXQgYXJyYXkuXG4gICAqL1xuICBzdGF0aWMgbWFrZTxUIGV4dGVuZHMgTkRBcnJheT4oc2hhcGU6IG51bWJlcltdLCBkYXRhOiBOREFycmF5RGF0YSk6IFQge1xuICAgIHN3aXRjaCAoc2hhcGUubGVuZ3RoKSB7XG4gICAgICBjYXNlIDA6XG4gICAgICAgIHJldHVybiBuZXcgU2NhbGFyKGRhdGEpIGFzIFQ7XG4gICAgICBjYXNlIDE6XG4gICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTFEKGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgMjpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IEFycmF5MkQoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgY2FzZSAzOlxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgICAgIHJldHVybiBuZXcgQXJyYXkzRChzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGEpIGFzIGFueTtcbiAgICAgIGNhc2UgNDpcbiAgICAgICAgcmV0dXJuIG5ldyBBcnJheTREKFxuICAgICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgICAgICAgICAgICAgICBzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZGF0YSkgYXMgYW55O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgICAgICByZXR1cm4gbmV3IE5EQXJyYXkoc2hhcGUsIGRhdGEpIGFzIGFueTtcbiAgICB9XG4gIH1cblxuICAvKiogUmVzaGFwZXMgdGhlIGN1cnJlbnQgbmRhcnJheSBpbnRvIHRoZSBwcm92aWRlZCBzaGFwZS4gKi9cbiAgcmVzaGFwZTxUIGV4dGVuZHMgTkRBcnJheT4obmV3U2hhcGU6IG51bWJlcltdKTogVCB7XG4gICAgaWYgKHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgbmV3U2hhcGUpKSB7XG4gICAgICAvLyBOby1vcC5cbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHJldHVybiB0aGlzIGFzIGFueTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdGhpcy5zaXplID09PSB1dGlsLnNpemVGcm9tU2hhcGUobmV3U2hhcGUpLFxuICAgICAgICAnbmV3IHNoYXBlIGFuZCBvbGQgc2hhcGUgbXVzdCBoYXZlIHRoZSBzYW1lIG51bWJlciBvZiBlbGVtZW50cy4nKTtcblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4obmV3U2hhcGUsIHRoaXMuZGF0YSk7XG4gIH1cblxuICBhc1NjYWxhcigpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KHRoaXMuc2l6ZSA9PT0gMSwgJ1RoZSBhcnJheSBtdXN0IGhhdmUgb25seSAxIGVsZW1lbnQuJyk7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxTY2FsYXI+KFtdKTtcbiAgfVxuXG4gIGFzMUQoKTogQXJyYXkxRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTFEPihbdGhpcy5zaXplXSk7XG4gIH1cblxuICBhczJEKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogQXJyYXkyRCB7XG4gICAgcmV0dXJuIHRoaXMucmVzaGFwZTxBcnJheTJEPihbcm93cywgY29sdW1uc10pO1xuICB9XG5cbiAgYXMzRChyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgZGVwdGg6IG51bWJlcik6IEFycmF5M0Qge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXkzRD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoXSk7XG4gIH1cblxuICBhczREKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCBkZXB0aDogbnVtYmVyLCBkZXB0aDI6IG51bWJlcik6IEFycmF5NEQge1xuICAgIHJldHVybiB0aGlzLnJlc2hhcGU8QXJyYXk0RD4oW3Jvd3MsIGNvbHVtbnMsIGRlcHRoLCBkZXB0aDJdKTtcbiAgfVxuXG4gIGdldCByYW5rKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuc2hhcGUubGVuZ3RoO1xuICB9XG5cbiAgZ2V0KC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmdldFZhbHVlcygpW2luZGV4XTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCAuLi5sb2NzOiBudW1iZXJbXSkge1xuICAgIHRoaXMuc2V0KHRoaXMuZ2V0KC4uLmxvY3MpICsgdmFsdWUsIC4uLmxvY3MpO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIC4uLmxvY3M6IG51bWJlcltdKSB7XG4gICAgbGV0IGluZGV4ID0gbG9jc1tsb2NzLmxlbmd0aCAtIDFdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbG9jcy5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgIGluZGV4ICs9IHRoaXMuc3RyaWRlc1tpXSAqIGxvY3NbaV07XG4gICAgfVxuICAgIHRoaXMuZ2V0VmFsdWVzKClbaW5kZXhdID0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IG51bWJlcltdKTogbnVtYmVyIHtcbiAgICBsZXQgaW5kZXggPSBsb2NzW2xvY3MubGVuZ3RoIC0gMV07XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsb2NzLmxlbmd0aCAtIDE7ICsraSkge1xuICAgICAgaW5kZXggKz0gdGhpcy5zdHJpZGVzW2ldICogbG9jc1tpXTtcbiAgICB9XG4gICAgcmV0dXJuIGluZGV4O1xuICB9XG5cbiAgaW5kZXhUb0xvYyhpbmRleDogbnVtYmVyKTogbnVtYmVyW10ge1xuICAgIGNvbnN0IGxvY3M6IG51bWJlcltdID0gbmV3IEFycmF5KHRoaXMuc2hhcGUubGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxvY3MubGVuZ3RoIC0gMTsgKytpKSB7XG4gICAgICBsb2NzW2ldID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlc1tpXSk7XG4gICAgICBpbmRleCAtPSBsb2NzW2ldICogdGhpcy5zdHJpZGVzW2ldO1xuICAgIH1cbiAgICBsb2NzW2xvY3MubGVuZ3RoIC0gMV0gPSBpbmRleDtcbiAgICByZXR1cm4gbG9jcztcbiAgfVxuXG4gIGZpbGwodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKCkuZmlsbCh2YWx1ZSk7XG4gIH1cblxuICBnZXREYXRhKCk6IE5EQXJyYXlEYXRhIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhO1xuICB9XG5cbiAgZ2V0VmFsdWVzKCk6IEZsb2F0MzJBcnJheSB7XG4gICAgaWYgKHRoaXMuZGF0YS52YWx1ZXMgPT0gbnVsbCkge1xuICAgICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgICB0aGlzLmRhdGEudmFsdWVzID0gR1BHUFUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyFbMF0sXG4gICAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDIVsxXSk7XG4gICAgICB0aGlzLmRpc3Bvc2VUZXh0dXJlKCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRhdGEudmFsdWVzO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGxvYWRUb0dQVShwcmVmZXJyZWRUZXhTaGFwZT86IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgICB0aHJvd0lmR1BVTm90SW5pdGlhbGl6ZWQoKTtcbiAgICB0aGlzLmRhdGEudGV4dHVyZVNoYXBlUkMgPSB3ZWJnbF91dGlsLmdldFRleHR1cmVTaGFwZUZyb21Mb2dpY2FsU2hhcGUoXG4gICAgICAgIEdQR1BVLmdsLCB0aGlzLnNoYXBlLCBwcmVmZXJyZWRUZXhTaGFwZSk7XG4gICAgdGhpcy5kYXRhLnRleHR1cmUgPVxuICAgICAgICBURVhUVVJFX01BTkFHRVIuYWNxdWlyZVRleHR1cmUodGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDKTtcblxuICAgIEdQR1BVLnVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmUsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQ1swXSxcbiAgICAgICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDWzFdLCB0aGlzLmRhdGEudmFsdWVzISk7XG5cbiAgICB0aGlzLmRhdGEudmFsdWVzID0gbnVsbCE7XG4gIH1cblxuICBnZXRUZXh0dXJlKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogV2ViR0xUZXh0dXJlIHtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgPT0gbnVsbCkge1xuICAgICAgdGhpcy51cGxvYWRUb0dQVShwcmVmZXJyZWRTaGFwZVJDKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGF0YS50ZXh0dXJlITtcbiAgfVxuXG4gIGdldFRleHR1cmVTaGFwZVJDKHByZWZlcnJlZFNoYXBlUkM/OiBbbnVtYmVyLCBudW1iZXJdKTogW251bWJlciwgbnVtYmVyXSB7XG4gICAgaWYgKHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyA9PSBudWxsKSB7XG4gICAgICB0aGlzLnVwbG9hZFRvR1BVKHByZWZlcnJlZFNoYXBlUkMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDITtcbiAgfVxuXG4gIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5kYXRhLnZhbHVlcyA9IG51bGwhO1xuICAgIHRoaXMuc2hhcGUgPSBudWxsITtcbiAgICBpZiAodGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbCkge1xuICAgICAgdGhpcy5kaXNwb3NlVGV4dHVyZSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZGlzcG9zZVRleHR1cmUoKSB7XG4gICAgdGhyb3dJZkdQVU5vdEluaXRpYWxpemVkKCk7XG4gICAgVEVYVFVSRV9NQU5BR0VSLnJlbGVhc2VUZXh0dXJlKFxuICAgICAgICB0aGlzLmRhdGEudGV4dHVyZSEsIHRoaXMuZGF0YS50ZXh0dXJlU2hhcGVSQyEpO1xuICAgIHRoaXMuZGF0YS50ZXh0dXJlID0gbnVsbCE7XG4gICAgdGhpcy5kYXRhLnRleHR1cmVTaGFwZVJDID0gbnVsbCE7XG4gIH1cblxuICBpbkdQVSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5kYXRhLnRleHR1cmUgIT0gbnVsbDtcbiAgfVxuXG4gIGVxdWFscyh0OiBOREFycmF5KTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHV0aWwuYXJyYXlzRXF1YWwodGhpcy5zaGFwZSwgdC5zaGFwZSkgJiZcbiAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh0aGlzLmdldFZhbHVlcygpLCB0LmdldFZhbHVlcygpKTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIHJhbmRGdW5jdGlvbjogKCkgPT4gbnVtYmVyKTpcbiAgICAgIFQge1xuICAgIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUoc2hhcGUpO1xuICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBGbG9hdDMyQXJyYXkoc2l6ZSk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzaXplOyBpKyspIHtcbiAgICAgIHZhbHVlc1tpXSA9IHJhbmRGdW5jdGlvbigpO1xuICAgIH1cblxuICAgIHJldHVybiBOREFycmF5Lm1ha2U8VD4oc2hhcGUsIHt2YWx1ZXN9KTtcbiAgfVxuXG4gIHN0YXRpYyByYW5kTm9ybWFsPFQgZXh0ZW5kcyBOREFycmF5PihzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2KSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFRydW5jYXRlZE5vcm1hbDxUIGV4dGVuZHMgTkRBcnJheT4oXG4gICAgICBzaGFwZTogbnVtYmVyW10sIG1lYW4gPSAwLCBzdGREZXYgPSAxKSB7XG4gICAgcmV0dXJuIE5EQXJyYXkucmFuZDxUPihzaGFwZSwgKCkgPT4gdXRpbC5yYW5kR2F1c3MobWVhbiwgc3RkRGV2LCB0cnVlKSk7XG4gIH1cblxuICBzdGF0aWMgcmFuZFVuaWZvcm08VCBleHRlbmRzIE5EQXJyYXk+KHNoYXBlOiBudW1iZXJbXSwgYTogbnVtYmVyLCBiOiBudW1iZXIpIHtcbiAgICByZXR1cm4gTkRBcnJheS5yYW5kPFQ+KHNoYXBlLCAoKSA9PiB1dGlsLnJhbmRVbmlmb3JtKGEsIGIpKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU2NhbGFyIGV4dGVuZHMgTkRBcnJheSB7XG4gIGNvbnN0cnVjdG9yKGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgaWYgKGRhdGEudGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICBkYXRhLnRleHR1cmVTaGFwZVJDID0gWzEsIDFdO1xuICAgIH1cbiAgICBzdXBlcihbXSwgZGF0YSk7XG4gIH1cblxuICBzdGF0aWMgbmV3KHZhbHVlOiBudW1iZXIpIHtcbiAgICByZXR1cm4gbmV3IFNjYWxhcih7dmFsdWVzOiBuZXcgRmxvYXQzMkFycmF5KFt2YWx1ZV0pfSk7XG4gIH1cblxuICBzdGF0aWMgWkVSTyA9IFNjYWxhci5uZXcoMCk7XG4gIHN0YXRpYyBPTkUgPSBTY2FsYXIubmV3KDEpO1xuICBzdGF0aWMgVFdPID0gU2NhbGFyLm5ldygyKTtcbiAgc3RhdGljIE5FR19PTkUgPSBTY2FsYXIubmV3KC0xKTtcblxuICBnZXQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVswXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVswXSA9IHZhbHVlO1xuICB9XG5cbiAgYWRkKHZhbHVlOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpWzBdICs9IHZhbHVlO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTFEIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyXTtcblxuICBjb25zdHJ1Y3RvcihkYXRhOiBOREFycmF5RGF0YSkge1xuICAgIGNvbnN0IHNoYXBlID0gKGRhdGEudmFsdWVzICE9IG51bGwpID9cbiAgICAgICAgW2RhdGEudmFsdWVzLmxlbmd0aF0gOlxuICAgICAgICBbdXRpbC5zaXplRnJvbVNoYXBlKGRhdGEudGV4dHVyZVNoYXBlUkMhKV07XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICB9XG5cbiAgc3RhdGljIG5ldyh2YWx1ZXM6IEZsb2F0MzJBcnJheXxudW1iZXJbXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICAgIGluZmVycmVkU2hhcGUubGVuZ3RoID09PSAxLFxuICAgICAgICAgIGBFcnJvciBjb25zdHJ1Y3RpbmcgQXJyYXkxRC4gU2hhcGUgb2YgdmFsdWVzICR7aW5mZXJyZWRTaGFwZX0gaXMgYCArXG4gICAgICAgICAgICAgIGBub3QgMSBkaW1lbnNpb25hbC5gKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTFEKHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVtpXTtcbiAgfVxuXG4gIHNldCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW2ldID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVtpXSArPSB2YWx1ZTtcbiAgfVxuXG4gIGxvY1RvSW5kZXgobG9jOiBbbnVtYmVyXSk6IG51bWJlciB7XG4gICAgcmV0dXJuIGxvY1swXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXJdIHtcbiAgICByZXR1cm4gW2luZGV4XTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlcl0pOiBBcnJheTFEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTFEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5MkQgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlcl07XG5cbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG5cbiAgY29uc3RydWN0b3Ioc2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAyLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAyJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgfVxuXG4gIHN0YXRpYyBuZXcoXG4gICAgICBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdmFsdWVzOiBGbG9hdDMyQXJyYXl8bnVtYmVyW118bnVtYmVyW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5MkQuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTJEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgal0gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClbdGhpcy5zdHJpZGUwICogaSArIGpdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIGxvY3NbMV07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgICByZXR1cm4gW01hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTApLCBpbmRleCAlIHRoaXMuc3RyaWRlMF07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlcl0pOiBBcnJheTJEIHtcbiAgICByZXR1cm4gTkRBcnJheS56ZXJvczxBcnJheTJEPihzaGFwZSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEFycmF5M0QgZXh0ZW5kcyBOREFycmF5IHtcbiAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSAzLCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCAzJyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gIH1cblxuICBzdGF0aWMgbmV3KFxuICAgICAgc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXSkge1xuICAgIGlmICghKHZhbHVlcyBpbnN0YW5jZW9mIEZsb2F0MzJBcnJheSkpIHtcbiAgICAgIGNvbnN0IGluZmVycmVkU2hhcGUgPSB1dGlsLmluZmVyU2hhcGUodmFsdWVzKTtcbiAgICAgIGlmIChpbmZlcnJlZFNoYXBlLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChcbiAgICAgICAgICAgIHNoYXBlLCBpbmZlcnJlZFNoYXBlLFxuICAgICAgICAgICAgYEVycm9yIHdoZW4gY29uc3RydWN0aW5nIEFycmF5M0QuIFNoYXBlIG9mIHZhbHVlcyBgICtcbiAgICAgICAgICAgICAgICBgJHtpbmZlcnJlZFNoYXBlfSBkb2VzIG5vdCBtYXRjaCB0aGUgcHJvdmlkZWQgc2hhcGUgYCArXG4gICAgICAgICAgICAgICAgYCR7c2hhcGV9LiBgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG5ldyBBcnJheTNEKHNoYXBlLCB7dmFsdWVzOiB0b1R5cGVkQXJyYXkodmFsdWVzKX0pO1xuICB9XG5cbiAgZ2V0KGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIpIHtcbiAgICB0aGlzLmdldFZhbHVlcygpW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsga10gPSB2YWx1ZTtcbiAgfVxuXG4gIGFkZCh2YWx1ZTogbnVtYmVyLCBpOiBudW1iZXIsIGo6IG51bWJlciwgazogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVt0aGlzLnN0cmlkZTAgKiBpICsgdGhpcy5zdHJpZGUxICogaiArIGtdICs9IHZhbHVlO1xuICB9XG5cbiAgbG9jVG9JbmRleChsb2NzOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0pOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLnN0cmlkZTAgKiBsb2NzWzBdICsgdGhpcy5zdHJpZGUxICogbG9jc1sxXSArIGxvY3NbMl07XG4gIH1cblxuICBpbmRleFRvTG9jKGluZGV4OiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0ge1xuICAgIGNvbnN0IGkgPSBNYXRoLmZsb29yKGluZGV4IC8gdGhpcy5zdHJpZGUwKTtcbiAgICBpbmRleCAtPSBpICogdGhpcy5zdHJpZGUwO1xuICAgIHJldHVybiBbaSwgTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSksIGluZGV4ICUgdGhpcy5zdHJpZGUxXTtcbiAgfVxuXG4gIHN0YXRpYyB6ZXJvcyhzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXkzRCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXkzRD4oc2hhcGUpO1xuICB9XG59XG5cbmV4cG9ydCBjbGFzcyBBcnJheTREIGV4dGVuZHMgTkRBcnJheSB7XG4gIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbiAgcHJpdmF0ZSBzdHJpZGUwOiBudW1iZXI7XG4gIHByaXZhdGUgc3RyaWRlMTogbnVtYmVyO1xuICBwcml2YXRlIHN0cmlkZTI6IG51bWJlcjtcblxuICBjb25zdHJ1Y3RvcihzaGFwZTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGRhdGE6IE5EQXJyYXlEYXRhKSB7XG4gICAgdXRpbC5hc3NlcnQoc2hhcGUubGVuZ3RoID09PSA0LCAnU2hhcGUgc2hvdWxkIGJlIG9mIGxlbmd0aCA0Jyk7XG4gICAgc3VwZXIoc2hhcGUsIGRhdGEpO1xuICAgIHRoaXMuc3RyaWRlMCA9IHRoaXMuc3RyaWRlc1swXTtcbiAgICB0aGlzLnN0cmlkZTEgPSB0aGlzLnN0cmlkZXNbMV07XG4gICAgdGhpcy5zdHJpZGUyID0gdGhpcy5zdHJpZGVzWzJdO1xuICB9XG5cbiAgc3RhdGljIG5ldyhcbiAgICAgIHNoYXBlOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICAgIHZhbHVlczogRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW11bXVtdKSB7XG4gICAgaWYgKCEodmFsdWVzIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSkge1xuICAgICAgY29uc3QgaW5mZXJyZWRTaGFwZSA9IHV0aWwuaW5mZXJTaGFwZSh2YWx1ZXMpO1xuICAgICAgaWYgKGluZmVycmVkU2hhcGUubGVuZ3RoID4gMSkge1xuICAgICAgICB1dGlsLmFzc2VydFNoYXBlc01hdGNoKFxuICAgICAgICAgICAgc2hhcGUsIGluZmVycmVkU2hhcGUsXG4gICAgICAgICAgICBgRXJyb3Igd2hlbiBjb25zdHJ1Y3RpbmcgQXJyYXk0RC4gU2hhcGUgb2YgdmFsdWVzIGAgK1xuICAgICAgICAgICAgICAgIGAke2luZmVycmVkU2hhcGV9IGRvZXMgbm90IG1hdGNoIHRoZSBwcm92aWRlZCBzaGFwZSBgICtcbiAgICAgICAgICAgICAgICBgJHtzaGFwZX0uIGApO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbmV3IEFycmF5NEQoc2hhcGUsIHt2YWx1ZXM6IHRvVHlwZWRBcnJheSh2YWx1ZXMpfSk7XG4gIH1cblxuICBnZXQoaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdO1xuICB9XG5cbiAgc2V0KHZhbHVlOiBudW1iZXIsIGk6IG51bWJlciwgajogbnVtYmVyLCBrOiBudW1iZXIsIGw6IG51bWJlcikge1xuICAgIHRoaXMuZ2V0VmFsdWVzKClcbiAgICAgICAgW3RoaXMuc3RyaWRlMCAqIGkgKyB0aGlzLnN0cmlkZTEgKiBqICsgdGhpcy5zdHJpZGUyICogayArIGxdID0gdmFsdWU7XG4gIH1cblxuICBhZGQodmFsdWU6IG51bWJlciwgaTogbnVtYmVyLCBqOiBudW1iZXIsIGs6IG51bWJlciwgbDogbnVtYmVyKSB7XG4gICAgdGhpcy5nZXRWYWx1ZXMoKVxuICAgICAgICBbdGhpcy5zdHJpZGUwICogaSArIHRoaXMuc3RyaWRlMSAqIGogKyB0aGlzLnN0cmlkZTIgKiBrICsgbF0gKz0gdmFsdWU7XG4gIH1cblxuICBsb2NUb0luZGV4KGxvY3M6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zdHJpZGUwICogbG9jc1swXSArIHRoaXMuc3RyaWRlMSAqIGxvY3NbMV0gK1xuICAgICAgICB0aGlzLnN0cmlkZTIgKiBsb2NzWzJdICsgbG9jc1szXTtcbiAgfVxuXG4gIGluZGV4VG9Mb2MoaW5kZXg6IG51bWJlcik6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdIHtcbiAgICBjb25zdCBpID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMCk7XG4gICAgaW5kZXggLT0gaSAqIHRoaXMuc3RyaWRlMDtcbiAgICBjb25zdCBqID0gTWF0aC5mbG9vcihpbmRleCAvIHRoaXMuc3RyaWRlMSk7XG4gICAgaW5kZXggLT0gaiAqIHRoaXMuc3RyaWRlMTtcbiAgICByZXR1cm4gW2ksIGosIE1hdGguZmxvb3IoaW5kZXggLyB0aGlzLnN0cmlkZTIpLCBpbmRleCAlIHRoaXMuc3RyaWRlMl07XG4gIH1cblxuICBzdGF0aWMgemVyb3Moc2hhcGU6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyLCBudW1iZXJdKTogQXJyYXk0RCB7XG4gICAgcmV0dXJuIE5EQXJyYXkuemVyb3M8QXJyYXk0RD4oc2hhcGUpO1xuICB9XG59XG5cbnR5cGUgQXJyYXlEYXRhID0gRmxvYXQzMkFycmF5fG51bWJlcltdfG51bWJlcltdW118bnVtYmVyW11bXVtdfG51bWJlcltdW11bXVtdO1xuXG5mdW5jdGlvbiB0b1R5cGVkQXJyYXkoYTogQXJyYXlEYXRhKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIChhIGluc3RhbmNlb2YgRmxvYXQzMkFycmF5KSA/IGEgOiBuZXcgRmxvYXQzMkFycmF5KHV0aWwuZmxhdHRlbihhKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEFTY2FsYXI7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QlNjYWxhcjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZUZXhlbCA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgYSA9IHRleHR1cmUyRChtYXRyaXhBLCByZXN1bHRVVikucjtcbiAgICAgIGZsb2F0IGIgPSB0ZXh0dXJlMkQobWF0cml4QiwgcmVzdWx0VVYpLnI7XG4gICAgICBmbG9hdCBhU2NhbGFyID0gdGV4dHVyZTJEKG1hdHJpeEFTY2FsYXIsIGhhbGZUZXhlbCkucjtcbiAgICAgIGZsb2F0IGJTY2FsYXIgPSB0ZXh0dXJlMkQobWF0cml4QlNjYWxhciwgaGFsZlRleGVsKS5yO1xuICAgICAgdmVjMiBhYlNjYWxlZCA9IHZlYzIoYSwgYikgKiB2ZWMyKGFTY2FsYXIsIGJTY2FsYXIpO1xuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChhYlNjYWxlZC54ICsgYWJTY2FsZWQueSwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhZGRTY2FsZWRNYXRyaWNlcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBhZGRTY2FsZWRNYXRyaWNlc1Byb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICBhOiBXZWJHTFRleHR1cmUsIGI6IFdlYkdMVGV4dHVyZSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsXG4gICAgYVNjYWxhcjogV2ViR0xUZXh0dXJlLCBiU2NhbGFyOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShhZGRTY2FsZWRNYXRyaWNlc1Byb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhU2NhbGFyLCAnbWF0cml4QVNjYWxhcicsIDIpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYlNjYWxhciwgJ21hdHJpeEJTY2FsYXInLCAzKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZEFkZFNjYWxlZE1hdHJpY2VzRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBiOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIGFTY2FsYXI6IG51bWJlciwgYlNjYWxhcjogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKSk7XG5cbiAgY29uc3QgYVRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IGJUZXggPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBhU2NhbGFyVGV4ID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZSgxLCAxKTtcbiAgY29uc3QgYlNjYWxhclRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGNvbnN0IHJlc3VsdFRleCA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocm93cywgY29sdW1ucyk7XG5cbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXgsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYlRleCwgcm93cywgY29sdW1ucywgYik7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhU2NhbGFyVGV4LCAxLCAxLCBuZXcgRmxvYXQzMkFycmF5KFthU2NhbGFyXSkpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYlNjYWxhclRleCwgMSwgMSwgbmV3IEZsb2F0MzJBcnJheShbYlNjYWxhcl0pKTtcblxuICBhZGRTY2FsZWRNYXRyaWNlcyhcbiAgICAgIGdwZ3B1LCBwcm9ncmFtLCBhVGV4LCBiVGV4LCByb3dzLCBjb2x1bW5zLCBhU2NhbGFyVGV4LCBiU2NhbGFyVGV4LFxuICAgICAgcmVzdWx0VGV4KTtcblxuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleCwgcm93cywgY29sdW1ucyk7XG5cbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShiVGV4KTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXgpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFTY2FsYXJUZXgpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGJTY2FsYXJUZXgpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vbWF0aCc7XG5cbmltcG9ydCAqIGFzIGJpbmFyeW9wX2dwdSBmcm9tICcuL2JpbmFyeW9wX2dwdSc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IHR5cGUgT3BlcmF0aW9uID0gJysnIHwgJy0nIHwgJyonIHwgJy8nO1xuXG5leHBvcnQgZW51bSBPcGVyYW5kVHlwZSB7XG4gIE1BVFJJWCxcbiAgU0NBTEFSXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBhVHlwZTogT3BlcmFuZFR5cGUsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sIG9wOiBPcGVyYXRpb24sXG4gICAgYlR5cGU6IE9wZXJhbmRUeXBlLCBiT3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogc3RyaW5nIHtcbiAgY29uc3QgYVVWID0gb3BlcmFuZFRvU2hhZGVyU25pcHBldChhVHlwZSwgYU9yaWVudGF0aW9uKTtcbiAgY29uc3QgYlVWID0gb3BlcmFuZFRvU2hhZGVyU25pcHBldChiVHlwZSwgYk9yaWVudGF0aW9uKTtcbiAgY29uc3QgcmVzdWx0T3AgPSBgZ2xfRnJhZ0NvbG9yID0gdmVjNChhICR7b3B9IGIsIDAsIDAsIDApO2A7XG4gIHJldHVybiBiaW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoYVVWLCBiVVYsIHJlc3VsdE9wKTtcbn1cblxuZnVuY3Rpb24gb3BlcmFuZFRvU2hhZGVyU25pcHBldChcbiAgICBvcGVyYW5kOiBPcGVyYW5kVHlwZSwgb3JpZW50YXRpb246IE1hdHJpeE9yaWVudGF0aW9uKTogc3RyaW5nIHtcbiAgc3dpdGNoIChvcGVyYW5kKSB7XG4gICAgY2FzZSBPcGVyYW5kVHlwZS5NQVRSSVg6XG4gICAgICByZXR1cm4gJ3Jlc3VsdFVWJyArXG4gICAgICAgICAgKG9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSID8gJy5zdCcgOiAnLnRzJyk7XG4gICAgY2FzZSBPcGVyYW5kVHlwZS5TQ0FMQVI6XG4gICAgICByZXR1cm4gJ3ZlYzIoMC41LCAwLjUpJztcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKCdVbmtub3duIG9wZXJhbmQgdHlwZScpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhZGRTdWJNdWxEaXYoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBXZWJHTFRleHR1cmUsXG4gICAgYlNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LmJpbmFyeU9wKFxuICAgICAgZ3BncHUsIHByb2dyYW0sIGEsIGFTaGFwZVJvd0NvbCwgYiwgYlNoYXBlUm93Q29sLCByZXN1bHQsXG4gICAgICByZXN1bHRTaGFwZVJvd0NvbCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTY2FsYXJQbHVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICcrJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeE1pbnVzU2NhbGFyRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBhU2hhcGU6IFtudW1iZXIsIG51bWJlcl0sIGI6IG51bWJlcixcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5NQVRSSVgsIGFPcmllbnRhdGlvbiwgJy0nLCBPcGVyYW5kVHlwZS5TQ0FMQVIsXG4gICAgICBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgYSwgYVNoYXBlLCBuZXcgRmxvYXQzMkFycmF5KFtiXSksIFsxLCAxXSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNjYWxhck1pbnVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICctJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNjYWxhclRpbWVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogbnVtYmVyLCBiOiBGbG9hdDMyQXJyYXksIGJTaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBiT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3Qgc3JjID0gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgICBPcGVyYW5kVHlwZS5TQ0FMQVIsIE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsICcqJywgT3BlcmFuZFR5cGUuTUFUUklYLFxuICAgICAgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgICAgbmV3IEZsb2F0MzJBcnJheShbYV0pLCBbMSwgMV0sIGIsIGJTaGFwZSwgc3JjKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFRpbWVzTWF0cml4RG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCBiOiBGbG9hdDMyQXJyYXksIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIGFPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIsXG4gICAgYk9yaWVudGF0aW9uID0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHNyYyA9IGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgICAgT3BlcmFuZFR5cGUuTUFUUklYLCBhT3JpZW50YXRpb24sICcqJywgT3BlcmFuZFR5cGUuTUFUUklYLCBiT3JpZW50YXRpb24pO1xuICByZXR1cm4gYmluYXJ5b3BfZ3B1LnVwbG9hZEJpbmFyeU9wRG93bmxvYWQoYSwgc2hhcGUsIGIsIHNoYXBlLCBzcmMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTWF0cml4UGx1c01hdHJpeERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYjogRmxvYXQzMkFycmF5LCBzaGFwZTogW251bWJlciwgbnVtYmVyXSxcbiAgICBhT3JpZW50YXRpb24gPSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgIGJPcmllbnRhdGlvbiA9IE1hdHJpeE9yaWVudGF0aW9uLlJFR1VMQVIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBzcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICAgIE9wZXJhbmRUeXBlLk1BVFJJWCwgYU9yaWVudGF0aW9uLCAnKycsIE9wZXJhbmRUeXBlLk1BVFJJWCwgYk9yaWVudGF0aW9uKTtcbiAgcmV0dXJuIGJpbmFyeW9wX2dwdS51cGxvYWRCaW5hcnlPcERvd25sb2FkKGEsIHNoYXBlLCBiLCBzaGFwZSwgc3JjKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgYXJnbWlubWF4X2dwdSBmcm9tICcuL2FyZ21pbm1heF9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQcm9sb2d1ZVNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEI7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGFyZ01heEEgPSBnZXRBcmdNaW5NYXgobWF0cml4QSk7XG4gICAgICBmbG9hdCBhcmdNYXhCID0gZ2V0QXJnTWluTWF4KG1hdHJpeEIpO1xuICAgICAgZmxvYXQgdmFsdWU7XG4gICAgICBpZiAoaXNOYU4oYXJnTWF4QSkpIHtcbiAgICAgICAgdmFsdWUgPSBhcmdNYXhBO1xuICAgICAgfSBlbHNlIGlmIChpc05hTihhcmdNYXhCKSkge1xuICAgICAgICB2YWx1ZSA9IGFyZ01heEI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB2YWx1ZSA9IGZsb2F0KGFyZ01heEEgPT0gYXJnTWF4Qik7XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01heEVxdWFsc0ZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIFtcbiAgICBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCksXG4gICAgYXJnbWlubWF4X2dwdS5nZXRGcmFnbWVudFNoYWRlckdldEFyZ01pbk1heFNvdXJjZSgnPicsIHJvd3MsIGNvbHVtbnMpLFxuICAgIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpXG4gIF0uam9pbignXFxuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhcmdNYXhFcXVhbHMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbWF4RXF1YWxzUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYjogV2ViR0xUZXh0dXJlLCBudW1Sb3dzOiBudW1iZXIsIG51bUNvbHM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1heEVxdWFsc1Byb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0IHtJU19OQU5fU0hBREVSX0ZVTkN9IGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIG1hdHJpeEE7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO2A7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWFpblNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZ2V0QXJnTWluTWF4KG1hdHJpeEEpLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZnVuY3Rpb24gZ2V0QXJnTWluTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGNvbXBPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIFtcbiAgICBnZXRGcmFnbWVudFNoYWRlclByb2xvZ3VlU291cmNlKCksXG4gICAgZ2V0RnJhZ21lbnRTaGFkZXJHZXRBcmdNaW5NYXhTb3VyY2UoY29tcE9wLCByb3dzLCBjb2x1bW5zKSxcbiAgICBnZXRGcmFnbWVudFNoYWRlck1haW5Tb3VyY2UoKVxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXJnTWluRnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gZ2V0QXJnTWluTWF4RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93cywgY29sdW1ucywgJzwnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFyZ01heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEFyZ01pbk1heEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICc+Jyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckdldEFyZ01pbk1heFNvdXJjZShcbiAgICBjb21wT3A6IHN0cmluZywgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpIHtcbiAgcmV0dXJuIGBcbiAgICBjb25zdCB2ZWMyIGRpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgZmxvYXQgZ2V0QXJnTWluTWF4KGluIHNhbXBsZXIyRCBtYXRyaXgpIHtcbiAgICAgIHZlYzIgYmVzdENSID0gdmVjMigwLCAwKTtcbiAgICAgIGZsb2F0IGJlc3RWYWx1ZSA9IHRleHR1cmUyRChtYXRyaXgsIGJlc3RDUikucjtcblxuICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgZGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgZGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIGNyID0gdmVjMihjLCByKTtcbiAgICAgICAgICB2ZWMyIHV2ID0gKGNyICsgaGFsZkNSKSAvIGRpbUNSO1xuICAgICAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeCwgdXYpLnI7XG4gICAgICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodmFsdWUgJHtjb21wT3B9IGJlc3RWYWx1ZSkge1xuICAgICAgICAgICAgYmVzdFZhbHVlID0gdmFsdWU7XG4gICAgICAgICAgICBiZXN0Q1IgPSBjcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBiZXN0Q1IueCArIChiZXN0Q1IueSAqIGRpbUNSLngpO1xuICAgIH1cbiAgYDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFyZ01pbk1heChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBtaW5NYXhQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obWluTWF4UHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHBvb2xfZ3B1IGZyb20gJy4vcG9vbF9ncHUnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJBdmdQb29sU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlcikge1xuICByZXR1cm4gcG9vbF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgICAgeFNoYXBlUkNELCBmU2l6ZSwgc3RyaWRlLCBwYWQsICdhdmcnLCBmYWxzZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhdmdQb29sKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgeFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0sIG1lYW5UZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdLFxuICAgIHZhcmlhbmNlVGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSxcbiAgICBvZmZzZXRUZXhTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdfG51bGwsXG4gICAgc2NhbGVUZXhTaGFwZVJDPzogW251bWJlciwgbnVtYmVyXXxudWxsLCB2YXJpYW5jZUVwc2lsb24gPSAwLjAwMSk6IHN0cmluZyB7XG4gIGxldCBvZmZzZXRTYW1wbGVyU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldENvb3Jkc1NuaXBwZXQgPSAnJztcbiAgbGV0IG9mZnNldFVWU25pcHBldCA9ICcnO1xuICBsZXQgb2Zmc2V0VmFsdWVTbmlwcGV0ID0gJyc7XG4gIGxldCBvZmZzZXRPcGVyYXRpb25TbmlwcGV0ID0gJzAuMCc7XG5cbiAgbGV0IHNjYWxlU2FtcGxlclNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlU2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlQ29vcmRzU25pcHBldCA9ICcnO1xuICBsZXQgc2NhbGVVVlNuaXBwZXQgPSAnJztcbiAgbGV0IHNjYWxlVmFsdWVTbmlwcGV0ID0gJyc7XG4gIGxldCBzY2FsZU9wZXJhdGlvblNuaXBwZXQgPSAnJztcblxuICBpZiAob2Zmc2V0VGV4U2hhcGVSQyAhPSBudWxsKSB7XG4gICAgb2Zmc2V0U2FtcGxlclNuaXBwZXQgPSAndW5pZm9ybSBzYW1wbGVyMkQgb2Zmc2V0Oyc7XG4gICAgb2Zmc2V0U2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXQgPSBgY29uc3QgdmVjMiBvZmZzZXRTaGFwZUNSID0gdmVjMihcbiAgICAgICAgICAgICR7b2Zmc2V0VGV4U2hhcGVSQ1sxXX0sICR7b2Zmc2V0VGV4U2hhcGVSQ1swXX0pO2A7XG4gICAgb2Zmc2V0Q29vcmRzU25pcHBldCA9ICd2ZWMyIG9mZnNldENvb3Jkc0NSID0gbW9kKHlUZXhDUiwgb2Zmc2V0U2hhcGVDUik7JztcbiAgICBvZmZzZXRVVlNuaXBwZXQgPVxuICAgICAgICAndmVjMiBvZmZzZXRVViA9IChvZmZzZXRDb29yZHNDUiArIGhhbGZDUikgLyBvZmZzZXRTaGFwZUNSOyc7XG4gICAgb2Zmc2V0VmFsdWVTbmlwcGV0ID0gJ2Zsb2F0IG9mZnNldFZhbHVlID0gdGV4dHVyZTJEKG9mZnNldCwgb2Zmc2V0VVYpLnI7JztcbiAgICBvZmZzZXRPcGVyYXRpb25TbmlwcGV0ID0gJ29mZnNldFZhbHVlJztcbiAgfVxuXG4gIGlmIChzY2FsZVRleFNoYXBlUkMgIT0gbnVsbCkge1xuICAgIHNjYWxlU2FtcGxlclNuaXBwZXQgPSAndW5pZm9ybSBzYW1wbGVyMkQgc2NhbGU7JztcbiAgICBzY2FsZVNoYXBlSW5pdGlhbGl6YXRpb25TbmlwcGV0ID0gYGNvbnN0IHZlYzIgc2NhbGVTaGFwZUNSID0gdmVjMihcbiAgICAgICAgICAgICR7c2NhbGVUZXhTaGFwZVJDWzFdfSwgJHtzY2FsZVRleFNoYXBlUkNbMF19KTtgO1xuICAgIHNjYWxlQ29vcmRzU25pcHBldCA9ICd2ZWMyIHNjYWxlQ29vcmRzQ1IgPSBtb2QoeVRleENSLCBzY2FsZVNoYXBlQ1IpOyc7XG4gICAgc2NhbGVVVlNuaXBwZXQgPSAndmVjMiBzY2FsZVVWID0gKHNjYWxlQ29vcmRzQ1IgKyBoYWxmQ1IpIC8gc2NhbGVTaGFwZUNSOyc7XG4gICAgc2NhbGVWYWx1ZVNuaXBwZXQgPSAnZmxvYXQgc2NhbGVWYWx1ZSA9IHRleHR1cmUyRChzY2FsZSwgc2NhbGVVVikucjsnO1xuICAgIHNjYWxlT3BlcmF0aW9uU25pcHBldCA9ICdpbnYgKj0gc2NhbGVWYWx1ZTsnO1xuICB9XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtZWFuO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHZhcmlhbmNlO1xuICAgICR7b2Zmc2V0U2FtcGxlclNuaXBwZXR9XG4gICAgJHtzY2FsZVNhbXBsZXJTbmlwcGV0fVxuXG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIG1lYW5TaGFwZUNSID0gdmVjMigke21lYW5UZXhTaGFwZVJDWzFdfSwgJHttZWFuVGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgdmFyaWFuY2VTaGFwZUNSID0gdmVjMihcbiAgICAgICAgJHt2YXJpYW5jZVRleFNoYXBlUkNbMV19LCAke3ZhcmlhbmNlVGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgJHtvZmZzZXRTaGFwZUluaXRpYWxpemF0aW9uU25pcHBldH1cbiAgICAke3NjYWxlU2hhcGVJbml0aWFsaXphdGlvblNuaXBwZXR9XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IGZsb2F0IHZhcmlhbmNlRXBzaWxvbiA9ICR7dmFyaWFuY2VFcHNpbG9ufTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgdmVjMiBtZWFuQ29vcmRzQ1IgPSBtb2QoeVRleENSLCBtZWFuU2hhcGVDUik7XG4gICAgICB2ZWMyIHZhcmlhbmNlQ29vcmRzQ1IgPSBtb2QoeVRleENSLCB2YXJpYW5jZVNoYXBlQ1IpO1xuICAgICAgJHtvZmZzZXRDb29yZHNTbmlwcGV0fVxuICAgICAgJHtzY2FsZUNvb3Jkc1NuaXBwZXR9XG5cbiAgICAgIHZlYzIgbWVhblVWID0gKG1lYW5Db29yZHNDUiArIGhhbGZDUikgLyBtZWFuU2hhcGVDUjtcbiAgICAgIHZlYzIgdmFyaWFuY2VVViA9ICh2YXJpYW5jZUNvb3Jkc0NSICsgaGFsZkNSKSAvIHZhcmlhbmNlU2hhcGVDUjtcbiAgICAgICR7b2Zmc2V0VVZTbmlwcGV0fVxuICAgICAgJHtzY2FsZVVWU25pcHBldH1cblxuICAgICAgZmxvYXQgeFZhbHVlID0gdGV4dHVyZTJEKHgsIHJlc3VsdFVWKS5yO1xuICAgICAgZmxvYXQgbWVhblZhbHVlID0gdGV4dHVyZTJEKG1lYW4sIG1lYW5VVikucjtcbiAgICAgIGZsb2F0IHZhcmlhbmNlVmFsdWUgPSB0ZXh0dXJlMkQodmFyaWFuY2UsIHZhcmlhbmNlVVYpLnI7XG4gICAgICAke29mZnNldFZhbHVlU25pcHBldH1cbiAgICAgICR7c2NhbGVWYWx1ZVNuaXBwZXR9XG5cbiAgICAgIGZsb2F0IGludiA9IDEuMCAvIHNxcnQodmFyaWFuY2VWYWx1ZSArIHZhcmlhbmNlRXBzaWxvbik7XG4gICAgICAke3NjYWxlT3BlcmF0aW9uU25pcHBldH1cbiAgICAgIGZsb2F0IHhUaW1lc0ludiA9IHhWYWx1ZSAqIGludjtcbiAgICAgIGZsb2F0IG1lYW5UaW1lc0ludldpdGhPZmZzZXQgPSAke29mZnNldE9wZXJhdGlvblNuaXBwZXR9XG4gICAgICAgICAgLSBtZWFuVmFsdWUgKiBpbnY7XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoeFRpbWVzSW52ICsgbWVhblRpbWVzSW52V2l0aE9mZnNldCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiYXRjaE5vcm1hbGl6YXRpb24oXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgeFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBtZWFuOiBXZWJHTFRleHR1cmUsXG4gICAgbWVhblNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCB2YXJpYW5jZTogV2ViR0xUZXh0dXJlLFxuICAgIHZhcmlhbmNlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sIG9mZnNldDogV2ViR0xUZXh0dXJlfG51bGwsXG4gICAgb2Zmc2V0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl18bnVsbCwgc2NhbGU6IFdlYkdMVGV4dHVyZXxudWxsLFxuICAgIHNjYWxlU2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl18bnVsbCwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShtZWFuLCAnbWVhbicsIDEpO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUodmFyaWFuY2UsICd2YXJpYW5jZScsIDIpO1xuICBsZXQgbmV4dEluZGV4ID0gMztcbiAgaWYgKG9mZnNldCAhPSBudWxsKSB7XG4gICAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKG9mZnNldCwgJ29mZnNldCcsIG5leHRJbmRleCk7XG4gICAgbmV4dEluZGV4Kys7XG4gIH1cbiAgaWYgKHNjYWxlICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc2NhbGUsICdzY2FsZScsIG5leHRJbmRleCk7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgYVJlc3VsdFVWOiBzdHJpbmcsIGJSZXN1bHRVVjogc3RyaW5nLCBvcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhCO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGEgPSB0ZXh0dXJlMkQobWF0cml4QSwgJHthUmVzdWx0VVZ9KS5yO1xuICAgICAgZmxvYXQgYiA9IHRleHR1cmUyRChtYXRyaXhCLCAke2JSZXN1bHRVVn0pLnI7XG4gICAgICAke29wfVxuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluYXJ5T3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgYVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBXZWJHTFRleHR1cmUsXG4gICAgYlNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRCaW5hcnlPcERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgYVNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBiOiBGbG9hdDMyQXJyYXksXG4gICAgYlNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCBmcmFnbWVudFNoYWRlclNvdXJjZTogc3RyaW5nKTogRmxvYXQzMkFycmF5IHtcbiAgY29uc3QgZ3BncHUgPSBuZXcgR1BHUFVDb250ZXh0KCk7XG4gIGNvbnN0IHByb2dyYW0gPSBncGdwdS5jcmVhdGVQcm9ncmFtKGZyYWdtZW50U2hhZGVyU291cmNlKTtcblxuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoYVNoYXBlWzBdLCBhU2hhcGVbMV0pO1xuICBjb25zdCBiVGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoYlNoYXBlWzBdLCBiU2hhcGVbMV0pO1xuXG4gIGNvbnN0IHJlc3VsdFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgIFtNYXRoLm1heChhU2hhcGVbMF0sIGJTaGFwZVswXSksIE1hdGgubWF4KGFTaGFwZVsxXSwgYlNoYXBlWzFdKV07XG5cbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID1cbiAgICAgIGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUocmVzdWx0U2hhcGVbMF0sIHJlc3VsdFNoYXBlWzFdKTtcblxuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIGFTaGFwZVswXSwgYVNoYXBlWzFdLCBhKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGJUZXh0dXJlLCBiU2hhcGVbMF0sIGJTaGFwZVsxXSwgYik7XG5cbiAgYmluYXJ5T3AoXG4gICAgICBncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIGFTaGFwZSwgYlRleHR1cmUsIGJTaGFwZSwgcmVzdWx0VGV4dHVyZSxcbiAgICAgIHJlc3VsdFNoYXBlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShcbiAgICAgIHJlc3VsdFRleHR1cmUsIHJlc3VsdFNoYXBlWzBdLCByZXN1bHRTaGFwZVsxXSk7XG5cbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUoYlRleHR1cmUpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHJlc3VsdFRleHR1cmUpO1xuICBncGdwdS5kZWxldGVQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5kaXNwb3NlKCk7XG4gIHJldHVybiByZXN1bHQ7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCAqIGFzIGNvbnZfdXRpbCBmcm9tICcuLi9jb252X3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICB4MVNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHgyU2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSxcbiAgICByZXN1bHRTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBheGlzOiBudW1iZXIpOiBzdHJpbmcge1xuICBjb25zdCB4MVRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHgxU2hhcGVSQ0QpO1xuICBjb25zdCB4MlRleFNoYXBlUkMgPSBjb252X3V0aWwuY29tcHV0ZVRleFNoYXBlRnJvbTNEKHgyU2hhcGVSQ0QpO1xuXG4gIGNvbnN0IHlBeGVzID0gWyd5UicsICd5QycsICd5RCddO1xuICBjb25zdCBjb25jYXRBeGlzID0geUF4ZXNbYXhpc107XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDE7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDI7XG5cbiAgICBjb25zdCB2ZWMyIHgxU2hhcGVDUiA9IHZlYzIoJHt4MVRleFNoYXBlUkNbMV19LCAke3gxVGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgeDJTaGFwZUNSID0gdmVjMigke3gyVGV4U2hhcGVSQ1sxXX0uMCwgJHt4MlRleFNoYXBlUkNbMF19LjApO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgeUQpLlxuICAgICAgZmxvYXQgeVIgPSB5VGV4Q1IueTtcbiAgICAgIGZsb2F0IHlDID0gZmxvb3IoeVRleENSLnggLyAke3Jlc3VsdFNoYXBlUkNEWzJdfS4wKTtcbiAgICAgIGZsb2F0IHlEID0gbW9kKHlUZXhDUi54LCAke3Jlc3VsdFNoYXBlUkNEWzJdfS4wKTtcblxuICAgICAgZmxvYXQgdmFsdWUgPSAwLjA7XG5cbiAgICAgIGlmICgke2NvbmNhdEF4aXN9IDwgJHt4MVNoYXBlUkNEW2F4aXNdfS4wKSB7XG4gICAgICAgIC8vIE1hcCB5UiwgeUMsIHlEIGJhY2sgdG8geDEgY29vcmRpbmF0ZXMuXG4gICAgICAgIHZlYzIgeDFDUiA9IHZlYzIoeUMgKiAke3gxU2hhcGVSQ0RbMl19LjAgKyB5RCwgeVIpO1xuICAgICAgICB2ZWMyIHgxVVYgPSAoeDFDUiArIGhhbGZDUikgLyB4MVNoYXBlQ1I7XG4gICAgICAgIHZhbHVlID0gdGV4dHVyZTJEKHgxLCB4MVVWKS5yO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgJHtjb25jYXRBeGlzfSA9ICR7Y29uY2F0QXhpc30gLSAke3gxU2hhcGVSQ0RbYXhpc119LjA7XG5cbiAgICAgICAgLy8gTWFwIHlSLCB5QywgeUQgYmFjayB0byB4MiBjb29yZGluYXRlcy5cbiAgICAgICAgdmVjMiB4MkNSID0gdmVjMih5QyAqICR7eDJTaGFwZVJDRFsyXX0uMCArIHlELCB5Uik7XG4gICAgICAgIHZlYzIgeDJVViA9ICh4MkNSICsgaGFsZkNSKSAvIHgyU2hhcGVDUjtcbiAgICAgICAgdmFsdWUgPSB0ZXh0dXJlMkQoeDIsIHgyVVYpLnI7XG4gICAgICB9XG5cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsdWUsIDAuMCwgMC4wLCAwLjApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29uY2F0M0QoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4MTogV2ViR0xUZXh0dXJlLFxuICAgIHgyOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByZXN1bHRTaGFwZVJDWzBdLCByZXN1bHRTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgxLCAneDEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgyLCAneDInLCAxKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5cbmltcG9ydCAqIGFzIGNvbnZfZ3B1IGZyb20gJy4vY29udl9ncHUnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckRlcldlaWdodHNTb3VyY2UoXG4gICAgeFNoYXBlUm93Q29sRGVwdGg6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlcixcbiAgICBvdXRwdXREZXB0aDogbnVtYmVyLCBzdHJpZGU6IG51bWJlciwgemVyb1BhZDogbnVtYmVyKSB7XG4gIGNvbnN0IGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkID1cbiAgICAgIGNvbnZfZ3B1LmdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTtcbiAgY29uc3QgaW5wdXREZXB0aCA9IHhTaGFwZVJvd0NvbERlcHRoWzJdO1xuXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSb3dDb2xEZXB0aCk7XG5cbiAgY29uc3QgeVNoYXBlID0gY29udl91dGlsLmNvbXB1dGVPdXRwdXRTaGFwZTNEKFxuICAgICAgeFNoYXBlUm93Q29sRGVwdGgsIGZTaXplLCBvdXRwdXREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkKTtcbiAgY29uc3QgeU51bVJvd3MgPSB5U2hhcGVbMF07XG4gIGNvbnN0IHlOdW1Db2xzID0geVNoYXBlWzFdO1xuICBjb25zdCB5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeVNoYXBlKTtcblxuICBjb25zdCBmU2l6ZVRpbWVzSW5wdXREZXB0aCA9IGZTaXplICogaW5wdXREZXB0aDtcblxuICBjb25zdCBwcm9sb2d1ZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBkeTtcbiAgYDtcblxuICByZXR1cm4gcHJvbG9ndWUgKyAnXFxuJyArIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkICsgJ1xcbicgK1xuICAgICAgYFxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHt5VGV4U2hhcGVSQ1sxXX0sICR7eVRleFNoYXBlUkNbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgd1RleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHdUZXhSLCB3VGV4QykgdG8gNEQgKHdSLCB3QywgZDEsIGQyKS5cbiAgICAgIGZsb2F0IHdSID0gZmxvb3Iod1RleENSLnkgLyAke2ZTaXplVGltZXNJbnB1dERlcHRofS4wKTtcbiAgICAgIGZsb2F0IHdUZXhSTGVmdG92ZXIgPSB3VGV4Q1IueSAtIHdSICogJHtmU2l6ZVRpbWVzSW5wdXREZXB0aH0uMDtcbiAgICAgIGZsb2F0IHdDID0gZmxvb3Iod1RleFJMZWZ0b3ZlciAvICR7aW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMSA9IG1vZCh3VGV4UkxlZnRvdmVyLCAke2lucHV0RGVwdGh9LjApO1xuICAgICAgZmxvYXQgZDIgPSB3VGV4Q1IueDtcblxuICAgICAgLy8gQ29udm9sdmUgeCg/LCA/LCBkMSkgd2l0aCBkeSg6LCA6LCBkMikgdG8gZ2V0IGR3KHdSLCB3QywgZDEsIGQyKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgeVIgPSAwLjA7IHlSIDwgJHt5TnVtUm93c30uMDsgeVIgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IHhSID0gd1IgKyB5UiAqICR7c3RyaWRlfS4wIC0gJHt6ZXJvUGFkfS4wO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuICAgICAgICBmbG9hdCB5VGV4UiA9IHlSO1xuICAgICAgICBmb3IgKGZsb2F0IHlDID0gMC4wOyB5QyA8ICR7eU51bUNvbHN9LjA7IHlDICs9IDEuMCkge1xuICAgICAgICAgIGZsb2F0IHhDID0gd0MgKyB5QyAqICR7c3RyaWRlfS4wIC0gJHt6ZXJvUGFkfS4wO1xuXG4gICAgICAgICAgLy8gTWFwIGZyb20gM0QgKHhSLCB4QywgZDEpIHRvIDJEICh4VGV4UiwgeFRleEMpLlxuICAgICAgICAgIC8vIE1hcCBmcm9tIDNEICh5UiwgeUMsIGQyKSB0byAyRCAoeVRleFIsIHlUZXhDKS5cbiAgICAgICAgICB2ZWMyIHh5VGV4QyA9IHZlYzIoeEMsIHlDKSAqIHZlYzIoJHtpbnB1dERlcHRofS4wLCAke291dHB1dERlcHRofS4wKSArXG4gICAgICAgICAgICAgICAgICAgICAgICB2ZWMyKGQxLCBkMik7XG4gICAgICAgICAgZmxvYXQgeFRleEMgPSB4eVRleEMueDtcbiAgICAgICAgICBmbG9hdCB5VGV4QyA9IHh5VGV4Qy55O1xuXG4gICAgICAgICAgLy8gUmVhZCBkeSh5UiwgeUMsIGQyKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMih5VGV4QywgeVRleFIpICsgaGFsZkNSKSAvIGR5U2hhcGVDUjtcbiAgICAgICAgICBmbG9hdCBkeVZhbHVlID0gdGV4dHVyZTJEKGR5LCBkeVVWKS5yO1xuXG4gICAgICAgICAgLy8gUmVhZCB4KHhSLCB4QywgZDEpIChwb3RlbnRpYWxseSB6ZXJvLXBhZGRlZCkuXG4gICAgICAgICAgZmxvYXQgeFZhbHVlID1cbiAgICAgICAgICAgIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKHgsIHhTaGFwZUNSLCB2ZWMyKHhUZXhDLCB4VGV4UikpO1xuXG4gICAgICAgICAgZG90UHJvZCArPSAoeFZhbHVlICogZHlWYWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckNvbnZUcmFuc3Bvc2VTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIG9yaWdJbnB1dERlcHRoOiBudW1iZXIsXG4gICAgb3JpZ1N0cmlkZTogbnVtYmVyLCBvcmlnUGFkOiBudW1iZXIsIGhhc0JpYXM6IGJvb2xlYW4pIHtcbiAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgY29uc3QgW3hSb3dzLCB4Q29scywgb3JpZ091dHB1dERlcHRoXSA9IHhTaGFwZVJDRDtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcbiAgY29uc3Qgd1RleFNoYXBlUkMgPVxuICAgICAgY29udl91dGlsLmNvbXB1dGVXZWlnaHRzVGV4U2hhcGUob3JpZ0lucHV0RGVwdGgsIG9yaWdPdXRwdXREZXB0aCwgZlNpemUpO1xuXG4gIGNvbnN0IGdldEJpYXNWYWx1ZSA9IGhhc0JpYXMgP1xuICAgICAgY29udl9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJHZXRCaWFzVmFsdWVTb3VyY2Uob3JpZ0lucHV0RGVwdGgpIDpcbiAgICAgICcnO1xuICBjb25zdCBiaWFzUHJvbG9ndWUgPSBoYXNCaWFzID8gJ3VuaWZvcm0gc2FtcGxlcjJEIGJpYXNlczsnIDogJyc7XG4gIGNvbnN0IGJpYXNPcGVyYXRpb24gPSBoYXNCaWFzID8gJ2RvdFByb2QgKz0gZ2V0Qmlhc1ZhbHVlKGJpYXNlcywgZDIpOycgOiAnJztcblxuICBjb25zdCBwcm9sb2d1ZSA9IGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB3ZWlnaHRzO1xuICAgICR7Ymlhc1Byb2xvZ3VlfVxuICAgIGA7XG5cbiAgcmV0dXJuIHByb2xvZ3VlICsgJ1xcbicgKyBnZXRCaWFzVmFsdWUgKyAnXFxuJyArXG4gICAgICBgXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIHhTaGFwZUNSID0gdmVjMigke3hUZXhTaGFwZVJDWzFdfSwgJHt4VGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgd1NoYXBlQ1IgPSB2ZWMyKCR7d1RleFNoYXBlUkNbMV19LCAke3dUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQyKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtvcmlnSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMiA9IG1vZCh5VGV4Q1IueCwgJHtvcmlnSW5wdXREZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgeFJDQ29ybmVyID0gdmVjMih5UiwgeUMpIC0gdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgeFJDb3JuZXIgPSB4UkNDb3JuZXIueDtcbiAgICAgIGZsb2F0IHhDQ29ybmVyID0geFJDQ29ybmVyLnk7XG5cbiAgICAgIC8vIENvbnZvbHZlIHgoPywgPywgZDEpIHdpdGggdyg6LCA6LCBkMiwgZDEpIHRvIGdldCB5KHlSLCB5QywgZDIpLlxuICAgICAgLy8gPyA9IHRvIGJlIGRldGVybWluZWQuIDogPSBhY3Jvc3MgYWxsIHZhbHVlcyBpbiB0aGF0IGF4aXMuXG4gICAgICBmbG9hdCBkb3RQcm9kID0gMC4wO1xuICAgICAgZm9yIChmbG9hdCB3UiA9IDAuMDsgd1IgPCAke2ZTaXplfS4wOyB3UiArPSAxLjApIHtcblxuICAgICAgICBmbG9hdCB4UiA9ICh4UkNvcm5lciArIHdSKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgLy8gVE9ETyhzbWlsa292KTogU3BsaWNlIHRoaXMgd2l0aCBhbm90aGVyIHZlcnNpb24gd2hlcmUgeW91IGNhbGxcbiAgICAgICAgLy8gZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoKS4gSGVyZSBhbmQgYmVsb3cuXG4gICAgICAgIGlmICh4UiA8IDAuMCB8fCB4UiA+PSAke3hSb3dzfS4wIHx8IGZyYWN0KHhSKSA+IDAuMCkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgZmxvYXQgd1JQZXJtID0gJHtmU2l6ZX0uMCAtIDEuMCAtIHdSO1xuICAgICAgICBmbG9hdCB4VGV4UiA9IHhSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgd0MgPSAwLjA7IHdDIDwgJHtmU2l6ZX0uMDsgd0MgKz0gMS4wKSB7XG5cbiAgICAgICAgICBmbG9hdCB4QyA9ICh4Q0Nvcm5lciArIHdDKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgICBpZiAoeEMgPCAwLjAgfHwgeEMgPj0gJHt4Q29sc30uMCB8fCBmcmFjdCh4QykgPiAwLjApIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGZsb2F0IHdDUGVybSA9ICR7ZlNpemV9LjAgLSAxLjAgLSB3QztcbiAgICAgICAgICBmbG9hdCB3VGV4UiA9IHdSUGVybSAqICR7ZlNpemV9LjAgKiAke29yaWdJbnB1dERlcHRofS4wICtcbiAgICAgICAgICAgICAgICAgICAgICAgIHdDUGVybSAqICR7b3JpZ0lucHV0RGVwdGh9LjAgKyBkMjtcblxuICAgICAgICAgIGZvciAoZmxvYXQgZDEgPSAwLjA7IGQxIDwgJHtvcmlnT3V0cHV0RGVwdGh9LjA7IGQxICs9IDEuMCkge1xuICAgICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7b3JpZ091dHB1dERlcHRofS4wICsgZDE7XG4gICAgICAgICAgICBmbG9hdCB3VGV4QyA9IGQxO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHgoeFIsIHhDLCBkMSkuXG4gICAgICAgICAgICB2ZWMyIHhVViA9ICh2ZWMyKHhUZXhDLCB4VGV4UikgKyBoYWxmQ1IpIC8geFNoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB4VmFsdWUgPSB0ZXh0dXJlMkQoeCwgeFVWKS5yO1xuXG4gICAgICAgICAgICAvLyBSZWFkIHcod1JQZXJtLCB3Q1Blcm0sIGQyLCBkMSkuXG4gICAgICAgICAgICB2ZWMyIHdVViA9ICh2ZWMyKHdUZXhDLCB3VGV4UikgKyBoYWxmQ1IpIC8gd1NoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB3VmFsdWUgPSB0ZXh0dXJlMkQod2VpZ2h0cywgd1VWKS5yO1xuXG4gICAgICAgICAgICBkb3RQcm9kICs9IHhWYWx1ZSAqIHdWYWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgICR7Ymlhc09wZXJhdGlvbn1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZG90UHJvZCwgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlckRlckJpYXNTb3VyY2UoXG4gICAgZHlTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdKSB7XG4gIGNvbnN0IGR5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZHlTaGFwZVJDRCk7XG4gIGNvbnN0IFt5TnVtUm93cywgeU51bUNvbHMsIG91dHB1dERlcHRoXSA9IGR5U2hhcGVSQ0Q7XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG5cbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuICAgIGNvbnN0IHZlYzIgZHlTaGFwZUNSID0gdmVjMigke2R5VGV4U2hhcGVSQ1sxXX0sICR7ZHlUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIGJpYXNUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIFRoZSBiaWFzIHRleHR1cmUgUkMgc2hhcGUgaXMgWzEsIGQyXS5cbiAgICAgIGZsb2F0IGQyID0gYmlhc1RleENSLng7XG5cbiAgICAgIGZsb2F0IGRlckJpYXMgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHlSID0gMC4wOyB5UiA8ICR7eU51bVJvd3N9LjA7IHlSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB5VGV4UiA9IHlSO1xuXG4gICAgICAgIGZvciAoZmxvYXQgeUMgPSAwLjA7IHlDIDwgJHt5TnVtQ29sc30uMDsgeUMgKz0gMS4wKSB7XG4gICAgICAgICAgLy8gTWFwIGZyb20gM0QgKHlSLCB5QywgZDIpIHRvIDJEICh5VGV4UiwgeVRleEMpLlxuICAgICAgICAgIGZsb2F0IHlUZXhDID0geUMgKiAke291dHB1dERlcHRofS4wICsgZDI7XG5cbiAgICAgICAgICAvLyBSZWFkIGR5KHlSLCB5QywgZDIpLlxuICAgICAgICAgIHZlYzIgZHlVViA9ICh2ZWMyKHlUZXhDLCB5VGV4UikgKyBoYWxmQ1IpIC8gZHlTaGFwZUNSO1xuICAgICAgICAgIGZsb2F0IGR5VmFsdWUgPSB0ZXh0dXJlMkQoZHksIGR5VVYpLnI7XG5cbiAgICAgICAgICBkZXJCaWFzICs9IGR5VmFsdWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoZGVyQmlhcywgMCwgMCwgMCk7XG4gICAgfWA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZXJCaWFzKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgZHlUZXg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGR5VGV4LCAnZHknLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlcldlaWdodHMoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgZHlUZXg6IFdlYkdMVGV4dHVyZSwgcmVzdWx0OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHhUZXgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjb252VHJhbnNwb3NlKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeFRleDogV2ViR0xUZXh0dXJlLFxuICAgIHdlaWdodHNUZXg6IFdlYkdMVGV4dHVyZSwgYmlhc2VzVGV4OiBXZWJHTFRleHR1cmV8bnVsbCxcbiAgICByZXN1bHRUZXg6IFdlYkdMVGV4dHVyZSwgcmVzdWx0VGV4U2hhcGVSQzogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0VGV4LCByZXN1bHRUZXhTaGFwZVJDWzBdLCByZXN1bHRUZXhTaGFwZVJDWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHhUZXgsICd4JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZSh3ZWlnaHRzVGV4LCAnd2VpZ2h0cycsIDEpO1xuICBpZiAoYmlhc2VzVGV4ICE9IG51bGwpIHtcbiAgICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYmlhc2VzVGV4LCAnYmlhc2VzJywgMik7XG4gIH1cbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgeDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB3ZWlnaHRzO1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIGJpYXNlcztcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyR2V0TWF0cml4VmFsdWVPclplcm9QYWRTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCBnZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZChpbiBzYW1wbGVyMkQgbWF0cml4LCB2ZWMyIG1hdHJpeFNoYXBlQ1IsXG4gICAgICAgIHZlYzIgcmVxdWVzdGVkQ1IpIHtcbiAgICAgIHZlYzIgdXYgPSAocmVxdWVzdGVkQ1IgKyB2ZWMyKDAuNSwgMC41KSkgLyBtYXRyaXhTaGFwZUNSO1xuICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQobWF0cml4LCB1dikucjtcbiAgICAgIGJvb2wgbGVzc1RoYW5aZXJvID0gYW55KGxlc3NUaGFuKHV2LCB2ZWMyKDAsIDApKSk7XG4gICAgICBib29sIGdyZWF0ZXJUaGFuT25lID0gYW55KGdyZWF0ZXJUaGFuKHV2LCB2ZWMyKDEsIDEpKSk7XG4gICAgICBib29sIG91dHNpZGUgPSBsZXNzVGhhblplcm8gfHwgZ3JlYXRlclRoYW5PbmU7XG4gICAgICByZXR1cm4gbWl4KHZhbHVlLCAwLjAsIGZsb2F0KG91dHNpZGUpKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyQ29udm9sdmVTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIG91dHB1dERlcHRoOiBudW1iZXIsXG4gICAgc3RyaWRlOiBudW1iZXIsIHBhZDogbnVtYmVyLCBoYXNCaWFzOiBib29sZWFuKSB7XG4gIGNvbnN0IFt4Um93cywgeENvbHMsIGlucHV0RGVwdGhdID0geFNoYXBlUkNEO1xuXG4gIGNvbnN0IHhUZXhTaGFwZVJDID0gY29udl91dGlsLmNvbXB1dGVUZXhTaGFwZUZyb20zRCh4U2hhcGVSQ0QpO1xuICBjb25zdCB3VGV4U2hhcGVSQyA9XG4gICAgICBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShpbnB1dERlcHRoLCBvdXRwdXREZXB0aCwgZlNpemUpO1xuXG4gIHJldHVybiBgXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIHhTaGFwZUNSID0gdmVjMigke3hUZXhTaGFwZVJDWzFdfSwgJHt4VGV4U2hhcGVSQ1swXX0pO1xuICAgIGNvbnN0IHZlYzIgd1NoYXBlQ1IgPSB2ZWMyKCR7d1RleFNoYXBlUkNbMV19LCAke3dUZXhTaGFwZVJDWzBdfSk7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICB2ZWMyIHlUZXhDUiA9IGZsb29yKGdsX0ZyYWdDb29yZC54eSk7XG5cbiAgICAgIC8vIE1hcCBmcm9tIDJEICh5VGV4UiwgeVRleEMpIHRvIDNEICh5UiwgeUMsIGQyKS5cbiAgICAgIGZsb2F0IHlSID0geVRleENSLnk7XG4gICAgICBmbG9hdCB5QyA9IGZsb29yKHlUZXhDUi54IC8gJHtvdXRwdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkMiA9IG1vZCh5VGV4Q1IueCwgJHtvdXRwdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCB3VGV4QyA9IGQyO1xuXG4gICAgICB2ZWMyIHhSQ0Nvcm5lciA9IHZlYzIoeVIsIHlDKSAqIHZlYzIoJHtzdHJpZGV9LCAke3N0cmlkZX0pIC1cbiAgICAgICAgICB2ZWMyKCR7cGFkfS4wLCAke3BhZH0uMCk7XG4gICAgICBmbG9hdCB4UkNvcm5lciA9IHhSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgeENDb3JuZXIgPSB4UkNDb3JuZXIueTtcblxuICAgICAgLy8gQ29udm9sdmUgeCg/LCA/LCBkMSkgd2l0aCB3KDosIDosIGQxLCBkMikgdG8gZ2V0IHkoeVIsIHlDLCBkMikuXG4gICAgICAvLyA/ID0gdG8gYmUgZGV0ZXJtaW5lZC4gOiA9IGFjcm9zcyBhbGwgdmFsdWVzIGluIHRoYXQgYXhpcy5cbiAgICAgIGZsb2F0IGRvdFByb2QgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHhSQ29ybmVyICsgd1I7XG4gICAgICAgIGZsb2F0IHhUZXhSID0geFI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHhDQ29ybmVyICsgd0M7XG5cbiAgICAgICAgICBmb3IgKGZsb2F0IGQxID0gMC4wOyBkMSA8ICR7aW5wdXREZXB0aH0uMDsgZDEgKz0gMS4wKSB7XG4gICAgICAgICAgICBmbG9hdCB4VGV4QyA9IHhDICogJHtpbnB1dERlcHRofS4wICsgZDE7XG4gICAgICAgICAgICBmbG9hdCB3VGV4UiA9IHdSICogJHtmU2l6ZSAqIGlucHV0RGVwdGh9LjAgK1xuICAgICAgICAgICAgICAgIHdDICogJHtpbnB1dERlcHRofS4wICsgZDE7XG5cbiAgICAgICAgICAgIGZsb2F0IHhWYWx1ZSA9XG4gICAgICAgICAgICAgICAgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQoeCwgeFNoYXBlQ1IsIHZlYzIoeFRleEMsIHhUZXhSKSk7XG5cbiAgICAgICAgICAgIC8vIFJlYWQgdyh3Uiwgd0MsIGQxLCBkMikuXG4gICAgICAgICAgICB2ZWMyIHdVViA9ICh2ZWMyKHdUZXhDLCB3VGV4UikgKyBoYWxmQ1IpIC8gd1NoYXBlQ1I7XG4gICAgICAgICAgICBmbG9hdCB3VmFsdWUgPSB0ZXh0dXJlMkQod2VpZ2h0cywgd1VWKS5yO1xuXG4gICAgICAgICAgICBkb3RQcm9kICs9IHhWYWx1ZSAqIHdWYWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICgke2hhc0JpYXN9KSB7XG4gICAgICAgIGRvdFByb2QgKz0gZ2V0Qmlhc1ZhbHVlKGJpYXNlcywgZDIpO1xuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChkb3RQcm9kLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyR2V0Qmlhc1ZhbHVlU291cmNlKG91dHB1dERlcHRoOiBudW1iZXIpOlxuICAgIHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgZmxvYXQgZ2V0Qmlhc1ZhbHVlKGluIHNhbXBsZXIyRCBiaWFzLCBmbG9hdCBiaWFzQykge1xuICAgICAgY29uc3QgdmVjMiBiaWFzU2hhcGVDUiA9IHZlYzIoJHtvdXRwdXREZXB0aH0sIDEpO1xuICAgICAgdmVjMiBiaWFzQ1IgPSB2ZWMyKG1vZChiaWFzQywgJHtvdXRwdXREZXB0aH0uMCksIDApO1xuICAgICAgdmVjMiBiaWFzVVYgPSAoYmlhc0NSICsgdmVjMigwLjUsIDAuNSkpIC8gYmlhc1NoYXBlQ1I7XG4gICAgICByZXR1cm4gdGV4dHVyZTJEKGJpYXMsIGJpYXNVVikucjtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIGFTaGFwZVJvd0NvbERlcHRoOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHJlc3VsdERlcHRoOiBudW1iZXIsXG4gICAgZmllbGRTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLCB6ZXJvUGFkOiBudW1iZXIsXG4gICAgaGFzQmlhczogYm9vbGVhbik6IHN0cmluZyB7XG4gIGNvbnN0IGFTaGFwZVJDOiBbbnVtYmVyLCBudW1iZXJdID1cbiAgICAgIGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoYVNoYXBlUm93Q29sRGVwdGgpO1xuXG4gIGNvbnN0IHdlaWdodFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0gPSBjb252X3V0aWwuY29tcHV0ZVdlaWdodHNUZXhTaGFwZShcbiAgICAgIGFTaGFwZVJvd0NvbERlcHRoWzJdLCByZXN1bHREZXB0aCwgZmllbGRTaXplKTtcblxuICBjb25zdCBwcm9sb2d1ZSA9IGdldEZyYWdtZW50U2hhZGVyUHJvbG9ndWVTb3VyY2UoKTtcbiAgY29uc3QgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQgPVxuICAgICAgZ2V0RnJhZ21lbnRTaGFkZXJHZXRNYXRyaXhWYWx1ZU9yWmVyb1BhZFNvdXJjZSgpO1xuICBjb25zdCBjb252b2x2ZSA9IGdldEZyYWdtZW50U2hhZGVyQ29udm9sdmVTb3VyY2UoXG4gICAgICBhU2hhcGVSb3dDb2xEZXB0aCwgZmllbGRTaXplLCByZXN1bHREZXB0aCwgc3RyaWRlLCB6ZXJvUGFkLCBoYXNCaWFzKTtcbiAgY29uc3QgZ2V0Qmlhc1ZhbHVlID0gZ2V0RnJhZ21lbnRTaGFkZXJHZXRCaWFzVmFsdWVTb3VyY2UocmVzdWx0RGVwdGgpO1xuXG4gIHJldHVybiBbXG4gICAgcHJvbG9ndWUsXG4gICAgZ2V0TWF0cml4VmFsdWVPclplcm9QYWQsXG4gICAgZ2V0Qmlhc1ZhbHVlLFxuICAgIGNvbnZvbHZlLFxuICBdLmpvaW4oJ1xcbicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29udm9sdmUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgd2VpZ2h0czogV2ViR0xUZXh0dXJlLCBiaWFzZXM6IFdlYkdMVGV4dHVyZXxudWxsLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKFxuICAgICAgcmVzdWx0LCByZXN1bHRTaGFwZVJvd0NvbFswXSwgcmVzdWx0U2hhcGVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ3gnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHdlaWdodHMsICd3ZWlnaHRzJywgMSk7XG4gIGlmIChiaWFzZXMgIT0gbnVsbCkge1xuICAgIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShiaWFzZXMsICdiaWFzZXMnLCAyKTtcbiAgfVxuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBzb3VyY2VTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSwgc291cmNlU2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBkZXN0U2l6ZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSk6IHN0cmluZyB7XG4gIHJldHVybiBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIHVuaWZvcm0gc2FtcGxlcjJEIHNvdXJjZTtcbiAgICB1bmlmb3JtIHZlYzIgc291cmNlU3RhcnRDUjtcbiAgICB1bmlmb3JtIHZlYzIgZGVzdFN0YXJ0Q1I7XG5cbiAgICBjb25zdCB2ZWMyIHNvdXJjZVNoYXBlQ1IgPVxuICAgICAgdmVjMigke3NvdXJjZVNoYXBlUm93Q29sWzFdfSwgJHtzb3VyY2VTaGFwZVJvd0NvbFswXX0pO1xuICAgIGNvbnN0IHZlYzIgc291cmNlU2l6ZUNSID1cbiAgICAgIHZlYzIoJHtzb3VyY2VTaXplUm93Q29sWzFdfSwgJHtzb3VyY2VTaXplUm93Q29sWzBdfSk7XG4gICAgY29uc3QgdmVjMiBkZXN0U2l6ZUNSID1cbiAgICAgIHZlYzIoJHtkZXN0U2l6ZVJvd0NvbFsxXX0sICR7ZGVzdFNpemVSb3dDb2xbMF19KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgZGVzdE9mZnNldENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KSAtIGRlc3RTdGFydENSO1xuICAgICAgZmxvYXQgZGVzdE9mZnNldEZsYXQgPSAoZGVzdE9mZnNldENSLnkgKiBkZXN0U2l6ZUNSLngpICsgZGVzdE9mZnNldENSLng7XG4gICAgICB2ZWMyIHNvdXJjZU9mZnNldENSID0gdmVjMihtb2QoZGVzdE9mZnNldEZsYXQsIHNvdXJjZVNpemVDUi54KSxcbiAgICAgICAgZmxvb3IoZGVzdE9mZnNldEZsYXQgLyBzb3VyY2VTaXplQ1IueCkpO1xuICAgICAgdmVjMiBzb3VyY2VDUiA9IHNvdXJjZVN0YXJ0Q1IgKyBzb3VyY2VPZmZzZXRDUjtcbiAgICAgIHZlYzIgc291cmNlVVYgPSAoc291cmNlQ1IgKyB2ZWMyKDAuNSwgMC41KSkgLyBzb3VyY2VTaGFwZUNSO1xuICAgICAgZ2xfRnJhZ0NvbG9yID0gdGV4dHVyZTJEKHNvdXJjZSwgc291cmNlVVYpO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY29weShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHNvdXJjZTogV2ViR0xUZXh0dXJlLFxuICAgIHNvdXJjZVNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBzb3VyY2VTdGFydFJvd0NvbDogW251bWJlciwgbnVtYmVyXSxcbiAgICBzb3VyY2VTaXplUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0OiBXZWJHTFRleHR1cmUsXG4gICAgZGVzdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdLCBkZXN0U3RhcnRSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0sXG4gICAgZGVzdFNpemVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShkZXN0LCBkZXN0U2hhcGVSb3dDb2xbMF0sIGRlc3RTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgZGVzdFN0YXJ0Um93Q29sWzBdLCBkZXN0U2l6ZVJvd0NvbFswXSwgZGVzdFN0YXJ0Um93Q29sWzFdLFxuICAgICAgZGVzdFNpemVSb3dDb2xbMV0pO1xuICBncGdwdS5zZXRQcm9ncmFtKHByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoc291cmNlLCAnc291cmNlJywgMCk7XG4gIGNvbnN0IHNvdXJjZVN0YXJ0Q1JMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ3NvdXJjZVN0YXJ0Q1InKTtcbiAgZ3BncHUuZ2wudW5pZm9ybTJmKFxuICAgICAgc291cmNlU3RhcnRDUkxvYywgc291cmNlU3RhcnRSb3dDb2xbMV0sIHNvdXJjZVN0YXJ0Um93Q29sWzBdKTtcbiAgY29uc3QgZGVzdFN0YXJ0Q1JMb2MgPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ2Rlc3RTdGFydENSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihkZXN0U3RhcnRDUkxvYywgZGVzdFN0YXJ0Um93Q29sWzFdLCBkZXN0U3RhcnRSb3dDb2xbMF0pO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG5mdW5jdGlvbiBnZXRFeHBVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNChleHAodmFsdWUpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldEV4cFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleHAoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgZXhwUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIGV4cFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRFeHBEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChhLCByb3dzLCBjb2x1bW5zLCBnZXRFeHBVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBncGdwdV91dGlsIGZyb20gJy4vZ3BncHVfdXRpbCc7XG5pbXBvcnQgKiBhcyB0ZXhfdXRpbCBmcm9tICcuL3RleF91dGlsJztcbmltcG9ydCAqIGFzIHdlYmdsX3V0aWwgZnJvbSAnLi93ZWJnbF91dGlsJztcblxuaW1wb3J0IHtXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9ufSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgY2xhc3MgR1BHUFVDb250ZXh0IHtcbiAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgdGV4dHVyZUZsb2F0RXh0ZW5zaW9uOiB7fTtcbiAgY29sb3JCdWZmZXJGbG9hdEV4dGVuc2lvbjoge307XG4gIGxvc2VDb250ZXh0RXh0ZW5zaW9uOiBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICB2ZXJ0ZXhCdWZmZXI6IFdlYkdMQnVmZmVyO1xuICBpbmRleEJ1ZmZlcjogV2ViR0xCdWZmZXI7XG4gIGZyYW1lYnVmZmVyOiBXZWJHTEZyYW1lYnVmZmVyO1xuICBvdXRwdXRUZXh0dXJlOiBXZWJHTFRleHR1cmV8bnVsbCA9IG51bGw7XG4gIHByb2dyYW06IFdlYkdMUHJvZ3JhbXxudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBkaXNwb3NlZCA9IGZhbHNlO1xuICBwcml2YXRlIGF1dG9EZWJ1Z1ZhbGlkYXRlID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoZ2w/OiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgICBpZiAoZ2wgIT0gbnVsbCkge1xuICAgICAgdGhpcy5nbCA9IGdsO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmdsID0gZ3BncHVfdXRpbC5jcmVhdGVXZWJHTENvbnRleHQoKTtcbiAgICB9XG5cbiAgICAvLyBXZWJHTCAyLjAgZW5hYmxlcyB0ZXh0dXJlIGZsb2F0cyB3aXRob3V0IGFuIGV4dGVuc2lvbi5cbiAgICBpZiAoIXdlYmdsX3V0aWwuaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICAgIHRoaXMudGV4dHVyZUZsb2F0RXh0ZW5zaW9uID1cbiAgICAgICAgICB3ZWJnbF91dGlsLmdldEV4dGVuc2lvbk9yVGhyb3codGhpcy5nbCwgJ09FU190ZXh0dXJlX2Zsb2F0Jyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY29sb3JCdWZmZXJGbG9hdEV4dGVuc2lvbiA9XG4gICAgICAgICAgd2ViZ2xfdXRpbC5nZXRFeHRlbnNpb25PclRocm93KHRoaXMuZ2wsICdFWFRfY29sb3JfYnVmZmVyX2Zsb2F0Jyk7XG4gICAgfVxuXG4gICAgdGhpcy5sb3NlQ29udGV4dEV4dGVuc2lvbiA9XG4gICAgICAgIHdlYmdsX3V0aWwuZ2V0RXh0ZW5zaW9uT3JUaHJvdyh0aGlzLmdsLCAnV0VCR0xfbG9zZV9jb250ZXh0JykgYXNcbiAgICAgICAgV2ViR0xMb3NlQ29udGV4dEV4dGVuc2lvbjtcbiAgICB0aGlzLnZlcnRleEJ1ZmZlciA9IGdwZ3B1X3V0aWwuY3JlYXRlVmVydGV4QnVmZmVyKHRoaXMuZ2wpO1xuICAgIHRoaXMuaW5kZXhCdWZmZXIgPSBncGdwdV91dGlsLmNyZWF0ZUluZGV4QnVmZmVyKHRoaXMuZ2wpO1xuICAgIHRoaXMuZnJhbWVidWZmZXIgPSB3ZWJnbF91dGlsLmNyZWF0ZUZyYW1lYnVmZmVyKHRoaXMuZ2wpO1xuICB9XG5cbiAgcHVibGljIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBpZiAodGhpcy5wcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAnRGlzcG9zaW5nIGEgR1BHUFVDb250ZXh0IHRoYXQgc3RpbGwgaGFzIGEgYm91bmQgV2ViR0xQcm9ncmFtLicgK1xuICAgICAgICAgICcgVGhpcyBpcyBwcm9iYWJseSBhIHJlc291cmNlIGxlYWssIGRlbGV0ZSB0aGUgcHJvZ3JhbSB3aXRoICcgK1xuICAgICAgICAgICdHUEdQVUNvbnRleHQuZGVsZXRlUHJvZ3JhbSBiZWZvcmUgZGlzcG9zaW5nLicpO1xuICAgIH1cbiAgICBpZiAodGhpcy5vdXRwdXRUZXh0dXJlICE9IG51bGwpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgICAnRGlzcG9zaW5nIGEgR1BHUFVDb250ZXh0IHRoYXQgc3RpbGwgaGFzIGEgYm91bmQgb3V0cHV0IG1hdHJpeCAnICtcbiAgICAgICAgICAndGV4dHVyZS4gIFRoaXMgaXMgcHJvYmFibHkgYSByZXNvdXJjZSBsZWFrLCBkZWxldGUgdGhlIG91dHB1dCAnICtcbiAgICAgICAgICAnbWF0cml4IHRleHR1cmUgd2l0aCBHUEdQVUNvbnRleHQuZGVsZXRlTWF0cml4VGV4dHVyZSBiZWZvcmUgJyArXG4gICAgICAgICAgJ2Rpc3Bvc2luZy4nKTtcbiAgICB9XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5maW5pc2goKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVGcmFtZWJ1ZmZlcih0aGlzLmZyYW1lYnVmZmVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBudWxsKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRlbGV0ZUJ1ZmZlcih0aGlzLnZlcnRleEJ1ZmZlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgICBnbCwgKCkgPT4gZ2wuYmluZEJ1ZmZlcihnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgbnVsbCkpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVCdWZmZXIodGhpcy5pbmRleEJ1ZmZlcikpO1xuICAgIHRoaXMubG9zZUNvbnRleHRFeHRlbnNpb24ubG9zZUNvbnRleHQoKTtcbiAgICB0aGlzLmRpc3Bvc2VkID0gdHJ1ZTtcbiAgfVxuXG4gIHB1YmxpYyBlbmFibGVBdXRvbWF0aWNEZWJ1Z1ZhbGlkYXRpb24oZW5hYmxlZDogYm9vbGVhbikge1xuICAgIHRoaXMuYXV0b0RlYnVnVmFsaWRhdGUgPSBlbmFibGVkO1xuICAgIHdlYmdsX3V0aWwuZW5hYmxlRGVidWdXZWJHTEVycm9yQ2hlY2tpbmcoZW5hYmxlZCk7XG4gIH1cblxuICBwdWJsaWMgY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC5jcmVhdGVNYXRyaXhUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpO1xuICB9XG5cbiAgcHVibGljIHVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZShcbiAgICAgIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICAgIHBpeGVsczogSW1hZ2VEYXRhfEhUTUxJbWFnZUVsZW1lbnR8SFRNTENhbnZhc0VsZW1lbnR8SFRNTFZpZGVvRWxlbWVudCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgZ3BncHVfdXRpbC51cGxvYWRQaXhlbERhdGFUb1RleHR1cmUodGhpcy5nbCwgdGV4dHVyZSwgcGl4ZWxzKTtcbiAgfVxuXG4gIHB1YmxpYyBjcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTpcbiAgICAgIFdlYkdMVGV4dHVyZSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICByZXR1cm4gZ3BncHVfdXRpbC5jcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKHRoaXMuZ2wsIHJvd3MsIGNvbHVtbnMpO1xuICB9XG5cbiAgcHVibGljIGRlbGV0ZU1hdHJpeFRleHR1cmUodGV4dHVyZTogV2ViR0xUZXh0dXJlKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBpZiAodGhpcy5vdXRwdXRUZXh0dXJlID09PSB0ZXh0dXJlKSB7XG4gICAgICB3ZWJnbF91dGlsLnVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcih0aGlzLmdsLCB0aGlzLmZyYW1lYnVmZmVyKTtcbiAgICAgIHRoaXMub3V0cHV0VGV4dHVyZSA9IG51bGw7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKHRoaXMuZ2wsICgpID0+IHRoaXMuZ2wuZGVsZXRlVGV4dHVyZSh0ZXh0dXJlKSk7XG4gIH1cblxuICBwdWJsaWMgdXBsb2FkTWF0cml4VG9UZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICAgIG1hdHJpeDogRmxvYXQzMkFycmF5KSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBudW1DaGFubmVscyA9IDE7XG4gICAgcmV0dXJuIGdwZ3B1X3V0aWwudXBsb2FkTWF0cml4VG9UZXh0dXJlKFxuICAgICAgICB0aGlzLmdsLCB0ZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBtYXRyaXgsIG51bUNoYW5uZWxzKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgICAgbWF0cml4OiBGbG9hdDMyQXJyYXkpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHJldHVybiBncGdwdV91dGlsLnVwbG9hZE1hdHJpeFRvUGFja2VkVGV4dHVyZShcbiAgICAgICAgdGhpcy5nbCwgdGV4dHVyZSwgcm93cywgY29sdW1ucywgbWF0cml4KTtcbiAgfVxuXG4gIHB1YmxpYyBkb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKFxuICAgICAgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gICAgcmV0dXJuIHRoaXMuZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICAgIHRleHR1cmUsXG4gICAgICAgICgpID0+XG4gICAgICAgICAgICBncGdwdV91dGlsLmRvd25sb2FkTWF0cml4RnJvbU91dHB1dFRleHR1cmUodGhpcy5nbCwgcm93cywgY29sdW1ucykpO1xuICB9XG5cbiAgcHVibGljIGRvd25sb2FkTWF0cml4RnJvbVBhY2tlZFRleHR1cmUoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5kb3dubG9hZE1hdHJpeERyaXZlcihcbiAgICAgICAgdGV4dHVyZSxcbiAgICAgICAgKCkgPT4gZ3BncHVfdXRpbC5kb3dubG9hZE1hdHJpeEZyb21QYWNrZWRPdXRwdXRUZXh0dXJlKFxuICAgICAgICAgICAgdGhpcy5nbCwgcm93cywgY29sdW1ucykpO1xuICB9XG5cbiAgcHVibGljIGNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTb3VyY2U6IHN0cmluZyk6IFdlYkdMUHJvZ3JhbSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBnbCA9IHRoaXMuZ2w7XG4gICAgY29uc3QgZnJhZ21lbnRTaGFkZXI6IFdlYkdMU2hhZGVyID1cbiAgICAgICAgd2ViZ2xfdXRpbC5jcmVhdGVGcmFnbWVudFNoYWRlcihnbCwgZnJhZ21lbnRTaGFkZXJTb3VyY2UpO1xuICAgIGNvbnN0IHZlcnRleFNoYWRlcjogV2ViR0xTaGFkZXIgPSBncGdwdV91dGlsLmNyZWF0ZVZlcnRleFNoYWRlcihnbCk7XG4gICAgY29uc3QgcHJvZ3JhbTogV2ViR0xQcm9ncmFtID0gd2ViZ2xfdXRpbC5jcmVhdGVQcm9ncmFtKGdsKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYXR0YWNoU2hhZGVyKHByb2dyYW0sIHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5hdHRhY2hTaGFkZXIocHJvZ3JhbSwgZnJhZ21lbnRTaGFkZXIpKTtcbiAgICB3ZWJnbF91dGlsLmxpbmtQcm9ncmFtKGdsLCBwcm9ncmFtKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgd2ViZ2xfdXRpbC52YWxpZGF0ZVByb2dyYW0oZ2wsIHByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGV0YWNoU2hhZGVyKHByb2dyYW0sIHZlcnRleFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVTaGFkZXIodmVydGV4U2hhZGVyKSk7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRldGFjaFNoYWRlcihwcm9ncmFtLCBmcmFnbWVudFNoYWRlcikpO1xuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kZWxldGVTaGFkZXIoZnJhZ21lbnRTaGFkZXIpKTtcbiAgICByZXR1cm4gcHJvZ3JhbTtcbiAgfVxuXG4gIHB1YmxpYyBkZWxldGVQcm9ncmFtKHByb2dyYW06IFdlYkdMUHJvZ3JhbSkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgaWYgKHByb2dyYW0gPT09IHRoaXMucHJvZ3JhbSkge1xuICAgICAgdGhpcy5wcm9ncmFtID0gbnVsbDtcbiAgICB9XG4gICAgaWYgKHByb2dyYW0gIT0gbnVsbCkge1xuICAgICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2sodGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5kZWxldGVQcm9ncmFtKHByb2dyYW0pKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgc2V0UHJvZ3JhbShwcm9ncmFtOiBXZWJHTFByb2dyYW18bnVsbCkge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy5wcm9ncmFtID0gcHJvZ3JhbTtcbiAgICBpZiAoKHRoaXMucHJvZ3JhbSAhPSBudWxsKSAmJiB0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlUHJvZ3JhbSh0aGlzLmdsLCB0aGlzLnByb2dyYW0pO1xuICAgIH1cbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLnVzZVByb2dyYW0ocHJvZ3JhbSkpO1xuICB9XG5cbiAgcHVibGljIGdldFVuaWZvcm1Mb2NhdGlvbih1bmlmb3JtTmFtZTogc3RyaW5nKTogV2ViR0xVbmlmb3JtTG9jYXRpb24ge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgdGhpcy50aHJvd0lmTm9Qcm9ncmFtKCk7XG4gICAgcmV0dXJuIHdlYmdsX3V0aWwuZ2V0UHJvZ3JhbVVuaWZvcm1Mb2NhdGlvbk9yVGhyb3coXG4gICAgICAgIHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSEsIHVuaWZvcm1OYW1lKTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRJbnB1dE1hdHJpeFRleHR1cmUoXG4gICAgICBpbnB1dE1hdHJpeFRleHR1cmU6IFdlYkdMVGV4dHVyZSwgdW5pZm9ybU5hbWU6IHN0cmluZyxcbiAgICAgIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIHdlYmdsX3V0aWwuYmluZFRleHR1cmVUb1Byb2dyYW1Vbmlmb3JtU2FtcGxlcihcbiAgICAgICAgdGhpcy5nbCwgdGhpcy5wcm9ncmFtISwgaW5wdXRNYXRyaXhUZXh0dXJlLCB1bmlmb3JtTmFtZSwgdGV4dHVyZVVuaXQpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICBvdXRwdXRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKG91dHB1dE1hdHJpeFRleHR1cmUsIGNvbHVtbnMsIHJvd3MpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dFBhY2tlZE1hdHJpeFRleHR1cmUoXG4gICAgICBvdXRwdXRQYWNrZWRNYXRyaXhUZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICBjb25zdCBbd2lkdGgsIGhlaWdodF0gPVxuICAgICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgICB0aGlzLnNldE91dHB1dE1hdHJpeFRleHR1cmVEcml2ZXIob3V0cHV0UGFja2VkTWF0cml4VGV4dHVyZSwgd2lkdGgsIGhlaWdodCk7XG4gIH1cblxuICBwdWJsaWMgc2V0T3V0cHV0TWF0cml4V3JpdGVSZWdpb24oXG4gICAgICBzdGFydFJvdzogbnVtYmVyLCBudW1Sb3dzOiBudW1iZXIsIHN0YXJ0Q29sdW1uOiBudW1iZXIsXG4gICAgICBudW1Db2x1bW5zOiBudW1iZXIpIHtcbiAgICB0aGlzLnNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uRHJpdmVyKFxuICAgICAgICBzdGFydENvbHVtbiwgc3RhcnRSb3csIG51bUNvbHVtbnMsIG51bVJvd3MpO1xuICB9XG5cbiAgcHVibGljIHNldE91dHB1dFBhY2tlZE1hdHJpeFdyaXRlUmVnaW9uKFxuICAgICAgc3RhcnRSb3c6IG51bWJlciwgbnVtUm93czogbnVtYmVyLCBzdGFydENvbHVtbjogbnVtYmVyLFxuICAgICAgbnVtQ29sdW1uczogbnVtYmVyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdzZXRPdXRwdXRQYWNrZWRNYXRyaXhXcml0ZVJlZ2lvbiBub3QgaW1wbGVtZW50ZWQuJyk7XG4gIH1cblxuICBwdWJsaWMgZGVidWdWYWxpZGF0ZSgpIHtcbiAgICBpZiAodGhpcy5wcm9ncmFtICE9IG51bGwpIHtcbiAgICAgIHdlYmdsX3V0aWwudmFsaWRhdGVQcm9ncmFtKHRoaXMuZ2wsIHRoaXMucHJvZ3JhbSk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwudmFsaWRhdGVGcmFtZWJ1ZmZlcih0aGlzLmdsKTtcbiAgfVxuXG4gIHB1YmxpYyBleGVjdXRlUHJvZ3JhbSgpIHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHRoaXMudGhyb3dJZk5vUHJvZ3JhbSgpO1xuICAgIGNvbnN0IGdsID0gdGhpcy5nbDtcbiAgICBncGdwdV91dGlsLmJpbmRWZXJ0ZXhQcm9ncmFtQXR0cmlidXRlU3RyZWFtcyhcbiAgICAgICAgZ2wsIHRoaXMucHJvZ3JhbSEsIHRoaXMudmVydGV4QnVmZmVyKTtcbiAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgdGhpcy5kZWJ1Z1ZhbGlkYXRlKCk7XG4gICAgfVxuICAgIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgICBnbCwgKCkgPT4gZ2wuZHJhd0VsZW1lbnRzKGdsLlRSSUFOR0xFUywgNiwgZ2wuVU5TSUdORURfU0hPUlQsIDApKTtcbiAgfVxuXG4gIHB1YmxpYyBibG9ja1VudGlsQWxsUHJvZ3JhbXNDb21wbGV0ZWQoKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayh0aGlzLmdsLCAoKSA9PiB0aGlzLmdsLmZpbmlzaCgpKTtcbiAgfVxuXG4gIHByaXZhdGUgZG93bmxvYWRNYXRyaXhEcml2ZXIoXG4gICAgICB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgICBkb3dubG9hZEFuZERlY29kZTogKCkgPT4gRmxvYXQzMkFycmF5KTogRmxvYXQzMkFycmF5IHtcbiAgICB0aGlzLnRocm93SWZEaXNwb3NlZCgpO1xuICAgIHdlYmdsX3V0aWwuYmluZENvbG9yVGV4dHVyZVRvRnJhbWVidWZmZXIoXG4gICAgICAgIHRoaXMuZ2wsIHRleHR1cmUsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGRvd25sb2FkQW5kRGVjb2RlKCk7XG4gICAgaWYgKHRoaXMub3V0cHV0VGV4dHVyZSAhPSBudWxsKSB7XG4gICAgICB3ZWJnbF91dGlsLmJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgICAgICAgIHRoaXMuZ2wsIHRoaXMub3V0cHV0VGV4dHVyZSwgdGhpcy5mcmFtZWJ1ZmZlcik7XG4gICAgICBpZiAodGhpcy5hdXRvRGVidWdWYWxpZGF0ZSkge1xuICAgICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIodGhpcy5nbCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHdlYmdsX3V0aWwudW5iaW5kQ29sb3JUZXh0dXJlRnJvbUZyYW1lYnVmZmVyKHRoaXMuZ2wsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRPdXRwdXRNYXRyaXhUZXh0dXJlRHJpdmVyKFxuICAgICAgb3V0cHV0TWF0cml4VGV4dHVyZU1heWJlUGFja2VkOiBXZWJHTFRleHR1cmUsIHdpZHRoOiBudW1iZXIsXG4gICAgICBoZWlnaHQ6IG51bWJlcikge1xuICAgIHRoaXMudGhyb3dJZkRpc3Bvc2VkKCk7XG4gICAgY29uc3QgZ2wgPSB0aGlzLmdsO1xuICAgIHdlYmdsX3V0aWwuYmluZENvbG9yVGV4dHVyZVRvRnJhbWVidWZmZXIoXG4gICAgICAgIGdsLCBvdXRwdXRNYXRyaXhUZXh0dXJlTWF5YmVQYWNrZWQsIHRoaXMuZnJhbWVidWZmZXIpO1xuICAgIGlmICh0aGlzLmF1dG9EZWJ1Z1ZhbGlkYXRlKSB7XG4gICAgICB3ZWJnbF91dGlsLnZhbGlkYXRlRnJhbWVidWZmZXIoZ2wpO1xuICAgIH1cbiAgICB0aGlzLm91dHB1dFRleHR1cmUgPSBvdXRwdXRNYXRyaXhUZXh0dXJlTWF5YmVQYWNrZWQ7XG4gICAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnZpZXdwb3J0KDAsIDAsIHdpZHRoLCBoZWlnaHQpKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuc2Npc3NvcigwLCAwLCB3aWR0aCwgaGVpZ2h0KSk7XG4gIH1cblxuICBwcml2YXRlIHNldE91dHB1dE1hdHJpeFdyaXRlUmVnaW9uRHJpdmVyKFxuICAgICAgeDogbnVtYmVyLCB5OiBudW1iZXIsIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKSB7XG4gICAgdGhpcy50aHJvd0lmRGlzcG9zZWQoKTtcbiAgICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgICAgdGhpcy5nbCwgKCkgPT4gdGhpcy5nbC5zY2lzc29yKHgsIHksIHdpZHRoLCBoZWlnaHQpKTtcbiAgfVxuXG4gIHByaXZhdGUgdGhyb3dJZkRpc3Bvc2VkKCkge1xuICAgIGlmICh0aGlzLmRpc3Bvc2VkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F0dGVtcHRlZCB0byB1c2UgZGlzcG9zZWQgR1BHUFVDb250ZXh0LicpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgdGhyb3dJZk5vUHJvZ3JhbSgpIHtcbiAgICBpZiAodGhpcy5wcm9ncmFtID09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTm8gR1BVIHByb2dyYW0gaXMgY3VycmVudGx5IHNldC4nKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgdGV4X3V0aWwgZnJvbSAnLi90ZXhfdXRpbCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRXZWJHTENvbnRleHRBdHRyaWJ1dGVzKCk6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMge1xuICByZXR1cm4ge1xuICAgIGFscGhhOiBmYWxzZSxcbiAgICBhbnRpYWxpYXM6IGZhbHNlLFxuICAgIHByZW11bHRpcGxpZWRBbHBoYTogZmFsc2UsXG4gICAgcHJlc2VydmVEcmF3aW5nQnVmZmVyOiBmYWxzZSxcbiAgICBkZXB0aDogZmFsc2UsXG4gICAgc3RlbmNpbDogZmFsc2UsXG4gICAgZmFpbElmTWFqb3JQZXJmb3JtYW5jZUNhdmVhdDogdHJ1ZVxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xDb250ZXh0KGNhbnZhcz86IEhUTUxDYW52YXNFbGVtZW50KSB7XG4gIGNvbnN0IGF0dHJpYnV0ZXMgPSBnZXRXZWJHTENvbnRleHRBdHRyaWJ1dGVzKCk7XG4gIGxldCBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICBpZiAoY2FudmFzICE9IG51bGwpIHtcbiAgICBnbCA9IHdlYmdsX3V0aWwuY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0RnJvbUNhbnZhcyhjYW52YXMsIGF0dHJpYnV0ZXMpO1xuICB9IGVsc2Uge1xuICAgIGdsID0gd2ViZ2xfdXRpbC5jcmVhdGVXZWJHTFJlbmRlcmluZ0NvbnRleHQoYXR0cmlidXRlcyk7XG4gIH1cbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuREVQVEhfVEVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuZGlzYWJsZShnbC5TVEVOQ0lMX1RFU1QpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuQkxFTkQpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuRElUSEVSKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5kaXNhYmxlKGdsLlBPTFlHT05fT0ZGU0VUX0ZJTEwpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmRpc2FibGUoZ2wuU0FNUExFX0NPVkVSQUdFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGUoZ2wuU0NJU1NPUl9URVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGUoZ2wuQ1VMTF9GQUNFKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jdWxsRmFjZShnbC5CQUNLKSk7XG4gIHJldHVybiBnbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVZlcnRleFNoYWRlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xTaGFkZXIge1xuICBjb25zdCB2ZXJ0ZXhTaGFkZXJTb3VyY2UgPSBgXG4gICAgcHJlY2lzaW9uIGhpZ2hwIGZsb2F0O1xuICAgIGF0dHJpYnV0ZSB2ZWMzIGNsaXBTcGFjZVBvcztcbiAgICBhdHRyaWJ1dGUgdmVjMiB1djtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBnbF9Qb3NpdGlvbiA9IHZlYzQoY2xpcFNwYWNlUG9zLCAxKTtcbiAgICAgIHJlc3VsdFVWID0gdXY7XG4gICAgfWA7XG4gIHJldHVybiB3ZWJnbF91dGlsLmNyZWF0ZVZlcnRleFNoYWRlcihnbCwgdmVydGV4U2hhZGVyU291cmNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVZlcnRleEJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xCdWZmZXIge1xuICAvLyBbeCB5IHogdSB2XSAqIFt1cHBlci1sZWZ0LCBsb3dlci1sZWZ0LCB1cHBlci1yaWdodCwgbG93ZXItcmlnaHRdXG4gIGNvbnN0IHZlcnRleEFycmF5ID0gbmV3IEZsb2F0MzJBcnJheShcbiAgICAgIFstMSwgMSwgMCwgMCwgMSwgLTEsIC0xLCAwLCAwLCAwLCAxLCAxLCAwLCAxLCAxLCAxLCAtMSwgMCwgMSwgMF0pO1xuICByZXR1cm4gd2ViZ2xfdXRpbC5jcmVhdGVTdGF0aWNWZXJ0ZXhCdWZmZXIoZ2wsIHZlcnRleEFycmF5KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUluZGV4QnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEJ1ZmZlciB7XG4gIC8vIE9wZW5HTCAoYW5kIFdlYkdMKSBoYXZlIFwiQ0NXID09IGZyb250XCIgd2luZGluZ1xuICBjb25zdCB0cmlhbmdsZVZlcnRleEluZGljZXMgPSBuZXcgVWludDE2QXJyYXkoWzAsIDEsIDIsIDIsIDEsIDNdKTtcbiAgcmV0dXJuIHdlYmdsX3V0aWwuY3JlYXRlU3RhdGljSW5kZXhCdWZmZXIoZ2wsIHRyaWFuZ2xlVmVydGV4SW5kaWNlcyk7XG59XG5cbmZ1bmN0aW9uIGdldFRleHR1cmVJbnRlcm5hbEZvcm1hdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBudW1DaGFubmVsczogbnVtYmVyKTogbnVtYmVyIHtcbiAgaWYgKHdlYmdsX3V0aWwuaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICBpZiAobnVtQ2hhbm5lbHMgPT09IDQpIHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICAgIHJldHVybiAoZ2wgYXMgYW55KS5SR0JBMzJGO1xuICAgIH1cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gICAgcmV0dXJuIChnbCBhcyBhbnkpLlIzMkY7XG4gIH1cbiAgcmV0dXJuIGdsLlJHQkE7XG59XG5cbmZ1bmN0aW9uIGdldFRleHR1cmVGb3JtYXQoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgbnVtQ2hhbm5lbHM6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh3ZWJnbF91dGlsLmlzV2ViR0wyRW5hYmxlZCgpICYmIG51bUNoYW5uZWxzID09PSAxKSB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIHJldHVybiAoZ2wgYXMgYW55KS5SRUQ7XG4gIH1cbiAgcmV0dXJuIGdsLlJHQkE7XG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIsXG4gICAgbnVtQ2hhbm5lbHM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIHdlYmdsX3V0aWwudmFsaWRhdGVUZXh0dXJlU2l6ZShnbCwgd2lkdGgsIGhlaWdodCk7XG4gIGNvbnN0IHRleHR1cmUgPSB3ZWJnbF91dGlsLmNyZWF0ZVRleHR1cmUoZ2wpO1xuXG4gIGNvbnN0IHRleDJkID0gZ2wuVEVYVFVSRV8yRDtcbiAgY29uc3QgaW50ZXJuYWxGb3JtYXQgPSBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgY29uc3QgZm9ybWF0ID0gZ2V0VGV4dHVyZUZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZFRleHR1cmUodGV4MmQsIHRleHR1cmUpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9XUkFQX1MsIGdsLkNMQU1QX1RPX0VER0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9XUkFQX1QsIGdsLkNMQU1QX1RPX0VER0UpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wudGV4UGFyYW1ldGVyaSh0ZXgyZCwgZ2wuVEVYVFVSRV9NSU5fRklMVEVSLCBnbC5ORUFSRVNUKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLnRleFBhcmFtZXRlcmkodGV4MmQsIGdsLlRFWFRVUkVfTUFHX0ZJTFRFUiwgZ2wuTkVBUkVTVCkpO1xuICB3ZWJnbF91dGlsLmNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudGV4SW1hZ2UyRChcbiAgICAgICAgICB0ZXgyZCwgMCwgaW50ZXJuYWxGb3JtYXQsIHdpZHRoLCBoZWlnaHQsIDAsIGZvcm1hdCwgZ2wuRkxPQVQsIG51bGwpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbiAgcmV0dXJuIHRleHR1cmU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVNYXRyaXhUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgIHRleF91dGlsLmdldFVucGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gMTtcbiAgcmV0dXJuIGNyZWF0ZUFuZENvbmZpZ3VyZVRleHR1cmUoZ2wsIHdpZHRoLCBoZWlnaHQsIG51bUNoYW5uZWxzKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUNvbG9yTWF0cml4VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFdlYkdMVGV4dHVyZSB7XG4gIGNvbnN0IFt3aWR0aCwgaGVpZ2h0XSA9XG4gICAgICB0ZXhfdXRpbC5nZXRDb2xvck1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVQYWNrZWRNYXRyaXhUZXh0dXJlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogV2ViR0xUZXh0dXJlIHtcbiAgY29uc3QgW3dpZHRoLCBoZWlnaHRdID1cbiAgICAgIHRleF91dGlsLmdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCBudW1DaGFubmVscyA9IDQ7XG4gIHJldHVybiBjcmVhdGVBbmRDb25maWd1cmVUZXh0dXJlKGdsLCB3aWR0aCwgaGVpZ2h0LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVmVydGV4UHJvZ3JhbUF0dHJpYnV0ZVN0cmVhbXMoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLFxuICAgIHZlcnRleEJ1ZmZlcjogV2ViR0xCdWZmZXIpIHtcbiAgY29uc3QgcG9zT2Zmc2V0ID0gMDsgICAgICAgICAgICAgICAvLyB4IGlzIHRoZSBmaXJzdCBidWZmZXIgZWxlbWVudFxuICBjb25zdCB1dk9mZnNldCA9IDMgKiA0OyAgICAgICAgICAgIC8vIHV2IGNvbWVzIGFmdGVyIFt4IHkgel1cbiAgY29uc3Qgc3RyaWRlID0gKDMgKiA0KSArICgyICogNCk7ICAvLyB4eXogKyB1diwgZWFjaCBlbnRyeSBpcyA0LWJ5dGUgZmxvYXQuXG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCB2ZXJ0ZXhCdWZmZXIpKTtcbiAgd2ViZ2xfdXRpbC5iaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgICAgZ2wsIHByb2dyYW0sICdjbGlwU3BhY2VQb3MnLCB2ZXJ0ZXhCdWZmZXIsIDMsIHN0cmlkZSwgcG9zT2Zmc2V0KTtcbiAgdHJ5IHtcbiAgICB3ZWJnbF91dGlsLmJpbmRWZXJ0ZXhCdWZmZXJUb1Byb2dyYW1BdHRyaWJ1dGUoXG4gICAgICAgIGdsLCBwcm9ncmFtLCAndXYnLCB2ZXJ0ZXhCdWZmZXIsIDIsIHN0cmlkZSwgdXZPZmZzZXQpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgLy8gUHJvZ3JhbXMgd2l0aCAxeDEgb3V0cHV0IHRleHR1cmVzIGRvbid0IHVzZSB0aGUgdXYgYXR0cmlidXRlLlxuICAgIC8vIFRoaXMgY2FuIGNhdXNlIHRoZSBzaGFkZXIgbGlua2VyIHRvIGRlYWQtc3RyaXAgaXQsIHNvIHdlIHNob3VsZG4ndFxuICAgIC8vIGNvbXBsYWluIG9yIGZhaWwgaWYgaXQncyBub3QgcHJlc2VudC5cbiAgICBpZiAoIWUuaGFzT3duUHJvcGVydHkoJ25hbWVkVmVydGV4QXR0cmlidXRlTm90Rm91bmQnKSkge1xuICAgICAgdGhyb3cgZTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFBpeGVsRGF0YVRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsXG4gICAgcGl4ZWxzOiBJbWFnZURhdGF8SFRNTEltYWdlRWxlbWVudHxIVE1MQ2FudmFzRWxlbWVudHxIVE1MVmlkZW9FbGVtZW50KSB7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgY29uc3QgaW50ZXJuYWxGb3JtYXQgPSBnZXRUZXh0dXJlSW50ZXJuYWxGb3JtYXQoZ2wsIG51bUNoYW5uZWxzKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCxcbiAgICAgICgpID0+IGdsLnRleEltYWdlMkQoXG4gICAgICAgICAgZ2wuVEVYVFVSRV8yRCwgMCwgaW50ZXJuYWxGb3JtYXQsIGdsLlJHQkEsIGdsLkZMT0FULCBwaXhlbHMpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZnVuY3Rpb24gdXBsb2FkRGF0YVRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHdpZHRoOiBudW1iZXIsXG4gICAgaGVpZ2h0OiBudW1iZXIsIGRhdGE6IEZsb2F0MzJBcnJheSwgbnVtQ2hhbm5lbHM6IG51bWJlcikge1xuICBjb25zdCB0ZXh0dXJlRm9ybWF0ID0gZ2V0VGV4dHVyZUZvcm1hdChnbCwgbnVtQ2hhbm5lbHMpO1xuXG4gIHdlYmdsX3V0aWwudmFsaWRhdGVUZXh0dXJlU2l6ZShnbCwgd2lkdGgsIGhlaWdodCk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kVGV4dHVyZShnbC5URVhUVVJFXzJELCB0ZXh0dXJlKSk7XG4gIHdlYmdsX3V0aWwuY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC50ZXhTdWJJbWFnZTJEKFxuICAgICAgICAgIGdsLlRFWFRVUkVfMkQsIDAsIDAsIDAsIHdpZHRoLCBoZWlnaHQsIHRleHR1cmVGb3JtYXQsIGdsLkZMT0FULFxuICAgICAgICAgIGRhdGEpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZE1hdHJpeFRvVGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHJvd3M6IG51bWJlcixcbiAgICBjb2x1bW5zOiBudW1iZXIsIG1hdHJpeDogRmxvYXQzMkFycmF5LCBudW1DaGFubmVsczogbnVtYmVyKSB7XG4gIGNvbnN0IFt3LCBoXSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuXG4gIGNvbnN0IGNoYW5uZWxzUGVyVGV4dHVyZSA9XG4gICAgICBudW1DaGFubmVscyA9PT0gMSA/IHdlYmdsX3V0aWwuZ2V0Q2hhbm5lbHNQZXJUZXh0dXJlKCkgOiBudW1DaGFubmVscztcbiAgY29uc3QgdW5wYWNrZWRBcnJheSA9XG4gICAgICBuZXcgRmxvYXQzMkFycmF5KHRleF91dGlsLmdldFVucGFja2VkQXJyYXlTaXplRnJvbU1hdHJpeFNpemUoXG4gICAgICAgICAgbWF0cml4Lmxlbmd0aCwgY2hhbm5lbHNQZXJUZXh0dXJlKSk7XG4gIHRleF91dGlsLmVuY29kZU1hdHJpeFRvVW5wYWNrZWRBcnJheShcbiAgICAgIG1hdHJpeCwgdW5wYWNrZWRBcnJheSwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcblxuICB1cGxvYWREYXRhVG9UZXh0dXJlKGdsLCB0ZXh0dXJlLCB3LCBoLCB1bnBhY2tlZEFycmF5LCBudW1DaGFubmVscyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRNYXRyaXhUb1BhY2tlZFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCBtYXRyaXg6IEZsb2F0MzJBcnJheSkge1xuICBjb25zdCBbdywgaF0gPSB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcGFja2VkUkdCQSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRSR0JBQXJyYXlTaXplRnJvbU1hdHJpeFNoYXBlKHJvd3MsIGNvbHVtbnMpKTtcbiAgdGV4X3V0aWwuZW5jb2RlTWF0cml4VG9QYWNrZWRSR0JBKG1hdHJpeCwgcm93cywgY29sdW1ucywgcGFja2VkUkdCQSk7XG4gIGNvbnN0IG51bUNoYW5uZWxzID0gNDtcbiAgdXBsb2FkRGF0YVRvVGV4dHVyZShnbCwgdGV4dHVyZSwgdywgaCwgcGFja2VkUkdCQSwgbnVtQ2hhbm5lbHMpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZG93bmxvYWRNYXRyaXhGcm9tT3V0cHV0VGV4dHVyZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IFt3LCBoXSA9XG4gICAgICB0ZXhfdXRpbC5nZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuXG4gIGNvbnN0IGNoYW5uZWxzUGVyVGV4dHVyZSA9IDQ7XG4gIGNvbnN0IHVucGFja2VkQXJyYXkgPVxuICAgICAgbmV3IEZsb2F0MzJBcnJheSh0ZXhfdXRpbC5nZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgICAgICAgIHJvd3MgKiBjb2x1bW5zLCBjaGFubmVsc1BlclRleHR1cmUpKTtcbiAgY29uc3QgdGV4dHVyZUZvcm1hdCA9IGdldFRleHR1cmVGb3JtYXQoZ2wsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG5cbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wucmVhZFBpeGVscygwLCAwLCB3LCBoLCBnbC5SR0JBLCBnbC5GTE9BVCwgdW5wYWNrZWRBcnJheSkpO1xuXG4gIGNvbnN0IG1hdHJpeCA9IG5ldyBGbG9hdDMyQXJyYXkocm93cyAqIGNvbHVtbnMpO1xuICB0ZXhfdXRpbC5kZWNvZGVNYXRyaXhGcm9tVW5wYWNrZWRBcnJheShcbiAgICAgIHVucGFja2VkQXJyYXksIG1hdHJpeCwgY2hhbm5lbHNQZXJUZXh0dXJlKTtcbiAgcmV0dXJuIG1hdHJpeDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRvd25sb2FkTWF0cml4RnJvbVBhY2tlZE91dHB1dFRleHR1cmUoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBbdywgaF0gPSB0ZXhfdXRpbC5nZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcGFja2VkUkdCQSA9IG5ldyBGbG9hdDMyQXJyYXkoXG4gICAgICB0ZXhfdXRpbC5nZXRQYWNrZWRSR0JBQXJyYXlTaXplRnJvbU1hdHJpeFNoYXBlKHJvd3MsIGNvbHVtbnMpKTtcbiAgd2ViZ2xfdXRpbC5jYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wucmVhZFBpeGVscygwLCAwLCB3LCBoLCBnbC5SR0JBLCBnbC5GTE9BVCwgcGFja2VkUkdCQSkpO1xuICBjb25zdCBtYXRyaXggPSBuZXcgRmxvYXQzMkFycmF5KHJvd3MgKiBjb2x1bW5zKTtcbiAgcmV0dXJuIHRleF91dGlsLmRlY29kZU1hdHJpeEZyb21QYWNrZWRSR0JBKHBhY2tlZFJHQkEsIHJvd3MsIGNvbHVtbnMsIG1hdHJpeCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldExvZ1VuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuICdnbF9GcmFnQ29sb3IgPSB2ZWM0KGxvZyh2YWx1ZSksIDAsIDAsIDApOyc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0TG9nVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvZyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBsb2dQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgbG9nUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZExvZ0Rvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldExvZ1VuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IGFNYXggPSB0ZXh0dXJlMkQobWF0cml4QSwgaGFsZkNSIC8gYURpbUNSKS5yO1xuICAgICAgZm9yIChmbG9hdCByID0gMC4wOyByIDwgYURpbUNSLnk7IHIgKz0gMS4wKSB7XG4gICAgICAgIGZvciAoZmxvYXQgYyA9IDAuMDsgYyA8IGFEaW1DUi54OyBjICs9IDEuMCkge1xuICAgICAgICAgIHZlYzIgdXYgPSAodmVjMihjLCByKSArIGhhbGZDUikgLyBhRGltQ1I7XG4gICAgICAgICAgZmxvYXQgYUN1ciA9IHRleHR1cmUyRChtYXRyaXhBLCB1dikucjtcbiAgICAgICAgICBhTWF4ID0gbWF4KGFNYXgsIGFDdXIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGZsb2F0IGV4cFN1bSA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIGZsb2F0IGFDdXIgPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgICAgZXhwU3VtICs9IGV4cChhQ3VyIC0gYU1heCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChhTWF4ICsgbG9nKGV4cFN1bSksIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbG9nU3VtRXhwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGxvZ1N1bUV4cFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0obG9nU3VtRXhwUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTG9nU3VtRXhwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtID0gZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zKSk7XG4gIGNvbnN0IGFUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZSA9IGdwZ3B1LmNyZWF0ZU1hdHJpeFRleHR1cmUoMSwgMSk7XG4gIGdwZ3B1LnVwbG9hZE1hdHJpeFRvVGV4dHVyZShhVGV4dHVyZSwgcm93cywgY29sdW1ucywgYSk7XG4gIGxvZ1N1bUV4cChncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleHR1cmUsIDEsIDEpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0WzBdO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJNYXhQb29sQmFja3Byb3AoXG4gICAgZHlTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBvcmlnU3RyaWRlOiBudW1iZXIsXG4gICAgb3JpZ1BhZDogbnVtYmVyKSB7XG4gIGNvbnN0IG9yaWdJbnB1dERlcHRoID0gZHlTaGFwZVJDRFsyXTtcbiAgY29uc3QgcGFkID0gZlNpemUgLSAxIC0gb3JpZ1BhZDtcbiAgY29uc3QgW2R5Um93cywgZHlDb2xzLCBkZXB0aF0gPSBkeVNoYXBlUkNEO1xuXG4gIGNvbnN0IGR5VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoZHlTaGFwZVJDRCk7XG5cbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgZHk7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF4UG9zO1xuXG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcbiAgICBjb25zdCB2ZWMyIGR5U2hhcGVDUiA9IHZlYzIoJHtkeVRleFNoYXBlUkNbMV19LCAke2R5VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiBkeFRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKGR4VGV4UiwgZHhUZXhDKSB0byAzRCAoZHhSLCBkeEMsIGQpLlxuICAgICAgZmxvYXQgZHhSID0gZHhUZXhDUi55O1xuICAgICAgZmxvYXQgZHhDID0gZmxvb3IoZHhUZXhDUi54IC8gJHtvcmlnSW5wdXREZXB0aH0uMCk7XG4gICAgICBmbG9hdCBkID0gbW9kKGR4VGV4Q1IueCwgJHtvcmlnSW5wdXREZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgZHlSQ0Nvcm5lciA9IHZlYzIoZHhSLCBkeEMpIC0gdmVjMigke3BhZH0uMCwgJHtwYWR9LjApO1xuICAgICAgZmxvYXQgZHlSQ29ybmVyID0gZHlSQ0Nvcm5lci54O1xuICAgICAgZmxvYXQgZHlDQ29ybmVyID0gZHlSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBDb252b2x2ZSBkeSg/LCA/LCBkKSB3aXRoIHBvcyBtYXNrKDosIDosIGQpIHRvIGdldCBkeCh5UiwgZHhDLCBkKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkLiA6ID0gYWNyb3NzIGFsbCB2YWx1ZXMgaW4gdGhhdCBheGlzLlxuICAgICAgZmxvYXQgZG90UHJvZCA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgd1IgPSAwLjA7IHdSIDwgJHtmU2l6ZX0uMDsgd1IgKz0gMS4wKSB7XG5cbiAgICAgICAgZmxvYXQgZHlSID0gKGR5UkNvcm5lciArIHdSKSAvICR7b3JpZ1N0cmlkZX0uMDtcbiAgICAgICAgLy8gVE9ETyhuc3Rob3JhdCk6IFNwbGljZSB0aGlzIHdpdGggYW5vdGhlciB2ZXJzaW9uIHdoZXJlIHlvdSBjYWxsXG4gICAgICAgIC8vIGdldE1hdHJpeFZhbHVlT3JaZXJvUGFkKCkuIEhlcmUgYW5kIGJlbG93LlxuICAgICAgICBpZiAoZHlSIDwgMC4wIHx8IGR5UiA+PSAke2R5Um93c30uMCB8fCBmcmFjdChkeVIpID4gMC4wKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBmbG9hdCBkeVRleFIgPSBkeVI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcblxuICAgICAgICAgIGZsb2F0IGR5QyA9IChkeUNDb3JuZXIgKyB3QykgLyAke29yaWdTdHJpZGV9LjA7XG4gICAgICAgICAgaWYgKGR5QyA8IDAuMCB8fCBkeUMgPj0gJHtkeUNvbHN9LjAgfHwgZnJhY3QoZHlDKSA+IDAuMCkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZmxvYXQgZHlUZXhDID0gZHlDICogJHtkZXB0aH0uMCArIGQ7XG5cbiAgICAgICAgICAvLyBSZWFkIGR5KGR5UiwgZHlDLCBkKS5cbiAgICAgICAgICB2ZWMyIGR5VVYgPSAodmVjMihkeVRleEMsIGR5VGV4UikgKyBoYWxmQ1IpIC8gZHlTaGFwZUNSO1xuICAgICAgICAgIGZsb2F0IGR5VmFsdWUgPSB0ZXh0dXJlMkQoZHksIGR5VVYpLnI7XG5cbiAgICAgICAgICAvLyBSZWFkIG1heFBvcyhkeVIsIGR5QywgZCkuXG4gICAgICAgICAgZmxvYXQgbWF4UG9zVmFsdWUgPVxuICAgICAgICAgICAgICAke2ZTaXplICogZlNpemUgLSAxfS4wIC0gdGV4dHVyZTJEKG1heFBvcywgZHlVVikucjtcblxuICAgICAgICAgIC8vIEdldCB0aGUgY3VycmVudCB2YWx1ZSwgY2hlY2sgaXQgYWdhaW5zdCB0aGUgdmFsdWUgZnJvbSB0aGVcbiAgICAgICAgICAvLyBwb3NpdGlvbiBtYXRyaXguXG4gICAgICAgICAgZmxvYXQgY3VyUG9zVmFsdWUgPSB3UiAqICR7ZlNpemV9LjAgKyB3QztcbiAgICAgICAgICBmbG9hdCBtYXNrID0gZmxvYXQobWF4UG9zVmFsdWUgPT0gY3VyUG9zVmFsdWUgPyAxLjAgOiAwLjApO1xuXG4gICAgICAgICAgZG90UHJvZCArPSBkeVZhbHVlICogbWFzaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChkb3RQcm9kLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1heFBvb2xCYWNrcHJvcChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIGR5VGV4OiBXZWJHTFRleHR1cmUsXG4gICAgbWF4UG9zaXRpb25zVGV4OiBXZWJHTFRleHR1cmUsIHJlc3VsdFRleDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdFRleFNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdFRleCwgcmVzdWx0VGV4U2hhcGVSQ1swXSwgcmVzdWx0VGV4U2hhcGVSQ1sxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShkeVRleCwgJ2R5JywgMCk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShtYXhQb3NpdGlvbnNUZXgsICdtYXhQb3MnLCAxKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyBwb29sX2dwdSBmcm9tICcuL3Bvb2xfZ3B1JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFBvc2l0aW9uc1NvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCB0cnVlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbFNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIpIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCBmYWxzZSk7XG59XG5cbmZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyTWF4UG9vbENvbW1vblNvdXJjZShcbiAgICB4U2hhcGVSQ0Q6IFtudW1iZXIsIG51bWJlciwgbnVtYmVyXSwgZlNpemU6IG51bWJlciwgc3RyaWRlOiBudW1iZXIsXG4gICAgcGFkOiBudW1iZXIsIGNvbXB1dGVNYXhQb3NpdGlvbnM6IGJvb2xlYW4pIHtcbiAgcmV0dXJuIHBvb2xfZ3B1LmdldEZyYWdtZW50U2hhZGVyUG9vbENvbW1vblNvdXJjZShcbiAgICAgIHhTaGFwZVJDRCwgZlNpemUsIHN0cmlkZSwgcGFkLCAnbWF4JywgY29tcHV0ZU1heFBvc2l0aW9ucyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXhQb29sQ29tbW9uKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgeDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdDogV2ViR0xUZXh0dXJlLCByZXN1bHRTaGFwZVJvd0NvbDogW251bWJlciwgbnVtYmVyXSkge1xuICBwb29sX2dwdS5wb29sQ29tbW9uKGdwZ3B1LCBwcm9ncmFtLCB4LCByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sKTtcbn0iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgcG9vbF9ncHUgZnJvbSAnLi9wb29sX2dwdSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlck1pblBvb2xTb3VyY2UoXG4gICAgeFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIGZTaXplOiBudW1iZXIsIHN0cmlkZTogbnVtYmVyLFxuICAgIHBhZDogbnVtYmVyKSB7XG4gIHJldHVybiBwb29sX2dwdS5nZXRGcmFnbWVudFNoYWRlclBvb2xDb21tb25Tb3VyY2UoXG4gICAgICB4U2hhcGVSQ0QsIGZTaXplLCBzdHJpZGUsIHBhZCwgJ21pbicsIGZhbHNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1pblBvb2woXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcHJvZ3JhbTogV2ViR0xQcm9ncmFtLCB4OiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIHBvb2xfZ3B1LnBvb2xDb21tb24oZ3BncHUsIHByb2dyYW0sIHgsIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2wpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQge0lTX05BTl9TSEFERVJfRlVOQ30gZnJvbSAnLi93ZWJnbF91dGlsJztcblxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIGNvbXBPcDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG4gICAgdW5pZm9ybSBzYW1wbGVyMkQgbWF0cml4QTtcbiAgICB2YXJ5aW5nIHZlYzIgb3V0cHV0Q29sdW1uUm93O1xuXG4gICAgY29uc3QgdmVjMiBhRGltQ1IgPSB2ZWMyKCR7Y29sdW1uc30uMCwgJHtyb3dzfS4wKTtcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgJHtJU19OQU5fU0hBREVSX0ZVTkN9XG5cbiAgICB2b2lkIG1haW4oKSB7XG4gICAgICBmbG9hdCB2YWx1ZSA9IHRleHR1cmUyRChtYXRyaXhBLCBoYWxmQ1IgLyBhRGltQ1IpLnI7XG4gICAgICBmb3IgKGZsb2F0IHIgPSAwLjA7IHIgPCBhRGltQ1IueTsgciArPSAxLjApIHtcbiAgICAgICAgZm9yIChmbG9hdCBjID0gMC4wOyBjIDwgYURpbUNSLng7IGMgKz0gMS4wKSB7XG4gICAgICAgICAgdmVjMiBjciA9IHZlYzIoYywgcik7XG4gICAgICAgICAgdmVjMiB1diA9IChjciArIGhhbGZDUikgLyBhRGltQ1I7XG4gICAgICAgICAgZmxvYXQgY2FuZGlkYXRlID0gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgICAgICAgIGlmIChpc05hTihjYW5kaWRhdGUpKSB7XG4gICAgICAgICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KGNhbmRpZGF0ZSwgMCwgMCwgMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIHZhbHVlID0gJHtjb21wT3B9KHZhbHVlLCBjYW5kaWRhdGUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHZhbHVlLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE1pbkZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICdtaW4nKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE1heEZyYWdtZW50U2hhZGVyU291cmNlKFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJvd3MsIGNvbHVtbnMsICdtYXgnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIG1pbk1heChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBtaW5NYXhQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShyZXN1bHQsIDEsIDEpO1xuICBncGdwdS5zZXRQcm9ncmFtKG1pbk1heFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbn0gZnJvbSAnLi4vbWF0aCc7XG5pbXBvcnQge0FycmF5MkR9IGZyb20gJy4uL25kYXJyYXknO1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHNoYWRlcl9jb21waWxlciBmcm9tICcuL3NoYWRlcl9jb21waWxlcic7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlcihcbiAgICBhOiBBcnJheTJELCBiOiBBcnJheTJELCBvdXQ6IEFycmF5MkQsIGFPcmllbnRhdGlvbjogTWF0cml4T3JpZW50YXRpb24sXG4gICAgYk9yaWVudGF0aW9uOiBNYXRyaXhPcmllbnRhdGlvbik6IHN0cmluZyB7XG4gIGNvbnN0IHNoYXJlZERpbSA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSID8gYS5zaGFwZVsxXSA6IGEuc2hhcGVbMF0pO1xuICBjb25zdCBhU25pcHBldCA9XG4gICAgICAoYU9yaWVudGF0aW9uID09PSBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSKSA/ICdhUm93LCBpJyA6ICdpLCBhUm93JztcbiAgY29uc3QgYlNuaXBwZXQgPVxuICAgICAgKGJPcmllbnRhdGlvbiA9PT0gTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUikgPyAnaSwgYkNvbCcgOiAnYkNvbCwgaSc7XG5cbiAgY29uc3QgaW5wdXRzID0gW3tuYW1lOiAnbWF0cml4QScsIGFycmF5OiBhfSwge25hbWU6ICdtYXRyaXhCJywgYXJyYXk6IGJ9XTtcbiAgY29uc3QgdXNlckNvZGUgPSBgXG4gICAgY29uc3QgZmxvYXQgc2hhcmVkRGltID0gJHtzaGFyZWREaW19LjA7XG5cbiAgICBmbG9hdCBkb3RBUm93QkNvbChmbG9hdCBhUm93LCBmbG9hdCBiQ29sKSB7XG4gICAgICBmbG9hdCByZXN1bHQgPSAwLjA7XG4gICAgICBmb3IgKGZsb2F0IGkgPSAwLjA7IGkgPCBzaGFyZWREaW07IGkgKz0gMS4wKSB7XG4gICAgICAgIGZsb2F0IGEgPSBnZXRNYXRyaXhBKCR7YVNuaXBwZXR9KTtcbiAgICAgICAgZmxvYXQgYiA9IGdldE1hdHJpeEIoJHtiU25pcHBldH0pO1xuICAgICAgICByZXN1bHQgKz0gKGEgKiBiKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiByZXNSQyA9IGdldE91dHB1dENvb3JkcygpO1xuICAgICAgc2V0T3V0cHV0KGRvdEFSb3dCQ29sKHJlc1JDLngsIHJlc1JDLnkpKTtcbiAgICB9XG4gIGA7XG4gIHJldHVybiBzaGFkZXJfY29tcGlsZXIubWFrZVNoYWRlcihpbnB1dHMsIG91dCwgdXNlckNvZGUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbXVsdGlwbHlNYXRyaXgoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgbXVsdGlwbHlQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBiOiBXZWJHTFRleHR1cmUsIHJlc3VsdDogV2ViR0xUZXh0dXJlLCBvdXRUZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgb3V0VGV4U2hhcGVbMF0sIG91dFRleFNoYXBlWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShtdWx0aXBseVByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGIsICdtYXRyaXhCJywgMSk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldE5lZ1VuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuICdnbF9GcmFnQ29sb3IgPSB2ZWM0KC12YWx1ZSwgMCwgMCwgMCk7Jztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXROZWdVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbmVnKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLCByb3dzOiBudW1iZXIsXG4gICAgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBwcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkTmVnRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0TmVnVW5hcnlPcCgpKTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0ICogYXMgY29udl91dGlsIGZyb20gJy4uL2NvbnZfdXRpbCc7XG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCB7SVNfTkFOX1NIQURFUl9GVU5DfSBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJQb29sQ29tbW9uU291cmNlKFxuICAgIHhTaGFwZVJDRDogW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCBmU2l6ZTogbnVtYmVyLCBzdHJpZGU6IG51bWJlcixcbiAgICBwYWQ6IG51bWJlciwgcG9vbFR5cGU6ICdtYXgnfCdtaW4nfCdhdmcnLCBjb21wdXRlUG9zaXRpb25zOiBib29sZWFuKSB7XG4gIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycgJiYgY29tcHV0ZVBvc2l0aW9ucykge1xuICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGNvbXB1dGUgcG9zaXRpb25zIGZvciBhdmVyYWdlIHBvb2wuJyk7XG4gIH1cblxuICBjb25zdCBkZXB0aCA9IHhTaGFwZVJDRFsyXTtcblxuICBjb25zdCB4VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoeFNoYXBlUkNEKTtcblxuICBsZXQgcmV0dXJuVmFsdWUgPSAnbWluTWF4VmFsdWUnO1xuICBpZiAoY29tcHV0ZVBvc2l0aW9ucykge1xuICAgIHJldHVyblZhbHVlID0gJ21pbk1heFBvc2l0aW9uJztcbiAgfSBlbHNlIGlmIChwb29sVHlwZSA9PT0gJ2F2ZycpIHtcbiAgICByZXR1cm5WYWx1ZSA9ICdhdmdWYWx1ZSc7XG4gIH1cblxuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCB4O1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgaGFsZkNSID0gdmVjMigwLjUsIDAuNSk7XG4gICAgY29uc3QgdmVjMiB4U2hhcGVDUiA9IHZlYzIoJHt4VGV4U2hhcGVSQ1sxXX0sICR7eFRleFNoYXBlUkNbMF19KTtcblxuICAgICR7SVNfTkFOX1NIQURFUl9GVU5DfVxuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgdmVjMiB5VGV4Q1IgPSBmbG9vcihnbF9GcmFnQ29vcmQueHkpO1xuXG4gICAgICAvLyBNYXAgZnJvbSAyRCAoeVRleFIsIHlUZXhDKSB0byAzRCAoeVIsIHlDLCBkMikuXG4gICAgICBmbG9hdCB5UiA9IHlUZXhDUi55O1xuICAgICAgZmxvYXQgeUMgPSBmbG9vcih5VGV4Q1IueCAvICR7ZGVwdGh9LjApO1xuICAgICAgZmxvYXQgZCA9IG1vZCh5VGV4Q1IueCwgJHtkZXB0aH0uMCk7XG5cbiAgICAgIHZlYzIgeFJDQ29ybmVyID0gdmVjMih5UiwgeUMpICogdmVjMigke3N0cmlkZX0sICR7c3RyaWRlfSkgLVxuICAgICAgICAgIHZlYzIoJHtwYWR9LjAsICR7cGFkfS4wKTtcbiAgICAgIGZsb2F0IHhSQ29ybmVyID0geFJDQ29ybmVyLng7XG4gICAgICBmbG9hdCB4Q0Nvcm5lciA9IHhSQ0Nvcm5lci55O1xuXG4gICAgICAvLyBtYXgvbWluIHgoPywgPywgZCkgdG8gZ2V0IHkoeVIsIHlDLCBkKS5cbiAgICAgIC8vID8gPSB0byBiZSBkZXRlcm1pbmVkXG4gICAgICBmbG9hdCBtaW5NYXhWYWx1ZSA9IDAuMDtcbiAgICAgIGZsb2F0IG1pbk1heFZhbHVlRm91bmQgPSAwLjA7XG4gICAgICBmbG9hdCBtaW5NYXhQb3NpdGlvbiA9IDAuMDtcbiAgICAgIGZsb2F0IGF2Z1ZhbHVlID0gMC4wO1xuXG4gICAgICBmb3IgKGZsb2F0IHdSID0gMC4wOyB3UiA8ICR7ZlNpemV9LjA7IHdSICs9IDEuMCkge1xuICAgICAgICBmbG9hdCB4UiA9IHhSQ29ybmVyICsgd1I7XG4gICAgICAgIGZsb2F0IHhUZXhSID0geFI7XG5cbiAgICAgICAgZm9yIChmbG9hdCB3QyA9IDAuMDsgd0MgPCAke2ZTaXplfS4wOyB3QyArPSAxLjApIHtcbiAgICAgICAgICBmbG9hdCB4QyA9IHhDQ29ybmVyICsgd0M7XG4gICAgICAgICAgZmxvYXQgeFRleEMgPSB4QyAqICR7ZGVwdGh9LjAgKyBkO1xuXG4gICAgICAgICAgdmVjMiB0ZXhDUiA9IHZlYzIoeFRleEMsIHhUZXhSKTtcblxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSByZXF1ZXN0ZWQgVVYgaXMgaW52YWxpZC5cbiAgICAgICAgICB2ZWMyIHV2ID0gKHRleENSICsgaGFsZkNSKSAvIHhTaGFwZUNSO1xuICAgICAgICAgIGJvb2wgbGVzc1RoYW5aZXJvID0gYW55KGxlc3NUaGFuKHV2LCB2ZWMyKDAsIDApKSk7XG4gICAgICAgICAgYm9vbCBncmVhdGVyVGhhbk9uZSA9IGFueShncmVhdGVyVGhhbih1diwgdmVjMigxLCAxKSkpO1xuICAgICAgICAgIGJvb2wgb3V0c2lkZSA9IGxlc3NUaGFuWmVybyB8fCBncmVhdGVyVGhhbk9uZTtcbiAgICAgICAgICBpZiAob3V0c2lkZSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZmxvYXQgdmFsdWUgPSB0ZXh0dXJlMkQoeCwgdXYpLnI7XG4gICAgICAgICAgaWYgKGlzTmFOKHZhbHVlKSkge1xuICAgICAgICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNCh2YWx1ZSwgMCwgMCwgMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICgke3Bvb2xUeXBlID09PSAnYXZnJ30pIHtcbiAgICAgICAgICAgIGF2Z1ZhbHVlICs9IHZhbHVlIC8gJHtmU2l6ZSAqIGZTaXplfS4wO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBJZiBhIG1pbiAvIG1heCB2YWx1ZSBoYXMgYWxyZWFkeSBiZWVuIGZvdW5kLCB1c2UgaXQuIElmIG5vdCwgdXNlXG4gICAgICAgICAgICAvLyB0aGUgY3VycmVudCB2YWx1ZS5cbiAgICAgICAgICAgIGZsb2F0IGN1cnJlbnRNaW5NYXhWYWx1ZSA9IG1peChcbiAgICAgICAgICAgICAgICB2YWx1ZSwgbWluTWF4VmFsdWUsIG1pbk1heFZhbHVlRm91bmQpO1xuICAgICAgICAgICAgaWYgKHZhbHVlICR7cG9vbFR5cGUgPT09ICdtaW4nID8gJzw9JyA6ICc+PSd9IGN1cnJlbnRNaW5NYXhWYWx1ZSkge1xuICAgICAgICAgICAgICBtaW5NYXhWYWx1ZSA9IHZhbHVlO1xuICAgICAgICAgICAgICBtaW5NYXhWYWx1ZUZvdW5kID0gMS4wO1xuICAgICAgICAgICAgICBpZiAoJHtjb21wdXRlUG9zaXRpb25zfSkge1xuICAgICAgICAgICAgICAgIG1pbk1heFBvc2l0aW9uID0gd1IgKiAke2ZTaXplfS4wICsgd0M7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoJHtyZXR1cm5WYWx1ZX0sIDAsIDAsIDApO1xuICAgIH1gO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcG9vbENvbW1vbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHg6IFdlYkdMVGV4dHVyZSxcbiAgICByZXN1bHQ6IFdlYkdMVGV4dHVyZSwgcmVzdWx0U2hhcGVSb3dDb2w6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgZ3BncHUuc2V0T3V0cHV0TWF0cml4VGV4dHVyZShcbiAgICAgIHJlc3VsdCwgcmVzdWx0U2hhcGVSb3dDb2xbMF0sIHJlc3VsdFNoYXBlUm93Q29sWzFdKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHgsICd4JywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2Uocm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIGNvbnN0IHZlYzIgYURpbUNSID0gdmVjMigke2NvbHVtbnN9LjAsICR7cm93c30uMCk7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHN1bSA9IDAuMDtcbiAgICAgIGZvciAoZmxvYXQgciA9IDAuMDsgciA8IGFEaW1DUi55OyByICs9IDEuMCkge1xuICAgICAgICBmb3IgKGZsb2F0IGMgPSAwLjA7IGMgPCBhRGltQ1IueDsgYyArPSAxLjApIHtcbiAgICAgICAgICB2ZWMyIHV2ID0gKHZlYzIoYywgcikgKyBoYWxmQ1IpIC8gYURpbUNSO1xuICAgICAgICAgIHN1bSArPSB0ZXh0dXJlMkQobWF0cml4QSwgdXYpLnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoc3VtLCAwLCAwLCAwKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlZHVjZVN1bShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZWR1Y2VTdW1Qcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICBhTnVtUm93czogbnVtYmVyLCBhTnVtQ29sczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgMSwgMSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVkdWNlU3VtUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkUmVkdWNlU3VtRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IGdwZ3B1ID0gbmV3IEdQR1BVQ29udGV4dCgpO1xuICBjb25zdCBwcm9ncmFtOiBXZWJHTFByb2dyYW0gPVxuICAgICAgZ3BncHUuY3JlYXRlUHJvZ3JhbShnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyb3dzLCBjb2x1bW5zKSk7XG4gIGNvbnN0IGFUZXh0dXJlOiBXZWJHTFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHJvd3MsIGNvbHVtbnMpO1xuICBjb25zdCByZXN1bHRUZXh0dXJlOiBXZWJHTFRleHR1cmUgPSBncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKDEsIDEpO1xuICBncGdwdS51cGxvYWRNYXRyaXhUb1RleHR1cmUoYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIGEpO1xuICByZWR1Y2VTdW0oZ3BncHUsIHByb2dyYW0sIGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCByZXN1bHRUZXh0dXJlKTtcbiAgY29uc3QgcmVzdWx0ID0gZ3BncHUuZG93bmxvYWRNYXRyaXhGcm9tVGV4dHVyZShyZXN1bHRUZXh0dXJlLCAxLCAxKVswXTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShhVGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZU1hdHJpeFRleHR1cmUocmVzdWx0VGV4dHVyZSk7XG4gIGdwZ3B1LmRlbGV0ZVByb2dyYW0ocHJvZ3JhbSk7XG4gIGdwZ3B1LmRpc3Bvc2UoKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0UmVsdVVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCByZXN1bHQgPSAodmFsdWUgPCAwLjAgPyAwLjAgOiB2YWx1ZSk7XG4gICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyZXN1bHQsIDAsIDAsIDApO1xuICBgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFJlbHVVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVsdShcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZWx1UHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHJlbHVQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkUmVsdURvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFJlbHVVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuaW1wb3J0ICogYXMgd2ViZ2xfdXRpbCBmcm9tICcuL3dlYmdsX3V0aWwnO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmVuZGVyUkdCU2hhZGVyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIGRlc3RpbmF0aW9uV2lkdGg6IG51bWJlcik6IFdlYkdMUHJvZ3JhbSB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyU291cmNlID0gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBzb3VyY2U7XG4gICAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuXG4gICAgY29uc3QgZmxvYXQgZGVzdGluYXRpb25XaWR0aCA9ICR7ZGVzdGluYXRpb25XaWR0aH0uMDtcbiAgICBjb25zdCBmbG9hdCBhID0gMS4wO1xuXG4gICAgdm9pZCBtYWluKCkge1xuICAgICAgZmxvYXQgeHIgPSBmbG9vcihyZXN1bHRVVi5zICogZGVzdGluYXRpb25XaWR0aCkgKiAzLjA7XG4gICAgICB2ZWMzIHggPSB4ciArIHZlYzMoMCwgMSwgMik7XG5cbiAgICAgIGZsb2F0IHNvdXJjZVdpZHRoID0gZGVzdGluYXRpb25XaWR0aCAqIDMuMDtcbiAgICAgIHZlYzMgdSA9ICh4ICsgMC41KSAvIHNvdXJjZVdpZHRoO1xuICAgICAgZmxvYXQgdiA9IDEuMCAtIHJlc3VsdFVWLnQ7XG5cbiAgICAgIGZsb2F0IHIgPSB0ZXh0dXJlMkQoc291cmNlLCB2ZWMyKHVbMF0sIHYpKS5yO1xuICAgICAgZmxvYXQgZyA9IHRleHR1cmUyRChzb3VyY2UsIHZlYzIodVsxXSwgdikpLnI7XG4gICAgICBmbG9hdCBiID0gdGV4dHVyZTJEKHNvdXJjZSwgdmVjMih1WzJdLCB2KSkucjtcblxuICAgICAgZ2xfRnJhZ0NvbG9yID0gdmVjNChyLCBnLCBiLCBhKTtcbiAgICB9YDtcblxuICByZXR1cm4gZ3BncHUuY3JlYXRlUHJvZ3JhbShmcmFnbWVudFNoYWRlclNvdXJjZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb0NhbnZhcyhcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCByZW5kZXJTaGFkZXI6IFdlYkdMUHJvZ3JhbSwgc291cmNlVGV4OiBXZWJHTFRleHR1cmUpIHtcbiAgd2ViZ2xfdXRpbC5iaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihncGdwdS5nbCk7XG4gIHJlbmRlclRvRnJhbWVidWZmZXIoZ3BncHUsIHJlbmRlclNoYWRlciwgc291cmNlVGV4KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvRnJhbWVidWZmZXIoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVuZGVyU2hhZGVyOiBXZWJHTFByb2dyYW0sIHNvdXJjZVRleDogV2ViR0xUZXh0dXJlKSB7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVuZGVyU2hhZGVyKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKHNvdXJjZVRleCwgJ3NvdXJjZScsIDApO1xuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHVuaWZvcm0gdmVjMiBpbnB1dERpbUNSO1xuICAgIHVuaWZvcm0gdmVjMiByZXN1bHREaW1DUjtcbiAgICB2YXJ5aW5nIHZlYzIgcmVzdWx0VVY7XG4gICAgY29uc3QgdmVjMiBoYWxmQ1IgPSB2ZWMyKDAuNSwgMC41KTtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgcmVzdWx0Q1IgPSBmbG9vcihyZXN1bHRVViAqIHJlc3VsdERpbUNSKTtcbiAgICAgIC8vIGluZGV4SW5GbGF0ID0gcm93ICogc3RyaWRlICsgY29sdW1uLCB3aGVyZSBzdHJpZGUgPT0gbnVtT3V0cHV0Q29sdW1uc1xuICAgICAgZmxvYXQgaW5kZXhJbkZsYXQgPSByZXN1bHRDUi55ICogcmVzdWx0RGltQ1IueCArIHJlc3VsdENSLng7XG5cbiAgICAgIHZlYzIgaW5wdXRDUiA9IHZlYzIoXG4gICAgICAgIG1vZChpbmRleEluRmxhdCwgaW5wdXREaW1DUi54KSwgLy8gY29sID0gaW5kZXhJbkZsYXQgJSBudW1JbnB1dENvbHVtbnNcbiAgICAgICAgZmxvb3IoaW5kZXhJbkZsYXQgLyBpbnB1dERpbUNSLngpIC8vIHJvdyA9IGluZGV4SW5GbGF0IC8gbnVtSW5wdXRDb2x1bW5zXG4gICAgICApICsgaGFsZkNSO1xuXG4gICAgICB2ZWMyIGlucHV0VVYgPSBpbnB1dENSIC8gaW5wdXREaW1DUjtcbiAgICAgIGdsX0ZyYWdDb2xvciA9IHRleHR1cmUyRChtYXRyaXhBLCBpbnB1dFVWKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc2hhcGUoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgcmVzaGFwZVByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIGFOdW1Sb3dzOiBudW1iZXIsIGFOdW1Db2xzOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlLFxuICAgIHJlc3VsdE51bVJvd3M6IG51bWJlciwgcmVzdWx0TnVtQ29sczogbnVtYmVyKSB7XG4gIGNvbnN0IGlucHV0U2l6ZSA9IGFOdW1Sb3dzICogYU51bUNvbHM7XG4gIGNvbnN0IG91dHB1dFNpemUgPSByZXN1bHROdW1Db2xzICogcmVzdWx0TnVtUm93cztcbiAgdXRpbC5hc3NlcnQoXG4gICAgICBpbnB1dFNpemUgPT09IG91dHB1dFNpemUsXG4gICAgICBgVGhlIGlucHV0IHNpemUgKCR7aW5wdXRTaXplfSkgYW5kIG91dHB1dCBzaXplICgke291dHB1dFNpemV9KSBgICtcbiAgICAgICAgICBgbXVzdCBtYXRjaGApO1xuXG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUocmVzdWx0LCByZXN1bHROdW1Sb3dzLCByZXN1bHROdW1Db2xzKTtcbiAgZ3BncHUuc2V0UHJvZ3JhbShyZXNoYXBlUHJvZ3JhbSk7XG4gIGdwZ3B1LnNldElucHV0TWF0cml4VGV4dHVyZShhLCAnbWF0cml4QScsIDApO1xuXG4gIGNvbnN0IGlucHV0RGltQ1JMb2NhdGlvbiA9IGdwZ3B1LmdldFVuaWZvcm1Mb2NhdGlvbignaW5wdXREaW1DUicpO1xuICBncGdwdS5nbC51bmlmb3JtMmYoaW5wdXREaW1DUkxvY2F0aW9uLCBhTnVtQ29scywgYU51bVJvd3MpO1xuXG4gIGNvbnN0IHJlc3VsdERpbUNSTG9jYXRpb24gPSBncGdwdS5nZXRVbmlmb3JtTG9jYXRpb24oJ3Jlc3VsdERpbUNSJyk7XG4gIGdwZ3B1LmdsLnVuaWZvcm0yZihyZXN1bHREaW1DUkxvY2F0aW9uLCByZXN1bHROdW1Db2xzLCByZXN1bHROdW1Sb3dzKTtcblxuICBncGdwdS5leGVjdXRlUHJvZ3JhbSgpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vY29udl91dGlsJztcblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB3ZWJnbF91dGlsIGZyb20gJy4vd2ViZ2xfdXRpbCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShcbiAgICBpbnB1dFNoYXBlUkNEOiBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sXG4gICAgb3V0cHV0RGltZW5zaW9uc1Jvd0NvbDogW251bWJlciwgbnVtYmVyXSwgYWxpZ25Db3JuZXJzOiBib29sZWFuKTogc3RyaW5nIHtcbiAgY29uc3QgZGVwdGggPSBpbnB1dFNoYXBlUkNEWzJdO1xuXG4gIGNvbnN0IGlucHV0VGV4U2hhcGVSQyA9IGNvbnZfdXRpbC5jb21wdXRlVGV4U2hhcGVGcm9tM0QoaW5wdXRTaGFwZVJDRCk7XG5cbiAgY29uc3QgZWZmZWN0aXZlSW5wdXRTaGFwZVJDRCA9IGFsaWduQ29ybmVycyA/XG4gICAgICBbaW5wdXRTaGFwZVJDRFswXSAtIDEsIGlucHV0U2hhcGVSQ0RbMV0gLSAxLCBkZXB0aF0gOlxuICAgICAgaW5wdXRTaGFwZVJDRDtcblxuICBjb25zdCBlZmZlY3RpdmVPdXRwdXRTaGFwZVJDRCA9IGFsaWduQ29ybmVycyA/XG4gICAgICBbb3V0cHV0RGltZW5zaW9uc1Jvd0NvbFswXSAtIDEsIG91dHB1dERpbWVuc2lvbnNSb3dDb2xbMV0gLSAxLCBkZXB0aF0gOlxuICAgICAgW291dHB1dERpbWVuc2lvbnNSb3dDb2xbMF0sIG91dHB1dERpbWVuc2lvbnNSb3dDb2xbMV0sIGRlcHRoXTtcblxuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcbiAgICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gICAgY29uc3QgdmVjMiBpbnB1dFNoYXBlQ1IgPSB2ZWMyKCR7aW5wdXRTaGFwZVJDRFsxXX0sICR7aW5wdXRTaGFwZVJDRFswXX0pO1xuICAgIGNvbnN0IHZlYzIgaW5wdXRTaGFwZVRleENSID0gdmVjMihcbiAgICAgICAgJHtpbnB1dFRleFNoYXBlUkNbMV19LCAke2lucHV0VGV4U2hhcGVSQ1swXX0pO1xuXG4gICAgY29uc3QgdmVjMiBlZmZlY3RpdmVJbnB1dE92ZXJPdXRwdXRSYXRpb0NSID0gdmVjMihcbiAgICAgICAgJHtlZmZlY3RpdmVJbnB1dFNoYXBlUkNEWzFdIC8gZWZmZWN0aXZlT3V0cHV0U2hhcGVSQ0RbMV19LFxuICAgICAgICAke2VmZmVjdGl2ZUlucHV0U2hhcGVSQ0RbMF0gLyBlZmZlY3RpdmVPdXRwdXRTaGFwZVJDRFswXX0pO1xuXG4gICAgZmxvYXQgc2FtcGxlSW5wdXQoZmxvYXQgY29sLCBmbG9hdCByb3csIGZsb2F0IGQpIHtcbiAgICAgIHZlYzIgdXYgPSAodmVjMihjb2wgKiAke2RlcHRofS4wICsgZCwgcm93KSArIGhhbGZDUikgLyBpbnB1dFNoYXBlVGV4Q1I7XG4gICAgICByZXR1cm4gdGV4dHVyZTJEKG1hdHJpeEEsIHV2KS5yO1xuICAgIH1cblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIHZlYzIgeVRleENSID0gZmxvb3IoZ2xfRnJhZ0Nvb3JkLnh5KTtcblxuICAgICAgLy8gTWFwIGZyb20gMkQgKHlUZXhSLCB5VGV4QykgdG8gM0QgKHlSLCB5QywgZCkuXG4gICAgICB2ZWMyIHlDUiA9IHZlYzIoZmxvb3IoeVRleENSLnggLyAke2RlcHRofS4wKSwgeVRleENSLnkpO1xuICAgICAgZmxvYXQgZCA9IG1vZCh5VGV4Q1IueCwgJHtkZXB0aH0uMCk7XG5cbiAgICAgIC8vIEZyYWN0aW9uYWwgc291cmNlIGluZGV4LlxuICAgICAgdmVjMiBzb3VyY2VGcmFjSW5kZXhDUiA9IHlDUiAqIGVmZmVjdGl2ZUlucHV0T3Zlck91dHB1dFJhdGlvQ1I7XG5cbiAgICAgIC8vIENvbXB1dGUgdGhlIGZvdXIgaW50ZWdlciBpbmRpY2VzLlxuICAgICAgdmVjMiBzb3VyY2VGbG9vckNSID0gZmxvb3Ioc291cmNlRnJhY0luZGV4Q1IpO1xuICAgICAgdmVjMiBzb3VyY2VDZWlsQ1IgPSBtaW4oaW5wdXRTaGFwZUNSIC0gMS4wLCBjZWlsKHNvdXJjZUZyYWNJbmRleENSKSk7XG5cbiAgICAgIGZsb2F0IHRvcExlZnQgPSBzYW1wbGVJbnB1dChzb3VyY2VGbG9vckNSWzBdLCBzb3VyY2VGbG9vckNSWzFdLCBkKTtcbiAgICAgIGZsb2F0IGJvdHRvbUxlZnQgPSBzYW1wbGVJbnB1dChzb3VyY2VGbG9vckNSWzBdLCBzb3VyY2VDZWlsQ1JbMV0sIGQpO1xuICAgICAgZmxvYXQgdG9wUmlnaHQgPSBzYW1wbGVJbnB1dChzb3VyY2VDZWlsQ1JbMF0sIHNvdXJjZUZsb29yQ1JbMV0sIGQpO1xuICAgICAgZmxvYXQgYm90dG9tUmlnaHQgPSBzYW1wbGVJbnB1dChzb3VyY2VDZWlsQ1JbMF0sIHNvdXJjZUNlaWxDUlsxXSwgZCk7XG5cbiAgICAgIHZlYzIgZnJhY0NSID0gc291cmNlRnJhY0luZGV4Q1IgLSBzb3VyY2VGbG9vckNSO1xuXG4gICAgICBmbG9hdCB0b3AgPSB0b3BMZWZ0ICsgKHRvcFJpZ2h0IC0gdG9wTGVmdCkgKiBmcmFjQ1JbMF07XG4gICAgICBmbG9hdCBib3R0b20gPSBib3R0b21MZWZ0ICsgKGJvdHRvbVJpZ2h0IC0gYm90dG9tTGVmdCkgKiBmcmFjQ1JbMF07XG4gICAgICBmbG9hdCBuZXdWYWx1ZSA9IHRvcCArIChib3R0b20gLSB0b3ApICogZnJhY0NSWzFdO1xuXG4gICAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KG5ld1ZhbHVlLCAwLjAsIDAuMCwgMC4wKTtcbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlc2l6ZUJpbGluZWFyKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHJlc2l6ZUJpbGluZWFyUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcmVzdWx0OiBXZWJHTFRleHR1cmUsIHJlc3VsdFNoYXBlUm93Q29sOiBbbnVtYmVyLCBudW1iZXJdKSB7XG4gIGdwZ3B1LnNldE91dHB1dE1hdHJpeFRleHR1cmUoXG4gICAgICByZXN1bHQsIHJlc3VsdFNoYXBlUm93Q29sWzBdLCByZXN1bHRTaGFwZVJvd0NvbFsxXSk7XG4gIGdwZ3B1LnNldFByb2dyYW0ocmVzaXplQmlsaW5lYXJQcm9ncmFtKTtcbiAgZ3BncHUuc2V0SW5wdXRNYXRyaXhUZXh0dXJlKGEsICdtYXRyaXhBJywgMCk7XG4gIGdwZ3B1LmV4ZWN1dGVQcm9ncmFtKCk7XG59IiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuLi9uZGFycmF5JztcblxuZXhwb3J0IHR5cGUgSW5wdXQgPSB7XG4gIG5hbWU6IHN0cmluZzsgYXJyYXk6IE5EQXJyYXk7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gbWFrZVNoYWRlcktleShpbnB1dHM6IE5EQXJyYXlbXSwgb3V0cHV0OiBOREFycmF5KTogc3RyaW5nIHtcbiAgY29uc3QgaW5zID0gaW5wdXRzLm1hcCh4ID0+IHguc2hhcGUgKyAnXycgKyB4LmdldFRleHR1cmVTaGFwZVJDKCkpO1xuICByZXR1cm4gaW5zLmpvaW4oJ18nKSArICdfJyArIG91dHB1dC5zaGFwZSArICdfJyArIG91dHB1dC5nZXRUZXh0dXJlU2hhcGVSQygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWFrZVNoYWRlcihcbiAgICBpbnB1dHM6IElucHV0W10sIG91dHB1dDogTkRBcnJheSwgdXNlckNvZGU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGlucHV0UHJlZml4U25pcHBldCA9XG4gICAgICBpbnB1dHMubWFwKHggPT4gYHVuaWZvcm0gc2FtcGxlcjJEICR7eC5uYW1lfTtgKS5qb2luKCdcXG4nKTtcbiAgY29uc3QgaW5wdXRTYW1wbGluZ1NuaXBwZXQgPVxuICAgICAgaW5wdXRzLm1hcCh4ID0+IGdldElucHV0U2FtcGxpbmdTbmlwcGV0KHgpKS5qb2luKCdcXG4nKTtcbiAgY29uc3Qgb3V0VGV4U2hhcGUgPSBvdXRwdXQuZ2V0VGV4dHVyZVNoYXBlUkMoKTtcbiAgY29uc3Qgb3V0cHV0U2FtcGxpbmdTbmlwcGV0ID1cbiAgICAgIGdldE91dHB1dFNhbXBsaW5nU25pcHBldChvdXRwdXQuc2hhcGUsIG91dFRleFNoYXBlKTtcbiAgY29uc3Qgc291cmNlID0gW1xuICAgIFNIQURFUl9QUkVGSVgsIGlucHV0UHJlZml4U25pcHBldCwgU0FNUExFXzJEX1NOSVBQRVQsIGlucHV0U2FtcGxpbmdTbmlwcGV0LFxuICAgIG91dHB1dFNhbXBsaW5nU25pcHBldCwgdXNlckNvZGVcbiAgXS5qb2luKCdcXG4nKTtcbiAgcmV0dXJuIHNvdXJjZTtcbn1cblxuZnVuY3Rpb24gZ2V0SW5wdXRTYW1wbGluZ1NuaXBwZXQoaW5wdXQ6IElucHV0KSB7XG4gIGNvbnN0IGFyciA9IGlucHV0LmFycmF5O1xuICBjb25zdCBzaGFwZSA9IGFyci5zaGFwZTtcbiAgY29uc3QgdGV4U2hhcGUgPSBhcnIuZ2V0VGV4dHVyZVNoYXBlUkMoc2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSk7XG4gIHN3aXRjaCAoc2hhcGUubGVuZ3RoKSB7XG4gICAgY2FzZSAyOlxuICAgICAgcmV0dXJuIGdldFNhbXBsZXIyRChpbnB1dC5uYW1lLCBzaGFwZSBhcyBbbnVtYmVyLCBudW1iZXJdLCB0ZXhTaGFwZSk7XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJHthcnIucmFua30tRCBpbnB1dCBzYW1wbGluZyBpcyBub3QgeWV0IHN1cHBvcnRlZGApO1xuICB9XG59XG5cbmZ1bmN0aW9uIGdldE91dHB1dFNhbXBsaW5nU25pcHBldChcbiAgICBvdXRTaGFwZTogbnVtYmVyW10sIG91dFRleFNoYXBlOiBbbnVtYmVyLCBudW1iZXJdKTogc3RyaW5nIHtcbiAgc3dpdGNoIChvdXRTaGFwZS5sZW5ndGgpIHtcbiAgICBjYXNlIDI6XG4gICAgICByZXR1cm4gZ2V0T3V0cHV0MkRDb29yZHMob3V0U2hhcGUgYXMgW251bWJlciwgbnVtYmVyXSwgb3V0VGV4U2hhcGUpO1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYCR7b3V0U2hhcGUubGVuZ3RofS1EIG91dHB1dCBzYW1wbGluZyBpcyBub3QgeWV0IHN1cHBvcnRlZGApO1xuICB9XG59XG5cbmNvbnN0IFNIQURFUl9QUkVGSVggPSBgXG4gIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgdmFyeWluZyB2ZWMyIHJlc3VsdFVWO1xuICBjb25zdCB2ZWMyIGhhbGZDUiA9IHZlYzIoMC41LCAwLjUpO1xuXG4gIHZvaWQgc2V0T3V0cHV0KGZsb2F0IHZhbCkge1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQodmFsLCAwLCAwLCAwKTtcbiAgfVxuYDtcblxuY29uc3QgU0FNUExFXzJEX1NOSVBQRVQgPSBgXG4gIGZsb2F0IHNhbXBsZTJEKHNhbXBsZXIyRCB0ZXh0dXJlLCBmbG9hdCB0ZXhOdW1SLCBmbG9hdCB0ZXhOdW1DLCBmbG9hdCBudW1DLFxuICAgICAgZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICBmbG9hdCBpbmRleCA9IGRvdCh2ZWMyKHJvdywgY29sKSwgdmVjMihudW1DLCAxLjApKTtcbiAgICBmbG9hdCB0ZXhSID0gZmxvb3IoaW5kZXggLyB0ZXhOdW1DKTtcbiAgICBmbG9hdCB0ZXhDID0gbW9kKGluZGV4LCB0ZXhOdW1DKTtcbiAgICB2ZWMyIHV2ID0gKHZlYzIodGV4QywgdGV4UikgKyBoYWxmQ1IpIC8gdmVjMih0ZXhOdW1DLCB0ZXhOdW1SKTtcbiAgICByZXR1cm4gdGV4dHVyZTJEKHRleHR1cmUsIHV2KS5yO1xuICB9XG5gO1xuXG5mdW5jdGlvbiBnZXRPdXRwdXQyRENvb3JkcyhcbiAgICBzaGFwZTogW251bWJlciwgbnVtYmVyXSwgdGV4U2hhcGU6IFtudW1iZXIsIG51bWJlcl0pIHtcbiAgaWYgKHV0aWwuYXJyYXlzRXF1YWwoc2hhcGUsIHRleFNoYXBlKSkge1xuICAgIHJldHVybiBgXG4gICAgICB2ZWMyIGdldE91dHB1dENvb3JkcygpIHtcbiAgICAgICAgcmV0dXJuIGZsb29yKGdsX0ZyYWdDb29yZC55eCk7XG4gICAgICB9XG4gICAgYDtcbiAgfVxuICByZXR1cm4gYFxuICAgIHZlYzIgZ2V0T3V0cHV0Q29vcmRzKCkge1xuICAgICAgdmVjMiByZXNUZXhSQyA9IGZsb29yKGdsX0ZyYWdDb29yZC55eCk7XG4gICAgICBmbG9hdCBpbmRleCA9IGRvdChyZXNUZXhSQywgdmVjMigke3RleFNoYXBlWzFdfS4wLCAxLjApKTtcbiAgICAgIGZsb2F0IHIgPSBmbG9vcihpbmRleCAvICR7c2hhcGVbMV19LjApO1xuICAgICAgZmxvYXQgYyA9IG1vZChpbmRleCwgJHtzaGFwZVsxXX0uMCk7XG4gICAgICByZXR1cm4gdmVjMihyLCBjKTtcbiAgICB9XG4gIGA7XG59XG5cbmZ1bmN0aW9uIGdldFNhbXBsZXIyRChcbiAgICB0ZXhOYW1lOiBzdHJpbmcsIHNoYXBlOiBbbnVtYmVyLCBudW1iZXJdLCB0ZXhTaGFwZTogW251bWJlciwgbnVtYmVyXSkge1xuICBjb25zdCBmdW5jTmFtZSA9ICdnZXQnICsgdGV4TmFtZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIHRleE5hbWUuc2xpY2UoMSk7XG4gIGNvbnN0IHRSID0gdGV4U2hhcGVbMF07XG4gIGNvbnN0IHRDID0gdGV4U2hhcGVbMV07XG4gIGlmICh1dGlsLmFycmF5c0VxdWFsKHNoYXBlLCB0ZXhTaGFwZSkpIHtcbiAgICByZXR1cm4gYFxuICAgICAgZmxvYXQgJHtmdW5jTmFtZX0oZmxvYXQgcm93LCBmbG9hdCBjb2wpIHtcbiAgICAgICAgdmVjMiB1diA9ICh2ZWMyKGNvbCwgcm93KSArIGhhbGZDUikgLyB2ZWMyKCR7dEN9LjAsICR7dFJ9LjApO1xuICAgICAgICByZXR1cm4gdGV4dHVyZTJEKCR7dGV4TmFtZX0sIHV2KS5yO1xuICAgICAgfVxuICAgIGA7XG4gIH1cbiAgcmV0dXJuIGBcbiAgICBmbG9hdCAke2Z1bmNOYW1lfShmbG9hdCByb3csIGZsb2F0IGNvbCkge1xuICAgICAgcmV0dXJuIHNhbXBsZTJEKCR7dGV4TmFtZX0sICR7dFJ9LjAsICR7dEN9LjAsICR7c2hhcGVbMV19LjAsIHJvdywgY29sKTtcbiAgICB9XG4gIGA7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuaW1wb3J0ICogYXMgdW5hcnlvcF9ncHUgZnJvbSAnLi91bmFyeW9wX2dwdSc7XG5cbmZ1bmN0aW9uIGdldFNpZ21vaWRVbmFyeU9wKCk6IHN0cmluZyB7XG4gIHJldHVybiAnZ2xfRnJhZ0NvbG9yID0gdmVjNCgxLjAgLyAoMS4wICsgZXhwKC0xLjAgKiB2YWx1ZSkpLCAwLCAwLCAwKTsnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2lnbW9pZEZyYWdtZW50U2hhZGVyU291cmNlKCk6IHN0cmluZyB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS5nZXRGcmFnbWVudFNoYWRlclNvdXJjZShnZXRTaWdtb2lkVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpZ21vaWQoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgc2lnbW9pZFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICB1bmFyeW9wX2dwdS51bmFyeU9wKGdwZ3B1LCBzaWdtb2lkUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNpZ21vaWREb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogRmxvYXQzMkFycmF5IHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LnVwbG9hZFVuYXJ5T3BEb3dubG9hZChcbiAgICAgIGEsIHJvd3MsIGNvbHVtbnMsIGdldFNpZ21vaWRVbmFyeU9wKCkpO1xufSIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtHUEdQVUNvbnRleHR9IGZyb20gJy4vZ3BncHVfY29udGV4dCc7XG5pbXBvcnQgKiBhcyB1bmFyeW9wX2dwdSBmcm9tICcuL3VuYXJ5b3BfZ3B1JztcblxuZnVuY3Rpb24gZ2V0U3RlcFVuYXJ5T3AoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcbiAgICBmbG9hdCByZXMgPSB2YWx1ZSA9PSB2YWx1ZSA/ICh2YWx1ZSA+IDAuMCA/IDEuMCA6IDAuMCkgOiB2YWx1ZTtcbiAgICBnbF9GcmFnQ29sb3IgPSB2ZWM0KHJlcywgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U3RlcFVuYXJ5T3AoKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzdGVwKFxuICAgIGdwZ3B1OiBHUEdQVUNvbnRleHQsIHN0ZXBQcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc3RlcFByb2dyYW0sIGEsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1cGxvYWRTdGVwRG93bmxvYWQoXG4gICAgYTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IEZsb2F0MzJBcnJheSB7XG4gIHJldHVybiB1bmFyeW9wX2dwdS51cGxvYWRVbmFyeU9wRG93bmxvYWQoYSwgcm93cywgY29sdW1ucywgZ2V0U3RlcFVuYXJ5T3AoKSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRVbnBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyKTogW251bWJlciwgbnVtYmVyXSB7XG4gIHJldHVybiBbY29sdW1ucywgcm93c107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKFxuICAgIG1hdHJpeFNpemU6IG51bWJlciwgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gbWF0cml4U2l6ZSAqIGNoYW5uZWxzUGVyVGV4dHVyZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldENvbG9yTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQoXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgcmV0dXJuIFtjb2x1bW5zICogNCwgcm93c107XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRNYXRyaXhTaXplRnJvbVVucGFja2VkQXJyYXlTaXplKFxuICAgIHVucGFja2VkU2l6ZTogbnVtYmVyLCBjaGFubmVsc1BlclRleHR1cmU6IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh1bnBhY2tlZFNpemUgJSBjaGFubmVsc1BlclRleHR1cmUgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd1bnBhY2tlZFNpemUgKCcgKyB1bnBhY2tlZFNpemUgKyAnKSBtdXN0IGJlIGEgbXVsdGlwbGUgb2YgJyArXG4gICAgICAgIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIH1cbiAgcmV0dXJuIHVucGFja2VkU2l6ZSAvIGNoYW5uZWxzUGVyVGV4dHVyZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZU1hdHJpeFRvVW5wYWNrZWRBcnJheShcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSwgdW5wYWNrZWRBcnJheTogRmxvYXQzMkFycmF5LFxuICAgIGNoYW5uZWxzUGVyVGV4dHVyZTogbnVtYmVyKSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9XG4gICAgICBnZXRVbnBhY2tlZEFycmF5U2l6ZUZyb21NYXRyaXhTaXplKG1hdHJpeC5sZW5ndGgsIGNoYW5uZWxzUGVyVGV4dHVyZSk7XG4gIGlmICh1bnBhY2tlZEFycmF5Lmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ3VucGFja2VkQXJyYXkgbGVuZ3RoICgnICsgdW5wYWNrZWRBcnJheS5sZW5ndGggK1xuICAgICAgICAnKSBtdXN0IGJlID49ICcgKyByZXF1aXJlZFNpemUpO1xuICB9XG4gIGxldCBkc3QgPSAwO1xuICBmb3IgKGxldCBzcmMgPSAwOyBzcmMgPCBtYXRyaXgubGVuZ3RoOyArK3NyYykge1xuICAgIHVucGFja2VkQXJyYXlbZHN0XSA9IG1hdHJpeFtzcmNdO1xuICAgIGRzdCArPSBjaGFubmVsc1BlclRleHR1cmU7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU1hdHJpeEZyb21VbnBhY2tlZEFycmF5KFxuICAgIHVucGFja2VkQXJyYXk6IEZsb2F0MzJBcnJheSwgbWF0cml4OiBGbG9hdDMyQXJyYXksXG4gICAgY2hhbm5lbHNQZXJUZXh0dXJlOiBudW1iZXIpIHtcbiAgY29uc3QgcmVxdWlyZWRTaXplID0gZ2V0TWF0cml4U2l6ZUZyb21VbnBhY2tlZEFycmF5U2l6ZShcbiAgICAgIHVucGFja2VkQXJyYXkubGVuZ3RoLCBjaGFubmVsc1BlclRleHR1cmUpO1xuICBpZiAobWF0cml4Lmxlbmd0aCA8IHJlcXVpcmVkU2l6ZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ21hdHJpeCBsZW5ndGggKCcgKyBtYXRyaXgubGVuZ3RoICsgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBsZXQgZHN0ID0gMDtcbiAgZm9yIChsZXQgc3JjID0gMDsgc3JjIDwgdW5wYWNrZWRBcnJheS5sZW5ndGg7IHNyYyArPSBjaGFubmVsc1BlclRleHR1cmUpIHtcbiAgICBtYXRyaXhbZHN0KytdID0gdW5wYWNrZWRBcnJheVtzcmNdO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICByZXR1cm4gW01hdGguY2VpbChjb2x1bW5zIC8gMiksIE1hdGguY2VpbChyb3dzIC8gMildO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IFt3LCBoXSA9IGdldFBhY2tlZE1hdHJpeFRleHR1cmVTaGFwZVdpZHRoSGVpZ2h0KHJvd3MsIGNvbHVtbnMpO1xuICByZXR1cm4gdyAqIGggKiA0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZW5jb2RlTWF0cml4VG9QYWNrZWRSR0JBKFxuICAgIG1hdHJpeDogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBwYWNrZWRSR0JBOiBGbG9hdDMyQXJyYXkpIHtcbiAgY29uc3QgcmVxdWlyZWRTaXplID0gZ2V0UGFja2VkUkdCQUFycmF5U2l6ZUZyb21NYXRyaXhTaGFwZShyb3dzLCBjb2x1bW5zKTtcbiAgaWYgKHBhY2tlZFJHQkEubGVuZ3RoIDwgcmVxdWlyZWRTaXplKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAncGFja2VkUkdCQSBsZW5ndGggKCcgKyBwYWNrZWRSR0JBLmxlbmd0aCArXG4gICAgICAgICcpIG11c3QgYmUgPj0gJyArIHJlcXVpcmVkU2l6ZSk7XG4gIH1cbiAgLypcbiAgICBVbnBhY2tlZCBtYXRyaXgsIHJvdy1tYWpvciBvcmRlciBpbiBGbG9hdDMyQXJyYXlbMTZdOiAgQSBCIEMgRFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFIEYgRyBIXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEkgSiBLIExcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTSBOIE8gUFxuXG4gICAgUGFja2VkIG1hdHJpeCwgMngyIFJHQkEzMiB0ZXh0dXJlIChtZW1vcnkgdmlldyk6ICAgICAgIEFCRUYgQ0RHSCBJSk1OIEtMT1BcblxuICAgIFBhY2tlZCBtYXRyaXgsIDJ4MiBSR0JBMzIgdGV4dHVyZSAobWF0cml4IHZpZXcpOiAgICAgICBBQnxDRFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFRnxHSFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLSstLVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJSnxLTFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNTnxPUFxuICAgKi9cbiAgY29uc3QgW3RleHR1cmVXaWR0aCwgdGV4dHVyZUhlaWdodF0gPVxuICAgICAgZ2V0UGFja2VkTWF0cml4VGV4dHVyZVNoYXBlV2lkdGhIZWlnaHQocm93cywgY29sdW1ucyk7XG4gIGNvbnN0IG9kZFdpZHRoID0gKGNvbHVtbnMgJSAyKSA9PT0gMTtcbiAgY29uc3Qgb2RkSGVpZ2h0ID0gKHJvd3MgJSAyKSA9PT0gMTtcbiAgY29uc3Qgd2lkdGhJbkZ1bGxCbG9ja3MgPSBNYXRoLmZsb29yKGNvbHVtbnMgLyAyKTtcbiAgY29uc3QgaGVpZ2h0SW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihyb3dzIC8gMik7XG5cbiAgLy8gbG9vcCBvdmVyIGZ1bGwgMngyIGJsb2Nrc1xuICB7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gKG9kZFdpZHRoID8gNCA6IDApO1xuICAgIGNvbnN0IG9uZVJvdyA9IGNvbHVtbnM7XG4gICAgbGV0IGRzdCA9IDA7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgY29uc3QgbWF0cml4U3JjUm93ID0gKGJsb2NrWSAqIDIgKiBjb2x1bW5zKTtcbiAgICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgICBjb25zdCBtYXRyaXhTcmNDb2wgPSBibG9ja1ggKiAyO1xuICAgICAgICBjb25zdCBzcmMgPSBtYXRyaXhTcmNSb3cgKyBtYXRyaXhTcmNDb2w7XG4gICAgICAgIHBhY2tlZFJHQkFbZHN0XSA9IG1hdHJpeFtzcmNdO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDFdID0gbWF0cml4W3NyYyArIDFdO1xuICAgICAgICBwYWNrZWRSR0JBW2RzdCArIDJdID0gbWF0cml4W3NyYyArIG9uZVJvd107XG4gICAgICAgIHBhY2tlZFJHQkFbZHN0ICsgM10gPSBtYXRyaXhbc3JjICsgb25lUm93ICsgMV07XG4gICAgICAgIGRzdCArPSA0O1xuICAgICAgfVxuICAgICAgZHN0ICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGRvd24gZmluYWwgb2RkIGNvbHVtblxuICBpZiAob2RkV2lkdGgpIHtcbiAgICBsZXQgc3JjID0gY29sdW1ucyAtIDE7XG4gICAgbGV0IGRzdCA9ICh0ZXh0dXJlV2lkdGggLSAxKSAqIDQ7XG4gICAgY29uc3Qgc3JjU3RyaWRlID0gMiAqIGNvbHVtbnM7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gdGV4dHVyZVdpZHRoICogNDtcbiAgICBmb3IgKGxldCBibG9ja1kgPSAwOyBibG9ja1kgPCBoZWlnaHRJbkZ1bGxCbG9ja3M7ICsrYmxvY2tZKSB7XG4gICAgICBwYWNrZWRSR0JBW2RzdF0gPSBtYXRyaXhbc3JjXTtcbiAgICAgIHBhY2tlZFJHQkFbZHN0ICsgMl0gPSBtYXRyaXhbc3JjICsgY29sdW1uc107XG4gICAgICBzcmMgKz0gc3JjU3RyaWRlO1xuICAgICAgZHN0ICs9IGRzdFN0cmlkZTtcbiAgICB9XG4gIH1cblxuICAvLyBsb29wIGFjcm9zcyBmaW5hbCByb3dcbiAgaWYgKG9kZEhlaWdodCkge1xuICAgIGxldCBzcmMgPSAocm93cyAtIDEpICogY29sdW1ucztcbiAgICBsZXQgZHN0ID0gKHRleHR1cmVIZWlnaHQgLSAxKSAqIHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgZm9yIChsZXQgYmxvY2tYID0gMDsgYmxvY2tYIDwgd2lkdGhJbkZ1bGxCbG9ja3M7ICsrYmxvY2tYKSB7XG4gICAgICBwYWNrZWRSR0JBW2RzdCsrXSA9IG1hdHJpeFtzcmMrK107XG4gICAgICBwYWNrZWRSR0JBW2RzdCsrXSA9IG1hdHJpeFtzcmMrK107XG4gICAgICBkc3QgKz0gMjtcbiAgICB9XG4gIH1cblxuICAvLyBmaWxsIGluIGJvdHRvbS1yaWdodCB0ZXhlbFxuICBpZiAob2RkV2lkdGggJiYgb2RkSGVpZ2h0KSB7XG4gICAgcGFja2VkUkdCQVtwYWNrZWRSR0JBLmxlbmd0aCAtIDRdID0gbWF0cml4W21hdHJpeC5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHJldHVybiBwYWNrZWRSR0JBO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlTWF0cml4RnJvbVBhY2tlZFJHQkEoXG4gICAgcGFja2VkUkdCQTogRmxvYXQzMkFycmF5LCByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlcixcbiAgICBtYXRyaXg6IEZsb2F0MzJBcnJheSk6IEZsb2F0MzJBcnJheSB7XG4gIGNvbnN0IHJlcXVpcmVkU2l6ZSA9IHJvd3MgKiBjb2x1bW5zO1xuICBpZiAocmVxdWlyZWRTaXplIDwgbWF0cml4Lmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ21hdHJpeCBsZW5ndGggKCcgKyBtYXRyaXgubGVuZ3RoICsgJykgbXVzdCBiZSA+PSAnICsgcmVxdWlyZWRTaXplKTtcbiAgfVxuICBjb25zdCBvZGRXaWR0aCA9IChjb2x1bW5zICUgMikgPT09IDE7XG4gIGNvbnN0IG9kZEhlaWdodCA9IChyb3dzICUgMikgPT09IDE7XG4gIGNvbnN0IHdpZHRoSW5GdWxsQmxvY2tzID0gTWF0aC5mbG9vcihjb2x1bW5zIC8gMik7XG4gIGNvbnN0IGhlaWdodEluRnVsbEJsb2NrcyA9IE1hdGguZmxvb3Iocm93cyAvIDIpO1xuICBjb25zdCBbdGV4dHVyZVdpZHRoLCB0ZXh0dXJlSGVpZ2h0XSA9XG4gICAgICBnZXRQYWNrZWRNYXRyaXhUZXh0dXJlU2hhcGVXaWR0aEhlaWdodChyb3dzLCBjb2x1bW5zKTtcblxuICAvLyBsb29wIG92ZXIgZnVsbCAyeDIgYmxvY2tzXG4gIHtcbiAgICBjb25zdCBzcmNTdHJpZGUgPSBvZGRXaWR0aCA/IDQgOiAwO1xuICAgIGNvbnN0IGRzdFN0cmlkZSA9IGNvbHVtbnMgKyAob2RkV2lkdGggPyAxIDogMCk7XG4gICAgbGV0IHNyYyA9IDA7XG4gICAgbGV0IGRzdFJvdzEgPSAwO1xuICAgIGxldCBkc3RSb3cyID0gY29sdW1ucztcbiAgICBmb3IgKGxldCBibG9ja1kgPSAwOyBibG9ja1kgPCBoZWlnaHRJbkZ1bGxCbG9ja3M7ICsrYmxvY2tZKSB7XG4gICAgICBmb3IgKGxldCBibG9ja1ggPSAwOyBibG9ja1ggPCB3aWR0aEluRnVsbEJsb2NrczsgKytibG9ja1gpIHtcbiAgICAgICAgbWF0cml4W2RzdFJvdzErK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzErK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzIrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgICAgbWF0cml4W2RzdFJvdzIrK10gPSBwYWNrZWRSR0JBW3NyYysrXTtcbiAgICAgIH1cbiAgICAgIHNyYyArPSBzcmNTdHJpZGU7XG4gICAgICBkc3RSb3cxICs9IGRzdFN0cmlkZTtcbiAgICAgIGRzdFJvdzIgKz0gZHN0U3RyaWRlO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvb3AgZG93biBmaW5hbCBjb2x1bW5cbiAgaWYgKG9kZFdpZHRoKSB7XG4gICAgbGV0IHNyYyA9ICh0ZXh0dXJlV2lkdGggLSAxKSAqIDQ7XG4gICAgbGV0IGRzdCA9IGNvbHVtbnMgLSAxO1xuICAgIGNvbnN0IHNyY1N0cmlkZSA9IHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgY29uc3QgZHN0U3RyaWRlID0gMiAqIGNvbHVtbnM7XG4gICAgZm9yIChsZXQgYmxvY2tZID0gMDsgYmxvY2tZIDwgaGVpZ2h0SW5GdWxsQmxvY2tzOyArK2Jsb2NrWSkge1xuICAgICAgbWF0cml4W2RzdF0gPSBwYWNrZWRSR0JBW3NyY107XG4gICAgICBtYXRyaXhbZHN0ICsgY29sdW1uc10gPSBwYWNrZWRSR0JBW3NyYyArIDJdO1xuICAgICAgc3JjICs9IHNyY1N0cmlkZTtcbiAgICAgIGRzdCArPSBkc3RTdHJpZGU7XG4gICAgfVxuICB9XG5cbiAgLy8gbG9vcCBhY3Jvc3MgZmluYWwgcm93XG4gIGlmIChvZGRIZWlnaHQpIHtcbiAgICBsZXQgc3JjID0gKHRleHR1cmVIZWlnaHQgLSAxKSAqIHRleHR1cmVXaWR0aCAqIDQ7XG4gICAgbGV0IGRzdCA9IChyb3dzIC0gMSkgKiBjb2x1bW5zO1xuICAgIGZvciAobGV0IGJsb2NrWCA9IDA7IGJsb2NrWCA8IHdpZHRoSW5GdWxsQmxvY2tzOyArK2Jsb2NrWCkge1xuICAgICAgbWF0cml4W2RzdCsrXSA9IHBhY2tlZFJHQkFbc3JjKytdO1xuICAgICAgbWF0cml4W2RzdCsrXSA9IHBhY2tlZFJHQkFbc3JjKytdO1xuICAgICAgc3JjICs9IDI7XG4gICAgfVxuICB9XG5cbiAgLy8gZmlsbCBpbiBib3R0b20tcmlnaHQgY2VsbFxuICBpZiAob2RkV2lkdGggJiYgb2RkSGVpZ2h0KSB7XG4gICAgbWF0cml4W21hdHJpeC5sZW5ndGggLSAxXSA9IHBhY2tlZFJHQkFbcGFja2VkUkdCQS5sZW5ndGggLSA0XTtcbiAgfVxuXG4gIHJldHVybiBtYXRyaXg7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7R1BHUFVDb250ZXh0fSBmcm9tICcuL2dwZ3B1X2NvbnRleHQnO1xuXG5leHBvcnQgY2xhc3MgVGV4dHVyZU1hbmFnZXIge1xuICBwcml2YXRlIG51bVVzZWRUZXh0dXJlcyA9IDA7XG4gIHByaXZhdGUgbnVtRnJlZVRleHR1cmVzID0gMDtcbiAgcHJpdmF0ZSBmcmVlVGV4dHVyZXM6IHtbc2hhcGU6IHN0cmluZ106IFdlYkdMVGV4dHVyZVtdfSA9IHt9O1xuICBwcml2YXRlIGxvZ0VuYWJsZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSB1c2VkVGV4dHVyZUNvdW50OiB7W3NoYXBlOiBzdHJpbmddOiBudW1iZXJ9ID0ge307XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBncGdwdTogR1BHUFVDb250ZXh0KSB7fVxuXG4gIGFjcXVpcmVUZXh0dXJlKHNoYXBlUkM6IFtudW1iZXIsIG51bWJlcl0pOiBXZWJHTFRleHR1cmUge1xuICAgIGNvbnN0IHNoYXBlS2V5ID0gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZVJDKTtcbiAgICBpZiAoIShzaGFwZUtleSBpbiB0aGlzLmZyZWVUZXh0dXJlcykpIHtcbiAgICAgIHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XSA9IFtdO1xuICAgIH1cbiAgICBpZiAoIShzaGFwZUtleSBpbiB0aGlzLnVzZWRUZXh0dXJlQ291bnQpKSB7XG4gICAgICB0aGlzLnVzZWRUZXh0dXJlQ291bnRbc2hhcGVLZXldID0gMDtcbiAgICB9XG4gICAgdGhpcy51c2VkVGV4dHVyZUNvdW50W3NoYXBlS2V5XSsrO1xuXG4gICAgaWYgKHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlS2V5XS5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLm51bUZyZWVUZXh0dXJlcy0tO1xuICAgICAgdGhpcy5udW1Vc2VkVGV4dHVyZXMrKztcbiAgICAgIHRoaXMubG9nKCk7XG4gICAgICByZXR1cm4gdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldLnNoaWZ0KCkhO1xuICAgIH1cbiAgICB0aGlzLm51bVVzZWRUZXh0dXJlcysrO1xuICAgIHRoaXMubG9nKCk7XG5cbiAgICByZXR1cm4gdGhpcy5ncGdwdS5jcmVhdGVNYXRyaXhUZXh0dXJlKHNoYXBlUkNbMF0sIHNoYXBlUkNbMV0pO1xuICB9XG5cbiAgcmVsZWFzZVRleHR1cmUodGV4dHVyZTogV2ViR0xUZXh0dXJlLCBzaGFwZTogW251bWJlciwgbnVtYmVyXSk6IHZvaWQge1xuICAgIGNvbnN0IHNoYXBlS2V5ID0gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZSk7XG4gICAgaWYgKCEoc2hhcGVLZXkgaW4gdGhpcy5mcmVlVGV4dHVyZXMpKSB7XG4gICAgICB0aGlzLmZyZWVUZXh0dXJlc1tzaGFwZUtleV0gPSBbXTtcbiAgICB9XG4gICAgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVLZXldLnB1c2godGV4dHVyZSk7XG4gICAgdGhpcy5udW1GcmVlVGV4dHVyZXMrKztcbiAgICB0aGlzLm51bVVzZWRUZXh0dXJlcy0tO1xuICAgIHRoaXMudXNlZFRleHR1cmVDb3VudFtzaGFwZUtleV0tLTtcbiAgICB0aGlzLmxvZygpO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2coKSB7XG4gICAgaWYgKCF0aGlzLmxvZ0VuYWJsZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdG90YWwgPSB0aGlzLm51bUZyZWVUZXh0dXJlcyArIHRoaXMubnVtVXNlZFRleHR1cmVzO1xuICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAnRnJlZS9Vc2VkJywgdGhpcy5udW1GcmVlVGV4dHVyZXMgKyAnIC8gJyArIHRoaXMubnVtVXNlZFRleHR1cmVzLFxuICAgICAgICBgKCR7dG90YWx9KWApO1xuICB9XG5cbiAgZ2V0TnVtVXNlZFRleHR1cmVzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubnVtVXNlZFRleHR1cmVzO1xuICB9XG5cbiAgZ2V0TnVtRnJlZVRleHR1cmVzKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMubnVtRnJlZVRleHR1cmVzO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBmb3IgKGNvbnN0IHNoYXBlIGluIHRoaXMuZnJlZVRleHR1cmVzKSB7XG4gICAgICBpZiAodGhpcy5mcmVlVGV4dHVyZXMuaGFzT3duUHJvcGVydHkoc2hhcGUpKSB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5mcmVlVGV4dHVyZXNbc2hhcGVdLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgdGhpcy5ncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKHRoaXMuZnJlZVRleHR1cmVzW3NoYXBlXVtpXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0S2V5RnJvbVRleHR1cmVTaGFwZShzaGFwZVJvd3NDb2w6IFtudW1iZXIsIG51bWJlcl0pOiBzdHJpbmcge1xuICByZXR1cm4gc2hhcGVSb3dzQ29sWzBdICsgJ18nICsgc2hhcGVSb3dzQ29sWzFdO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcbmltcG9ydCAqIGFzIHVuYXJ5b3BfZ3B1IGZyb20gJy4vdW5hcnlvcF9ncHUnO1xuXG4vKipcbiAqIFNpbmVcbiAqL1xuZnVuY3Rpb24gZ2V0U2luVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoc2luKHZhbHVlKSwgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTaW5GcmFnbWVudFNoYWRlclNvdXJjZSgpOiBzdHJpbmcge1xuICByZXR1cm4gdW5hcnlvcF9ncHUuZ2V0RnJhZ21lbnRTaGFkZXJTb3VyY2UoZ2V0U2luVW5hcnlPcCgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNpbihcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCBzaW5Qcm9ncmFtOiBXZWJHTFByb2dyYW0sIGE6IFdlYkdMVGV4dHVyZSxcbiAgICByb3dzOiBudW1iZXIsIGNvbHVtbnM6IG51bWJlciwgcmVzdWx0OiBXZWJHTFRleHR1cmUpIHtcbiAgdW5hcnlvcF9ncHUudW5hcnlPcChncGdwdSwgc2luUHJvZ3JhbSwgYSwgcm93cywgY29sdW1ucywgcmVzdWx0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFNpbkRvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFNpblVuYXJ5T3AoKSk7XG59XG5cbi8qKlxuICogVGFuaFxuICovXG5mdW5jdGlvbiBnZXRUYW5oVW5hcnlPcCgpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIGZsb2F0IGUyeCA9IGV4cCgtMi4wICogdmFsdWUpO1xuICAgIGdsX0ZyYWdDb2xvciA9IHZlYzQoKDEuMCAtIGUyeCkgLyAoMS4wICsgZTJ4KSwgMCwgMCwgMCk7XG4gIGA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUYW5oRnJhZ21lbnRTaGFkZXJTb3VyY2UoKTogc3RyaW5nIHtcbiAgcmV0dXJuIHVuYXJ5b3BfZ3B1LmdldEZyYWdtZW50U2hhZGVyU291cmNlKGdldFRhbmhVbmFyeU9wKCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdGFuaChcbiAgICBncGdwdTogR1BHUFVDb250ZXh0LCB0YW5oUHJvZ3JhbTogV2ViR0xQcm9ncmFtLCBhOiBXZWJHTFRleHR1cmUsXG4gICAgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIsIHJlc3VsdDogV2ViR0xUZXh0dXJlKSB7XG4gIHVuYXJ5b3BfZ3B1LnVuYXJ5T3AoZ3BncHUsIHRhbmhQcm9ncmFtLCBhLCByb3dzLCBjb2x1bW5zLCByZXN1bHQpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdXBsb2FkVGFuaERvd25sb2FkKFxuICAgIGE6IEZsb2F0MzJBcnJheSwgcm93czogbnVtYmVyLCBjb2x1bW5zOiBudW1iZXIpOiBGbG9hdDMyQXJyYXkge1xuICByZXR1cm4gdW5hcnlvcF9ncHUudXBsb2FkVW5hcnlPcERvd25sb2FkKGEsIHJvd3MsIGNvbHVtbnMsIGdldFRhbmhVbmFyeU9wKCkpO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dQR1BVQ29udGV4dH0gZnJvbSAnLi9ncGdwdV9jb250ZXh0JztcblxuZXhwb3J0IGZ1bmN0aW9uIGdldEZyYWdtZW50U2hhZGVyU291cmNlKHJlc3VsdE9wOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYFxuICAgIHByZWNpc2lvbiBoaWdocCBmbG9hdDtcbiAgICB1bmlmb3JtIHNhbXBsZXIyRCBtYXRyaXhBO1xuICAgIHZhcnlpbmcgdmVjMiByZXN1bHRVVjtcblxuICAgIHZvaWQgbWFpbigpIHtcbiAgICAgIGZsb2F0IHZhbHVlID0gdGV4dHVyZTJEKG1hdHJpeEEsIHJlc3VsdFVWKS5yO1xuICAgICAgJHtyZXN1bHRPcH1cbiAgICB9YDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYXJ5T3AoXG4gICAgZ3BncHU6IEdQR1BVQ29udGV4dCwgdW5hcnlPcFByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYTogV2ViR0xUZXh0dXJlLFxuICAgIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLCByZXN1bHQ6IFdlYkdMVGV4dHVyZSkge1xuICBncGdwdS5zZXRPdXRwdXRNYXRyaXhUZXh0dXJlKHJlc3VsdCwgcm93cywgY29sdW1ucyk7XG4gIGdwZ3B1LnNldFByb2dyYW0odW5hcnlPcFByb2dyYW0pO1xuICBncGdwdS5zZXRJbnB1dE1hdHJpeFRleHR1cmUoYSwgJ21hdHJpeEEnLCAwKTtcbiAgZ3BncHUuZXhlY3V0ZVByb2dyYW0oKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVwbG9hZFVuYXJ5T3BEb3dubG9hZChcbiAgICBhOiBGbG9hdDMyQXJyYXksIHJvd3M6IG51bWJlciwgY29sdW1uczogbnVtYmVyLFxuICAgIHJlc3VsdE9wOiBzdHJpbmcpOiBGbG9hdDMyQXJyYXkge1xuICBjb25zdCBncGdwdSA9IG5ldyBHUEdQVUNvbnRleHQoKTtcbiAgY29uc3QgZnJhZ21lbnRTaGFkZXJTcmMgPSBnZXRGcmFnbWVudFNoYWRlclNvdXJjZShyZXN1bHRPcCk7XG4gIGNvbnN0IHByb2dyYW06IFdlYkdMUHJvZ3JhbSA9IGdwZ3B1LmNyZWF0ZVByb2dyYW0oZnJhZ21lbnRTaGFkZXJTcmMpO1xuICBjb25zdCBhVGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgY29uc3QgcmVzdWx0VGV4dHVyZTogV2ViR0xUZXh0dXJlID0gZ3BncHUuY3JlYXRlTWF0cml4VGV4dHVyZShyb3dzLCBjb2x1bW5zKTtcbiAgZ3BncHUudXBsb2FkTWF0cml4VG9UZXh0dXJlKGFUZXh0dXJlLCByb3dzLCBjb2x1bW5zLCBhKTtcbiAgdW5hcnlPcChncGdwdSwgcHJvZ3JhbSwgYVRleHR1cmUsIHJvd3MsIGNvbHVtbnMsIHJlc3VsdFRleHR1cmUpO1xuICBjb25zdCByZXN1bHQgPSBncGdwdS5kb3dubG9hZE1hdHJpeEZyb21UZXh0dXJlKHJlc3VsdFRleHR1cmUsIHJvd3MsIGNvbHVtbnMpO1xuICBncGdwdS5kZWxldGVNYXRyaXhUZXh0dXJlKGFUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlTWF0cml4VGV4dHVyZShyZXN1bHRUZXh0dXJlKTtcbiAgZ3BncHUuZGVsZXRlUHJvZ3JhbShwcm9ncmFtKTtcbiAgZ3BncHUuZGlzcG9zZSgpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5sZXQgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xubGV0IFdFQkdMMl9FTkFCTEVEOiBib29sZWFufHVuZGVmaW5lZCA9IG51bGwhO1xubGV0IE1BWF9URVhUVVJFX1NJWkU6IG51bWJlciA9IG51bGwhO1xuXG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uLy4uL3V0aWwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMge1xuICBhbHBoYT86IGJvb2xlYW47XG4gIGFudGlhbGlhcz86IGJvb2xlYW47XG4gIHByZW11bHRpcGxpZWRBbHBoYT86IGJvb2xlYW47XG4gIHByZXNlcnZlRHJhd2luZ0J1ZmZlcj86IGJvb2xlYW47XG4gIGRlcHRoPzogYm9vbGVhbjtcbiAgc3RlbmNpbD86IGJvb2xlYW47XG4gIGZhaWxJZk1ham9yUGVyZm9ybWFuY2VDYXZlYXQ/OiBib29sZWFuO1xufVxuXG4vKiogQGhpZGRlbiAqL1xuZXhwb3J0IGNvbnN0IElTX05BTl9TSEFERVJfRlVOQyA9IGBcbmJvb2wgaXNOYU4oZmxvYXQgdmFsKSB7XG4gIHJldHVybiB2YWwgPT0gdmFsID8gZmFsc2UgOiB0cnVlO1xufVxuYDtcblxuZXhwb3J0IGludGVyZmFjZSBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uIHsgbG9zZUNvbnRleHQoKTogdm9pZDsgfVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0KGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOlxuICAgIFdlYkdMUmVuZGVyaW5nQ29udGV4dCB7XG4gIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICBjYW52YXMud2lkdGggPSAxO1xuICBjYW52YXMuaGVpZ2h0ID0gMTtcbiAgcmV0dXJuIGNyZWF0ZVdlYkdMUmVuZGVyaW5nQ29udGV4dEZyb21DYW52YXMoY2FudmFzLCBhdHRyaWJ1dGVzKTtcbn1cblxuLyoqXG4gKiBGb3JjZSB0aGUgbGlicmFyeSB0byBwcmVmZXIgV2ViR0wgMS4wIGluc3RlYWQgb2YgV2ViR0wgMi4wIGV2ZW4gd2hlbiBXZWJHTFxuICogMi4wIGlzIGF2YWlsYWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZWZlcldlYkdMMSgpIHtcbiAgVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSA9IGZhbHNlO1xuICBXRUJHTDJfRU5BQkxFRCA9IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBQcmVmZXIgV2ViR0wgMi4wIHRvIFdlYkdMIDEuMC4gVGhpcyBpcyB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZmVyV2ViR0wyKCkge1xuICBVU0VfV0VCR0wyX1dIRU5fQVZBSUxBQkxFID0gdHJ1ZTtcbiAgV0VCR0wyX0VOQUJMRUQgPSB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1dlYkdMMkVuYWJsZWQoKSB7XG4gIGlmICghVVNFX1dFQkdMMl9XSEVOX0FWQUlMQUJMRSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGlmIChXRUJHTDJfRU5BQkxFRCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgdGVtcENhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNvbnN0IGdsID0gdGVtcENhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbDInKTtcbiAgICBpZiAoZ2wgIT0gbnVsbCkge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSB0cnVlO1xuXG4gICAgICBjb25zdCBsb3NlQ29udGV4dEV4dGVuc2lvbiA9XG4gICAgICAgICAgZ2V0RXh0ZW5zaW9uT3JUaHJvdyhcbiAgICAgICAgICAgICAgZ2wgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0LCAnV0VCR0xfbG9zZV9jb250ZXh0JykgYXNcbiAgICAgICAgICBXZWJHTExvc2VDb250ZXh0RXh0ZW5zaW9uO1xuICAgICAgbG9zZUNvbnRleHRFeHRlbnNpb24ubG9zZUNvbnRleHQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgV0VCR0wyX0VOQUJMRUQgPSBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIFdFQkdMMl9FTkFCTEVEO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV2ViR0xSZW5kZXJpbmdDb250ZXh0RnJvbUNhbnZhcyhcbiAgICBjYW52YXM6IEhUTUxDYW52YXNFbGVtZW50LFxuICAgIGF0dHJpYnV0ZXM6IFdlYkdMQ29udGV4dEF0dHJpYnV0ZXMpOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQge1xuICBsZXQgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dDtcbiAgaWYgKGlzV2ViR0wyRW5hYmxlZCgpKSB7XG4gICAgZ2wgPSBjYW52YXMuZ2V0Q29udGV4dCgnd2ViZ2wyJywgYXR0cmlidXRlcykgYXMgV2ViR0xSZW5kZXJpbmdDb250ZXh0O1xuICB9IGVsc2Uge1xuICAgIGdsID0gKGNhbnZhcy5nZXRDb250ZXh0KCd3ZWJnbCcsIGF0dHJpYnV0ZXMpIHx8XG4gICAgICAgICAgY2FudmFzLmdldENvbnRleHQoJ2V4cGVyaW1lbnRhbC13ZWJnbCcsIGF0dHJpYnV0ZXMpKSBhc1xuICAgICAgICBXZWJHTFJlbmRlcmluZ0NvbnRleHQ7XG4gIH1cblxuICBpZiAoZ2wgPT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcignVGhpcyBicm93c2VyIGRvZXMgbm90IHN1cHBvcnQgV2ViR0wuJyk7XG4gIH1cbiAgcmV0dXJuIGdsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2FsbEFuZENoZWNrPFQ+KGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZ1bmM6ICgpID0+IFQpOiBUIHtcbiAgY29uc3QgcmV0dXJuVmFsdWUgPSBmdW5jKCk7XG4gIGNoZWNrV2ViR0xFcnJvcihnbCk7XG4gIHJldHVybiByZXR1cm5WYWx1ZTtcbn1cblxubGV0IHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCA9IGZhbHNlO1xuXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRGVidWdXZWJHTEVycm9yQ2hlY2tpbmcoZW5hYmxlZDogYm9vbGVhbikge1xuICB3ZWJHTERlYnVnRXJyb3JDaGVja2luZ0VuYWJsZWQgPSBlbmFibGVkO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tXZWJHTEVycm9yKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpIHtcbiAgaWYgKHdlYkdMRGVidWdFcnJvckNoZWNraW5nRW5hYmxlZCkge1xuICAgIGNvbnN0IGVycm9yID0gZ2wuZ2V0RXJyb3IoKTtcbiAgICBpZiAoZXJyb3IgIT09IGdsLk5PX0VSUk9SKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlYkdMIEVycm9yOiAnICsgZ2V0V2ViR0xFcnJvck1lc3NhZ2UoZ2wsIGVycm9yKSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRXZWJHTEVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5OT19FUlJPUjpcbiAgICAgIHJldHVybiAnTk9fRVJST1InO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9FTlVNOlxuICAgICAgcmV0dXJuICdJTlZBTElEX0VOVU0nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9WQUxVRTpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9WQUxVRSc7XG4gICAgY2FzZSBnbC5JTlZBTElEX09QRVJBVElPTjpcbiAgICAgIHJldHVybiAnSU5WQUxJRF9PUEVSQVRJT04nO1xuICAgIGNhc2UgZ2wuSU5WQUxJRF9GUkFNRUJVRkZFUl9PUEVSQVRJT046XG4gICAgICByZXR1cm4gJ0lOVkFMSURfRlJBTUVCVUZGRVJfT1BFUkFUSU9OJztcbiAgICBjYXNlIGdsLk9VVF9PRl9NRU1PUlk6XG4gICAgICByZXR1cm4gJ09VVF9PRl9NRU1PUlknO1xuICAgIGNhc2UgZ2wuQ09OVEVYVF9MT1NUX1dFQkdMOlxuICAgICAgcmV0dXJuICdDT05URVhUX0xPU1RfV0VCR0wnO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gJ1Vua25vd24gZXJyb3IgY29kZSAnICsgc3RhdHVzO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHRlbnNpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGV4dGVuc2lvbk5hbWU6IHN0cmluZyk6IHt9IHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPHt9PihcbiAgICAgIGdsLCAoKSA9PiBnbC5nZXRFeHRlbnNpb24oZXh0ZW5zaW9uTmFtZSksXG4gICAgICAnRXh0ZW5zaW9uIFwiJyArIGV4dGVuc2lvbk5hbWUgKyAnXCIgbm90IHN1cHBvcnRlZCBvbiB0aGlzIGJyb3dzZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVWZXJ0ZXhTaGFkZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdmVydGV4U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IHZlcnRleFNoYWRlcjogV2ViR0xTaGFkZXIgPSB0aHJvd0lmTnVsbDxXZWJHTFNoYWRlcj4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlU2hhZGVyKGdsLlZFUlRFWF9TSEFERVIpLFxuICAgICAgJ1VuYWJsZSB0byBjcmVhdGUgdmVydGV4IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZSh2ZXJ0ZXhTaGFkZXIsIHZlcnRleFNoYWRlclNvdXJjZSkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmNvbXBpbGVTaGFkZXIodmVydGV4U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIodmVydGV4U2hhZGVyLCBnbC5DT01QSUxFX1NUQVRVUykgPT09IGZhbHNlKSB7XG4gICAgY29uc29sZS5sb2coZ2wuZ2V0U2hhZGVySW5mb0xvZyh2ZXJ0ZXhTaGFkZXIpKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBjb21waWxlIHZlcnRleCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIHZlcnRleFNoYWRlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYWdtZW50U2hhZGVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGZyYWdtZW50U2hhZGVyU291cmNlOiBzdHJpbmcpOiBXZWJHTFNoYWRlciB7XG4gIGNvbnN0IGZyYWdtZW50U2hhZGVyOiBXZWJHTFNoYWRlciA9IHRocm93SWZOdWxsPFdlYkdMU2hhZGVyPihcbiAgICAgIGdsLCAoKSA9PiBnbC5jcmVhdGVTaGFkZXIoZ2wuRlJBR01FTlRfU0hBREVSKSxcbiAgICAgICdVbmFibGUgdG8gY3JlYXRlIGZyYWdtZW50IFdlYkdMU2hhZGVyLicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnNoYWRlclNvdXJjZShmcmFnbWVudFNoYWRlciwgZnJhZ21lbnRTaGFkZXJTb3VyY2UpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5jb21waWxlU2hhZGVyKGZyYWdtZW50U2hhZGVyKSk7XG4gIGlmIChnbC5nZXRTaGFkZXJQYXJhbWV0ZXIoZnJhZ21lbnRTaGFkZXIsIGdsLkNPTVBJTEVfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRTaGFkZXJJbmZvTG9nKGZyYWdtZW50U2hhZGVyKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gY29tcGlsZSBmcmFnbWVudCBzaGFkZXIuJyk7XG4gIH1cbiAgcmV0dXJuIGZyYWdtZW50U2hhZGVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KTogV2ViR0xQcm9ncmFtIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMUHJvZ3JhbT4oXG4gICAgICBnbCwgKCkgPT4gZ2wuY3JlYXRlUHJvZ3JhbSgpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTFByb2dyYW0uJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsaW5rUHJvZ3JhbShnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5saW5rUHJvZ3JhbShwcm9ncmFtKSk7XG4gIGlmIChnbC5nZXRQcm9ncmFtUGFyYW1ldGVyKHByb2dyYW0sIGdsLkxJTktfU1RBVFVTKSA9PT0gZmFsc2UpIHtcbiAgICBjb25zb2xlLmxvZyhnbC5nZXRQcm9ncmFtSW5mb0xvZyhwcm9ncmFtKSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGYWlsZWQgdG8gbGluayB2ZXJ0ZXggYW5kIGZyYWdtZW50IHNoYWRlcnMuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlUHJvZ3JhbShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0pIHtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC52YWxpZGF0ZVByb2dyYW0ocHJvZ3JhbSkpO1xuICBpZiAoZ2wuZ2V0UHJvZ3JhbVBhcmFtZXRlcihwcm9ncmFtLCBnbC5WQUxJREFURV9TVEFUVVMpID09PSBmYWxzZSkge1xuICAgIGNvbnNvbGUubG9nKGdsLmdldFByb2dyYW1JbmZvTG9nKHByb2dyYW0pKTtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1NoYWRlciBwcm9ncmFtIHZhbGlkYXRpb24gZmFpbGVkLicpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdGF0aWNWZXJ0ZXhCdWZmZXIoXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgZGF0YTogRmxvYXQzMkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuQVJSQVlfQlVGRkVSLCBidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5idWZmZXJEYXRhKGdsLkFSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVN0YXRpY0luZGV4QnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIGRhdGE6IFVpbnQxNkFycmF5KTogV2ViR0xCdWZmZXIge1xuICBjb25zdCBidWZmZXI6IFdlYkdMQnVmZmVyID0gdGhyb3dJZk51bGw8V2ViR0xCdWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUJ1ZmZlcigpLCAnVW5hYmxlIHRvIGNyZWF0ZSBXZWJHTEJ1ZmZlcicpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRCdWZmZXIoZ2wuRUxFTUVOVF9BUlJBWV9CVUZGRVIsIGJ1ZmZlcikpO1xuICBjYWxsQW5kQ2hlY2soXG4gICAgICBnbCwgKCkgPT4gZ2wuYnVmZmVyRGF0YShnbC5FTEVNRU5UX0FSUkFZX0JVRkZFUiwgZGF0YSwgZ2wuU1RBVElDX0RSQVcpKTtcbiAgcmV0dXJuIGJ1ZmZlcjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHF1ZXJ5TWF4VGV4dHVyZVNpemUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IG51bWJlciB7XG4gIGlmIChNQVhfVEVYVFVSRV9TSVpFICE9IG51bGwpIHtcbiAgICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbiAgfVxuICBNQVhfVEVYVFVSRV9TSVpFID1cbiAgICAgIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2whLmdldFBhcmFtZXRlcihnbCEuTUFYX1RFWFRVUkVfU0laRSkpO1xuICByZXR1cm4gTUFYX1RFWFRVUkVfU0laRTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldENoYW5uZWxzUGVyVGV4dHVyZSgpOiBudW1iZXIge1xuICBpZiAoaXNXZWJHTDJFbmFibGVkKCkpIHtcbiAgICByZXR1cm4gMTtcbiAgfVxuICByZXR1cm4gNDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVRleHR1cmUoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCk6IFdlYkdMVGV4dHVyZSB7XG4gIHJldHVybiB0aHJvd0lmTnVsbDxXZWJHTFRleHR1cmU+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZVRleHR1cmUoKSwgJ1VuYWJsZSB0byBjcmVhdGUgV2ViR0xUZXh0dXJlLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVUZXh0dXJlU2l6ZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlU2l6ZTogbnVtYmVyID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGlmICgod2lkdGggPD0gMCkgfHwgKGhlaWdodCA8PSAwKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIHRocm93IG5ldyBFcnJvcignUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICsgJyBpcyBpbnZhbGlkLicpO1xuICB9XG4gIGlmICgod2lkdGggPiBtYXhUZXh0dXJlU2l6ZSkgfHwgKGhlaWdodCA+IG1heFRleHR1cmVTaXplKSkge1xuICAgIGNvbnN0IHJlcXVlc3RlZCA9ICdbJyArIHdpZHRoICsgJ3gnICsgaGVpZ2h0ICsgJ10nO1xuICAgIGNvbnN0IG1heCA9ICdbJyArIG1heFRleHR1cmVTaXplICsgJ3gnICsgbWF4VGV4dHVyZVNpemUgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnUmVxdWVzdGVkIHRleHR1cmUgc2l6ZSAnICsgcmVxdWVzdGVkICtcbiAgICAgICAgJyBncmVhdGVyIHRoYW4gV2ViR0wgbWF4aW11bSBvbiB0aGlzIGJyb3dzZXIgLyBHUFUgJyArIG1heCArICcuJyk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUZyYW1lYnVmZmVyKGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQpOiBXZWJHTEZyYW1lYnVmZmVyIHtcbiAgcmV0dXJuIHRocm93SWZOdWxsPFdlYkdMRnJhbWVidWZmZXI+KFxuICAgICAgZ2wsICgpID0+IGdsLmNyZWF0ZUZyYW1lYnVmZmVyKCksICdVbmFibGUgdG8gY3JlYXRlIFdlYkdMRnJhbWVidWZmZXIuJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kVmVydGV4QnVmZmVyVG9Qcm9ncmFtQXR0cmlidXRlKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSwgYXR0cmlidXRlOiBzdHJpbmcsXG4gICAgYnVmZmVyOiBXZWJHTEJ1ZmZlciwgYXJyYXlFbnRyaWVzUGVySXRlbTogbnVtYmVyLCBpdGVtU3RyaWRlSW5CeXRlczogbnVtYmVyLFxuICAgIGl0ZW1PZmZzZXRJbkJ5dGVzOiBudW1iZXIpIHtcbiAgY29uc3QgbG9jID0gZ2wuZ2V0QXR0cmliTG9jYXRpb24ocHJvZ3JhbSwgYXR0cmlidXRlKTtcbiAgaWYgKGxvYyA9PT0gLTEpIHtcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihcbiAgICAgICAgJ1VuYWJsZSB0byBnZXQgYXR0cmlidXRlIFwiJyArIGF0dHJpYnV0ZSArICdcIiBvbiBXZWJHTFByb2dyYW0uJyk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWFueVxuICAgIChlcnJvciBhcyBhbnkpLm5hbWVkVmVydGV4QXR0cmlidXRlTm90Rm91bmQgPSBhdHRyaWJ1dGU7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5iaW5kQnVmZmVyKGdsLkFSUkFZX0JVRkZFUiwgYnVmZmVyKSk7XG4gIGNhbGxBbmRDaGVjayhcbiAgICAgIGdsLFxuICAgICAgKCkgPT4gZ2wudmVydGV4QXR0cmliUG9pbnRlcihcbiAgICAgICAgICBsb2MsIGFycmF5RW50cmllc1Blckl0ZW0sIGdsLkZMT0FULCBmYWxzZSwgaXRlbVN0cmlkZUluQnl0ZXMsXG4gICAgICAgICAgaXRlbU9mZnNldEluQnl0ZXMpKTtcbiAgY2FsbEFuZENoZWNrKGdsLCAoKSA9PiBnbC5lbmFibGVWZXJ0ZXhBdHRyaWJBcnJheShsb2MpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRUZXh0dXJlVW5pdChcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCB0ZXh0dXJlOiBXZWJHTFRleHR1cmUsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIHRleHR1cmUpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZFRleHR1cmVVbml0KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmVVbml0OiBudW1iZXIpIHtcbiAgdmFsaWRhdGVUZXh0dXJlVW5pdChnbCwgdGV4dHVyZVVuaXQpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmFjdGl2ZVRleHR1cmUoZ2wuVEVYVFVSRTAgKyB0ZXh0dXJlVW5pdCkpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRUZXh0dXJlKGdsLlRFWFRVUkVfMkQsIG51bGwpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHByb2dyYW06IFdlYkdMUHJvZ3JhbSxcbiAgICB1bmlmb3JtTmFtZTogc3RyaW5nKTogV2ViR0xVbmlmb3JtTG9jYXRpb24ge1xuICByZXR1cm4gdGhyb3dJZk51bGw8V2ViR0xVbmlmb3JtTG9jYXRpb24+KFxuICAgICAgZ2wsICgpID0+IGdsLmdldFVuaWZvcm1Mb2NhdGlvbihwcm9ncmFtLCB1bmlmb3JtTmFtZSksXG4gICAgICAndW5pZm9ybSBcIicgKyB1bmlmb3JtTmFtZSArICdcIiBub3QgcHJlc2VudCBpbiBwcm9ncmFtLicpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gYmluZFRleHR1cmVUb1Byb2dyYW1Vbmlmb3JtU2FtcGxlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBwcm9ncmFtOiBXZWJHTFByb2dyYW0sIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICB1bmlmb3JtU2FtcGxlck5hbWU6IHN0cmluZywgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGJpbmRUZXh0dXJlVW5pdChnbCwgdGV4dHVyZSwgdGV4dHVyZVVuaXQpKTtcbiAgY29uc3Qgc2FtcGxlckxvY2F0aW9uID1cbiAgICAgIGdldFByb2dyYW1Vbmlmb3JtTG9jYXRpb25PclRocm93KGdsLCBwcm9ncmFtLCB1bmlmb3JtU2FtcGxlck5hbWUpO1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLnVuaWZvcm0xaShzYW1wbGVyTG9jYXRpb24sIHRleHR1cmVVbml0KSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kQ2FudmFzVG9GcmFtZWJ1ZmZlcihnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0KSB7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuYmluZEZyYW1lYnVmZmVyKGdsLkZSQU1FQlVGRkVSLCBudWxsKSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wudmlld3BvcnQoMCwgMCwgZ2wuY2FudmFzLndpZHRoLCBnbC5jYW52YXMuaGVpZ2h0KSk7XG4gIGNhbGxBbmRDaGVjayhnbCwgKCkgPT4gZ2wuc2Npc3NvcigwLCAwLCBnbC5jYW52YXMud2lkdGgsIGdsLmNhbnZhcy5oZWlnaHQpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGJpbmRDb2xvclRleHR1cmVUb0ZyYW1lYnVmZmVyKFxuICAgIGdsOiBXZWJHTFJlbmRlcmluZ0NvbnRleHQsIHRleHR1cmU6IFdlYkdMVGV4dHVyZSxcbiAgICBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIHRleHR1cmUsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVuYmluZENvbG9yVGV4dHVyZUZyb21GcmFtZWJ1ZmZlcihcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBmcmFtZWJ1ZmZlcjogV2ViR0xGcmFtZWJ1ZmZlcikge1xuICBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgZnJhbWVidWZmZXIpKTtcbiAgY2FsbEFuZENoZWNrKFxuICAgICAgZ2wsXG4gICAgICAoKSA9PiBnbC5mcmFtZWJ1ZmZlclRleHR1cmUyRChcbiAgICAgICAgICBnbC5GUkFNRUJVRkZFUiwgZ2wuQ09MT1JfQVRUQUNITUVOVDAsIGdsLlRFWFRVUkVfMkQsIG51bGwsIDApKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlRnJhbWVidWZmZXIoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCkge1xuICBjb25zdCBzdGF0dXMgPSBnbC5jaGVja0ZyYW1lYnVmZmVyU3RhdHVzKGdsLkZSQU1FQlVGRkVSKTtcbiAgaWYgKHN0YXR1cyAhPT0gZ2wuRlJBTUVCVUZGRVJfQ09NUExFVEUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdFcnJvciBiaW5kaW5nIGZyYW1lYnVmZmVyOiAnICsgZ2V0RnJhbWVidWZmZXJFcnJvck1lc3NhZ2UoZ2wsIHN0YXR1cykpO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRGcmFtZWJ1ZmZlckVycm9yTWVzc2FnZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBzdGF0dXM6IG51bWJlcik6IHN0cmluZyB7XG4gIHN3aXRjaCAoc3RhdHVzKSB7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0FUVEFDSE1FTlQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX0lOQ09NUExFVEVfQVRUQUNITUVOVCc7XG4gICAgY2FzZSBnbC5GUkFNRUJVRkZFUl9JTkNPTVBMRVRFX01JU1NJTkdfQVRUQUNITUVOVDpcbiAgICAgIHJldHVybiAnRlJBTUVCVUZGRVJfSU5DT01QTEVURV9NSVNTSU5HX0FUVEFDSE1FTlQnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfSU5DT01QTEVURV9ESU1FTlNJT05TOlxuICAgICAgcmV0dXJuICdGUkFNRUJVRkZFUl9JTkNPTVBMRVRFX0RJTUVOU0lPTlMnO1xuICAgIGNhc2UgZ2wuRlJBTUVCVUZGRVJfVU5TVVBQT1JURUQ6XG4gICAgICByZXR1cm4gJ0ZSQU1FQlVGRkVSX1VOU1VQUE9SVEVEJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICd1bmtub3duIGVycm9yICcgKyBzdGF0dXM7XG4gIH1cbn1cblxuZnVuY3Rpb24gdGhyb3dJZk51bGw8VD4oXG4gICAgZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgcmV0dXJuVE9yTnVsbDogKCkgPT4gVCB8IG51bGwsXG4gICAgZmFpbHVyZU1lc3NhZ2U6IHN0cmluZyk6IFQge1xuICBjb25zdCB0T3JOdWxsOiBUfG51bGwgPSBjYWxsQW5kQ2hlY2soZ2wsICgpID0+IHJldHVyblRPck51bGwoKSk7XG4gIGlmICh0T3JOdWxsID09IG51bGwpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoZmFpbHVyZU1lc3NhZ2UpO1xuICB9XG4gIHJldHVybiB0T3JOdWxsIGFzIFQ7XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlVGV4dHVyZVVuaXQoZ2w6IFdlYkdMUmVuZGVyaW5nQ29udGV4dCwgdGV4dHVyZVVuaXQ6IG51bWJlcikge1xuICBjb25zdCBtYXhUZXh0dXJlVW5pdCA9IGdsLk1BWF9DT01CSU5FRF9URVhUVVJFX0lNQUdFX1VOSVRTIC0gMTtcbiAgY29uc3QgZ2xUZXh0dXJlVW5pdCA9IHRleHR1cmVVbml0ICsgZ2wuVEVYVFVSRTA7XG4gIGlmIChnbFRleHR1cmVVbml0IDwgZ2wuVEVYVFVSRTAgfHwgZ2xUZXh0dXJlVW5pdCA+IG1heFRleHR1cmVVbml0KSB7XG4gICAgY29uc3QgdGV4dHVyZVVuaXRSYW5nZSA9ICdbZ2wuVEVYVFVSRTAsIGdsLlRFWFRVUkUnICsgbWF4VGV4dHVyZVVuaXQgKyAnXSc7XG4gICAgdGhyb3cgbmV3IEVycm9yKCd0ZXh0dXJlVW5pdCBtdXN0IGJlIGluICcgKyB0ZXh0dXJlVW5pdFJhbmdlICsgJy4nKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGV4dHVyZVNoYXBlRnJvbUxvZ2ljYWxTaGFwZShcbiAgICBnbDogV2ViR0xSZW5kZXJpbmdDb250ZXh0LCBsb2dpY2FsU2hhcGU6IG51bWJlcltdLFxuICAgIHByZWZlcnJlZFRleFNoYXBlPzogW251bWJlciwgbnVtYmVyXSk6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBtYXhUZXhTaXplID0gcXVlcnlNYXhUZXh0dXJlU2l6ZShnbCk7XG4gIGNvbnN0IHNpemUgPSB1dGlsLnNpemVGcm9tU2hhcGUobG9naWNhbFNoYXBlKTtcbiAgaWYgKHByZWZlcnJlZFRleFNoYXBlICE9IG51bGwpIHtcbiAgICBjb25zdCBzaXplUHJlZmVycmVkID0gdXRpbC5zaXplRnJvbVNoYXBlKHByZWZlcnJlZFRleFNoYXBlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgc2l6ZSA9PT0gc2l6ZVByZWZlcnJlZCxcbiAgICAgICAgYFNpemUgb2Ygc2hhcGUgKCR7c2l6ZX0pIG11c3QgbWF0Y2ggc2l6ZSBvZiBgICtcbiAgICAgICAgICAgIGBwcmVmZXJyZWRTaGFwZSAoJHtzaXplUHJlZmVycmVkfSlgKTtcbiAgICBpZiAocHJlZmVycmVkVGV4U2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgICBwcmVmZXJyZWRUZXhTaGFwZVsxXSA8PSBtYXhUZXhTaXplKSB7XG4gICAgICByZXR1cm4gcHJlZmVycmVkVGV4U2hhcGU7XG4gICAgfVxuICB9XG5cbiAgaWYgKGxvZ2ljYWxTaGFwZS5sZW5ndGggPD0gMSAmJiBzaXplIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gW3NpemUsIDFdO1xuICB9IGVsc2UgaWYgKFxuICAgICAgbG9naWNhbFNoYXBlLmxlbmd0aCA9PT0gMiAmJiBsb2dpY2FsU2hhcGVbMF0gPD0gbWF4VGV4U2l6ZSAmJlxuICAgICAgbG9naWNhbFNoYXBlWzFdIDw9IG1heFRleFNpemUpIHtcbiAgICByZXR1cm4gbG9naWNhbFNoYXBlIGFzIFtudW1iZXIsIG51bWJlcl07XG4gIH0gZWxzZSBpZiAoXG4gICAgICBsb2dpY2FsU2hhcGUubGVuZ3RoID09PSAzICYmIGxvZ2ljYWxTaGFwZVswXSA8PSBtYXhUZXhTaXplICYmXG4gICAgICBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl0gPD0gbWF4VGV4U2l6ZSkge1xuICAgIHJldHVybiBbbG9naWNhbFNoYXBlWzBdLCBsb2dpY2FsU2hhcGVbMV0gKiBsb2dpY2FsU2hhcGVbMl1dO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB1dGlsLnNpemVUb1NxdWFyaXNoU2hhcGUoc2l6ZSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtBZGROb2RlLCBBcmdNYXhFcXVhbHNOb2RlLCBBcmdNYXhOb2RlLCBDb25jYXQzRE5vZGUsIENvbnZvbHV0aW9uMkROb2RlLCBEaXZpZGVOb2RlLCBFeHBOb2RlLCBGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZSwgR3JhcGgsIExvZ05vZGUsIE1hdE11bE5vZGUsIE1heFBvb2xOb2RlLCBNZWFuU3F1YXJlZENvc3ROb2RlLCBNdWx0aXBseU5vZGUsIE5vZGUsIFJlZHVjZVN1bU5vZGUsIFJlTFVOb2RlLCBSZXNoYXBlTm9kZSwgU2lnbW9pZE5vZGUsIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSwgU29mdG1heE5vZGUsIFNwbGl0Tm9kZSwgU3F1YXJlTm9kZSwgU3VidHJhY3ROb2RlLCBUYW5ITm9kZSwgVGVuc29yfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7QWRkfSBmcm9tICcuL29wcy9hZGQnO1xuaW1wb3J0IHtBcmdNYXh9IGZyb20gJy4vb3BzL2FyZ21heCc7XG5pbXBvcnQge0FyZ01heEVxdWFsc30gZnJvbSAnLi9vcHMvYXJnbWF4ZXF1YWxzJztcbmltcG9ydCB7Q29uY2F0M0R9IGZyb20gJy4vb3BzL2NvbmNhdDNkJztcbmltcG9ydCB7Q29udm9sdXRpb24yRH0gZnJvbSAnLi9vcHMvY29udm9sdXRpb24nO1xuaW1wb3J0IHtEaXZpZGV9IGZyb20gJy4vb3BzL2RpdmlkZSc7XG5pbXBvcnQge1JlTFUsIFNpZ21vaWQsIFNxdWFyZSwgVGFuSH0gZnJvbSAnLi9vcHMvZWxlbWVudF93aXNlX2FjdGl2YXRpb24nO1xuaW1wb3J0IHtNZWFuU3F1YXJlZENvc3R9IGZyb20gJy4vb3BzL2VsZW1lbnRfd2lzZV9jb3N0JztcbmltcG9ydCB7RXhwfSBmcm9tICcuL29wcy9leHAnO1xuaW1wb3J0IHtMaW5lYXJDb21iaW5hdGlvbn0gZnJvbSAnLi9vcHMvbGluZWFyX2NvbWJpbmF0aW9uJztcbmltcG9ydCB7TG9nfSBmcm9tICcuL29wcy9sb2cnO1xuaW1wb3J0IHtNYXRNdWx9IGZyb20gJy4vb3BzL21hdG11bCc7XG5pbXBvcnQge01heFBvb2x9IGZyb20gJy4vb3BzL21heF9wb29sJztcbmltcG9ydCB7TXVsdGlwbHl9IGZyb20gJy4vb3BzL211bHRpcGx5JztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge1JlZHVjZVN1bX0gZnJvbSAnLi9vcHMvcmVkdWNlX3N1bSc7XG5pbXBvcnQge1Jlc2hhcGV9IGZyb20gJy4vb3BzL3Jlc2hhcGUnO1xuaW1wb3J0IHtTb2Z0bWF4LCBTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdH0gZnJvbSAnLi9vcHMvc29mdG1heCc7XG5pbXBvcnQge1NwbGl0fSBmcm9tICcuL29wcy9zcGxpdCc7XG5pbXBvcnQge1N1YnRyYWN0fSBmcm9tICcuL29wcy9zdWJ0cmFjdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBlbWl0RnJvbUdyYXBoTm9kZXMobm9kZXM6IE5vZGVbXSk6IE9wZXJhdGlvbltdIHtcbiAgY29uc3Qgb3BzOiBPcGVyYXRpb25bXSA9IFtdO1xuICBub2Rlcy5mb3JFYWNoKG5vZGUgPT4gQXJyYXkucHJvdG90eXBlLnB1c2guYXBwbHkob3BzLCBlbWl0T3BGcm9tTm9kZShub2RlKSkpO1xuICByZXR1cm4gb3BzO1xufVxuXG5mdW5jdGlvbiBlbWl0T3BGcm9tTm9kZShub2RlOiBOb2RlKTogT3BlcmF0aW9uW10ge1xuICBpZiAobm9kZSBpbnN0YW5jZW9mIFJlc2hhcGVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVzaGFwZShub2RlLmlucHV0c1tSZXNoYXBlTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgTWF0TXVsTm9kZSkge1xuICAgIGNvbnN0IHgxID0gbm9kZS5pbnB1dHNbTWF0TXVsTm9kZS5YMV07XG4gICAgY29uc3QgeDIgPSBub2RlLmlucHV0c1tNYXRNdWxOb2RlLlgyXTtcbiAgICByZXR1cm4gW25ldyBNYXRNdWwoeDEsIHgyLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBDb252b2x1dGlvbjJETm9kZSkge1xuICAgIGNvbnN0IHcgPSBub2RlLmlucHV0c1tDb252b2x1dGlvbjJETm9kZS5XXTtcbiAgICBjb25zdCB4ID0gbm9kZS5pbnB1dHNbQ29udm9sdXRpb24yRE5vZGUuWF07XG4gICAgY29uc3QgYiA9IG5vZGUuaW5wdXRzW0NvbnZvbHV0aW9uMkROb2RlLkJdO1xuICAgIHJldHVybiBbbmV3IENvbnZvbHV0aW9uMkQoXG4gICAgICAgIHcsIHgsIGIsIG5vZGUub3V0cHV0LCBub2RlLmZpZWxkU2l6ZSwgbm9kZS5vdXRwdXREZXB0aCwgbm9kZS5zdHJpZGUsXG4gICAgICAgIG5vZGUuemVyb1BhZCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBNYXhQb29sTm9kZSkge1xuICAgIGNvbnN0IHggPSBub2RlLmlucHV0c1tNYXhQb29sTm9kZS5YXTtcbiAgICByZXR1cm4gW25ldyBNYXhQb29sKFxuICAgICAgICB4LCBub2RlLm91dHB1dCwgbm9kZS5maWVsZFNpemUsIG5vZGUuc3RyaWRlLCBub2RlLnplcm9QYWQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRXhwTm9kZSkge1xuICAgIHJldHVybiBbbmV3IEV4cChub2RlLmlucHV0c1tFeHBOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBMb2dOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgTG9nKG5vZGUuaW5wdXRzW0xvZ05vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFJlTFVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgUmVMVShub2RlLmlucHV0c1tSZUxVTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgVGFuSE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBUYW5IKG5vZGUuaW5wdXRzW1RhbkhOb2RlLlhdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTaWdtb2lkTm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNpZ21vaWQobm9kZS5pbnB1dHNbU2lnbW9pZE5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFNvZnRtYXhDcm9zc0VudHJvcHlDb3N0Tm9kZSkge1xuICAgIGNvbnN0IHggPSBub2RlLmlucHV0c1tTb2Z0bWF4Q3Jvc3NFbnRyb3B5Q29zdE5vZGUuWF07XG4gICAgY29uc3QgdGFyZ2V0ID0gbm9kZS5pbnB1dHNbU29mdG1heENyb3NzRW50cm9weUNvc3ROb2RlLlRBUkdFVF07XG4gICAgcmV0dXJuIFtuZXcgU29mdG1heENyb3NzRW50cm9weUNvc3QoeCwgdGFyZ2V0LCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTb2Z0bWF4Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNvZnRtYXgobm9kZS5pbnB1dHNbU29mdG1heE5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE1lYW5TcXVhcmVkQ29zdE5vZGUpIHtcbiAgICBjb25zdCBsYWJlbCA9IG5vZGUuaW5wdXRzW01lYW5TcXVhcmVkQ29zdE5vZGUuTEFCRUxdO1xuICAgIGNvbnN0IHByZWRpY3Rpb24gPSBub2RlLmlucHV0c1tNZWFuU3F1YXJlZENvc3ROb2RlLlBSRURJQ1RJT05dO1xuICAgIHJldHVybiBbbmV3IE1lYW5TcXVhcmVkQ29zdChsYWJlbCwgcHJlZGljdGlvbiwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgQXJnTWF4RXF1YWxzTm9kZSkge1xuICAgIHJldHVybiBbbmV3IEFyZ01heEVxdWFscyhcbiAgICAgICAgbm9kZS5pbnB1dHNbQXJnTWF4RXF1YWxzTm9kZS5YMV0sIG5vZGUuaW5wdXRzW0FyZ01heEVxdWFsc05vZGUuWDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBBcmdNYXhOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgQXJnTWF4KG5vZGUueCwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBMaW5lYXJDb21iaW5hdGlvbihcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuVDFdLFxuICAgICAgICBub2RlLmlucHV0c1tGdXNlZExpbmVhckNvbWJpbmF0aW9uTm9kZS5UMl0sXG4gICAgICAgIG5vZGUuaW5wdXRzW0Z1c2VkTGluZWFyQ29tYmluYXRpb25Ob2RlLkMxXSxcbiAgICAgICAgbm9kZS5pbnB1dHNbRnVzZWRMaW5lYXJDb21iaW5hdGlvbk5vZGUuQzJdLCBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBDb25jYXQzRE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBDb25jYXQzRChcbiAgICAgICAgbm9kZS5pbnB1dHNbQ29uY2F0M0ROb2RlLlgxXSwgbm9kZS5pbnB1dHNbQ29uY2F0M0ROb2RlLlgyXSwgbm9kZS5heGlzLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBTcXVhcmVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgU3F1YXJlKG5vZGUuaW5wdXRzW1NxdWFyZU5vZGUuWF0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIEFkZE5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBBZGQoXG4gICAgICAgIG5vZGUuaW5wdXRzW0FkZE5vZGUuVDFdLCBub2RlLmlucHV0c1tBZGROb2RlLlQyXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChub2RlIGluc3RhbmNlb2YgU3VidHJhY3ROb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgU3VidHJhY3QoXG4gICAgICAgIG5vZGUuaW5wdXRzW1N1YnRyYWN0Tm9kZS5UMV0sIG5vZGUuaW5wdXRzW1N1YnRyYWN0Tm9kZS5UMl0sXG4gICAgICAgIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIE11bHRpcGx5Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IE11bHRpcGx5KFxuICAgICAgICBub2RlLmlucHV0c1tNdWx0aXBseU5vZGUuVDFdLCBub2RlLmlucHV0c1tNdWx0aXBseU5vZGUuVDJdLFxuICAgICAgICBub2RlLm91dHB1dCldO1xuICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiBEaXZpZGVOb2RlKSB7XG4gICAgcmV0dXJuIFtuZXcgRGl2aWRlKFxuICAgICAgICBub2RlLmlucHV0c1tEaXZpZGVOb2RlLlQxXSwgbm9kZS5pbnB1dHNbRGl2aWRlTm9kZS5UMl0sIG5vZGUub3V0cHV0KV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFNwbGl0Tm9kZSkge1xuICAgIHJldHVybiBbbmV3IFNwbGl0KG5vZGUuaW5wdXRzW1NwbGl0Tm9kZS5YXSwgbm9kZS5vdXRwdXRzKV07XG4gIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIFJlZHVjZVN1bU5vZGUpIHtcbiAgICByZXR1cm4gW25ldyBSZWR1Y2VTdW0obm9kZS5pbnB1dHNbUmVkdWNlU3VtTm9kZS5YXSwgbm9kZS5vdXRwdXQpXTtcbiAgfSBlbHNlIGlmIChncmFwaF91dGlsLmlzSW5wdXROb2RlKG5vZGUpKSB7XG4gICAgcmV0dXJuIFtdO1xuICB9IGVsc2Uge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICB0aHJvdyBFcnJvcignVW5zdXBwb3J0ZWQgbm9kZSB0eXBlOiAnICsgKG5vZGUuY29uc3RydWN0b3IgYXMgYW55KS5uYW1lKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFkZCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgZHlTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgLyoqIEVsZW1lbnQtd2lzZSBhZGQgb3BlcmF0aW9uLiBCcm9hZGNhc3RzIGlmIG9uZSBvZiB0aGUgdGVuc29ycyBpcyBzY2FsYXIuICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh4MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoeDEsIHgyKTtcbiAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhclBsdXNBcnJheSh4MiwgeDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5hZGQoeDEsIHgyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDFUZW5zb3IpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy54MVRlbnNvci5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBzdW0gPSBtYXRoLnN1bShkeSk7XG4gICAgICAgICAgaWYgKHRoaXMuZHlTaXplU2NhbGFyID09IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMuZHlTaXplU2NhbGFyID0gU2NhbGFyLm5ldyhkeS5zaXplKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZGl2aWRlKHN1bSwgdGhpcy5keVNpemVTY2FsYXIpKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGR5KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDJUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MlRlbnNvciwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBkeSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHRoaXMuZHlTaXplU2NhbGFyICE9IG51bGwpIHtcbiAgICAgIHRoaXMuZHlTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICB9XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIEFyZ01heCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBbiBBcmdNYXggb3BlcmF0aW9uLlxuICAgKi9cbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKTtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmFyZ01heCh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRocm93IG5ldyBFcnJvcignQXJnTWF4IGJhY2twcm9wIHVuaW1wbGVtZW50ZWQnKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgQXJyYXkyRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgQXJnTWF4RXF1YWxzIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEFuIEFyZ01heEVxdWFscyBvcGVyYXRpb24uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKG1hdGguYXJnTWF4RXF1YWxzKHgxLCB4MikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0FyZ01heEVxdWFscyBiYWNrcHJvcCB1bmltcGxlbWVudGVkJyk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGNvbmNhdDNkX3V0aWwgZnJvbSAnLi4vbWF0aC9jb25jYXQzZF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5M0QsIE5EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25jYXQzRCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBIENvbmNhdCAzRCBvcGVyYXRpb24uXG4gICAqXG4gICAqIENvbmNhdHMgdHdvIDNEIHRlbnNvcnMgYWxvbmcgYW4gYXhpcy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgYXhpczogbnVtYmVyLFxuICAgICAgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIGNvbmNhdDNkX3V0aWwuYXNzZXJ0Q29uY2F0M0RTaGFwZXNNYXRjaChcbiAgICAgICAgeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlLCBheGlzKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBjb25jYXRSZXN1bHQgPSBtYXRoLmNvbmNhdDNEKHgxLCB4MiwgdGhpcy5heGlzKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKGNvbmNhdFJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIHRocm93IG5ldyBFcnJvcignQ29uY2F0M0QgYmFja3Byb3Agbm90IGltcGxlbWVudGVkLicpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtNYXRyaXhPcmllbnRhdGlvbiwgTkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge0FycmF5MUQsIEFycmF5MkQsIEFycmF5M0QsIEFycmF5NEQsIE5EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIENvbnZvbHV0aW9uMkQgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIHplcm9QYWQ6IG51bWJlcjtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIGNvbnZvbHV0aW9uIG9wIHdpdGggdGhlIHNwZWNpZmllZCBwcm9wZXJ0aWVzLlxuICAgKlxuICAgKiBAcGFyYW0gaW5wdXRTaGFwZSBUaGUgc2hhcGUgb2YgdGhlIGlucHV0IG5kYXJyYXkuXG4gICAqIEBwYXJhbSBmaWVsZFNpemUgVGhlIHNpemUgb2YgdGhlIGZpbHRlciAocm93cy9jb2xzIG9mIHNsaWRpbmcgd2luZG93KS5cbiAgICogQHBhcmFtIG91dHB1dERlcHRoIFRoZSBkZXB0aCBvZiB0aGUgb3V0cHV0IChOdW1iZXIgb2YgZmlsdGVycykuXG4gICAqIEBwYXJhbSBzdHJpZGUgSG93IG1hbnkgcGl4ZWxzIHRvIHNoaWZ0IHRoZSBmaWx0ZXIgYnkgd2hlbiBzbGlkaW5nLlxuICAgKiAgICAgRGVmYXVsdHMgdG8gMS5cbiAgICogQHBhcmFtIHplcm9QYWQgSG93IG1hbnkgcGl4ZWxzIHRvIHBhZCB0aGUgaW5wdXQgZnJvbSBlYWNoIHNpZGUuIERlZmF1bHRzIHRvXG4gICAqICAgICBhIHZhbHVlIHNvIHRoYXQgdGhlIHJvd3MgYW5kIGNvbHVtbnMgb2YgdGhlIG91dHB1dCBuZGFycmF5IGlzXG4gICAqICAgICB0aGUgc2FtZSBhcyB0aGUgaW5wdXQgbmRhcnJheS5cbiAgICogQHBhcmFtIHdlaWdodHMgT3B0aW9uYWwuIFRoZSB3ZWlnaHRzIG9mIHRoZSBmaWx0ZXJzLlxuICAgKiBAcGFyYW0gYmlhc2VzIE9wdGlvbmFsLiBUaGUgYmlhcyB0ZXJtcyBvZiB0aGUgZmlsdGVycy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB3VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIGJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yLCBwcml2YXRlIGZpZWxkU2l6ZTogbnVtYmVyLFxuICAgICAgcHJpdmF0ZSBvdXRwdXREZXB0aDogbnVtYmVyLCBwcml2YXRlIHN0cmlkZSA9IDEsIHplcm9QYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuYXNzZXJ0V2VpZ2h0c1NoYXBlKHdUZW5zb3Iuc2hhcGUpO1xuICAgIHRoaXMuemVyb1BhZCA9IHplcm9QYWQgIT0gbnVsbCA/XG4gICAgICAgIHplcm9QYWQgOlxuICAgICAgICBjb252X3V0aWwuY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgICAgICAgICB0aGlzLnhUZW5zb3Iuc2hhcGUgYXMgW251bWJlciwgbnVtYmVyLCBudW1iZXJdLCB0aGlzLmZpZWxkU2l6ZSxcbiAgICAgICAgICAgIHRoaXMuc3RyaWRlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc0ludCh0aGlzLnplcm9QYWQpLFxuICAgICAgICBgVGhlIHplcm8gcGFkZGluZyAoJHt0aGlzLnplcm9QYWR9KSBtdXN0IGJlIGFuIGludGVnZXIuIENoYW5nZSB0aGUgYCArXG4gICAgICAgICAgICBgc3RyaWRlIGFuZC9vciB6ZXJvIHBhZCBwYXJhbWV0ZXJzYCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHdlaWdodHMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMud1RlbnNvcikgYXMgQXJyYXk0RDtcbiAgICBjb25zdCBiaWFzZXMgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuYlRlbnNvcikgYXMgQXJyYXkxRDtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICBrZWVwKG1hdGguY29udjJkKHgsIHdlaWdodHMsIGJpYXNlcywgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB3ZWlnaHRzID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLndUZW5zb3IpIGFzIEFycmF5NEQ7XG4gICAgY29uc3QgeCA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54VGVuc29yKSBhcyBBcnJheTNEO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcikgYXMgQXJyYXkzRDtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IHtkdywgZGIsIGR4fSA9XG4gICAgICAgICAgbWF0aC5jb252MmRCYWNrUHJvcCh4LCBkeSwgd2VpZ2h0cywgdGhpcy5zdHJpZGUsIHRoaXMuemVyb1BhZCk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy53VGVuc29yLCBrZWVwKGR3KSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5iVGVuc29yLCBrZWVwKGRiKSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54VGVuc29yLCBrZWVwKGR4KSk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzc2VydFdlaWdodHNTaGFwZSh3ZWlnaHRzU2hhcGU6IG51bWJlcltdKSB7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHdlaWdodHNTaGFwZVswXSA9PT0gdGhpcy5maWVsZFNpemUgJiZcbiAgICAgICAgICAgIHdlaWdodHNTaGFwZVsxXSA9PT0gdGhpcy5maWVsZFNpemUgJiZcbiAgICAgICAgICAgIHdlaWdodHNTaGFwZVsyXSA9PT0gdGhpcy54VGVuc29yLnNoYXBlWzJdICYmXG4gICAgICAgICAgICB3ZWlnaHRzU2hhcGVbM10gPT09IHRoaXMub3V0cHV0RGVwdGgsXG4gICAgICAgIGB3ZWlnaHRzIG11c3QgYmUgb2Ygc2hhcGUgWyR7dGhpcy5maWVsZFNpemV9LCR7dGhpcy5maWVsZFNpemV9LGAgK1xuICAgICAgICAgICAgYCR7dGhpcy54VGVuc29yLnNoYXBlWzJdfSwke3RoaXMub3V0cHV0RGVwdGh9XSBidXQgdGhleSBhcmUgb2ZgICtcbiAgICAgICAgICAgIGBzaGFwZSBbJHt3ZWlnaHRzU2hhcGV9XWApO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRGl2aWRlIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgcHJpdmF0ZSBvbmVzOiBOREFycmF5O1xuXG4gIC8qKlxuICAgKiBFbGVtZW50LXdpc2UgZGl2aWRlIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXNcbiAgICogc2NhbGFyLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHgxVGVuc29yLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5zaXplRnJvbVNoYXBlKHgyVGVuc29yLnNoYXBlKSA9PT0gMSB8fFxuICAgICAgICAgICAgdXRpbC5hcnJheXNFcXVhbCh4MVRlbnNvci5zaGFwZSwgeDJUZW5zb3Iuc2hhcGUpLFxuICAgICAgICAnT25lIG9mIHQxIG9yIHQyIG11c3QgYmUgYSBzY2FsYXIsIG9yIHQxIGFuZCB0MiBtdXN0IGhhdmUgJyArXG4gICAgICAgICAgICAndGhlIHNhbWUgc2hhcGUnKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGxldCByZXN1bHQ6IE5EQXJyYXk7XG4gICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHQxLnNoYXBlKSkge1xuICAgICAgICByZXN1bHQgPSBtYXRoLnNjYWxhckRpdmlkZWRCeUFycmF5KHQxLCB0Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5hcnJheURpdmlkZWRCeVNjYWxhcih0MSwgdDIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5kaXZpZGUodDEsIHQyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIGNvbnN0IHgxSXNTY2FsYXIgPSB1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpO1xuICAgIGNvbnN0IHgySXNTY2FsYXIgPSB1dGlsLmlzU2NhbGFyU2hhcGUoeDIuc2hhcGUpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgaWYgKHgxSXNTY2FsYXIpIHtcbiAgICAgICAgICBjb25zdCBkaXYgPSBtYXRoLmRpdmlkZShkeSwgeDIpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zdW0oZGl2KSkpO1xuXG4gICAgICAgICAgZGl2LmRpc3Bvc2UoKTtcbiAgICAgICAgfSBlbHNlIGlmICh4MklzU2NhbGFyKSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguYXJyYXlEaXZpZGVkQnlTY2FsYXIoZHksIHgyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZGl2aWRlKGR5LCB4MikpKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngyVGVuc29yKSkge1xuICAgICAgICAvLyBkeDIgPSAtMSAqIHgxICogeDIgXiAtMi5cbiAgICAgICAgY29uc3QgeDJTcXVhcmVkID0gbWF0aC5lbGVtZW50V2lzZU11bCh4MiwgeDIpO1xuXG4gICAgICAgIGxldCB4MU92ZXJYMlNxdWFyZWQ6IE5EQXJyYXk7XG4gICAgICAgIGlmICh4MklzU2NhbGFyKSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5hcnJheURpdmlkZWRCeVNjYWxhcih4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfSBlbHNlIGlmICh4MUlzU2NhbGFyKSB7XG4gICAgICAgICAgeDFPdmVyWDJTcXVhcmVkID0gbWF0aC5zY2FsYXJEaXZpZGVkQnlBcnJheSh4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB4MU92ZXJYMlNxdWFyZWQgPSBtYXRoLmRpdmlkZSh4MSwgeDJTcXVhcmVkKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGR4MiA9IG1hdGgubmVnKHgxT3ZlclgyU3F1YXJlZCk7XG4gICAgICAgIGNvbnN0IGR5VGltZXNEZXJpdmF0aXZlID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgZHgyKTtcblxuICAgICAgICBpZiAoeDJJc1NjYWxhcikge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc3VtKGR5VGltZXNEZXJpdmF0aXZlKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKGR5VGltZXNEZXJpdmF0aXZlKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtBY3RpdmF0aW9uRnVuY3Rpb24sIFJlTFVGdW5jLCBTaWdtb2lkRnVuYywgU3F1YXJlRnVuYywgVGFuSEZ1bmN9IGZyb20gJy4uL21hdGgvYWN0aXZhdGlvbl9mdW5jdGlvbnMnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRWxlbWVudFdpc2VBY3RpdmF0aW9uIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcm90ZWN0ZWQgeFRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgeVRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBmdW5jOiBBY3RpdmF0aW9uRnVuY3Rpb24pIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAodGhpcy5mdW5jLm91dHB1dChtYXRoLCB4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIC8vIGRFL2R4X2kgPSBzdW1faiBkRS9keV9qICogZHlfai9keF9pXG4gICAgLy8gICAgICAgICA9IGRFL2R5X2kgKiBkeV9pL2R4X2lcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuICAgIGNvbnN0IHkgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGNvbnN0IGR5ZHggPSB0aGlzLmZ1bmMuZGVyKG1hdGgsIHgsIHkpO1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChtYXRoLmVsZW1lbnRXaXNlTXVsKGR5LCBkeWR4KSkpO1xuICAgICAgZHlkeC5kaXNwb3NlKCk7XG4gICAgfSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZUxVIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgUmVMVUZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBUYW5IIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgVGFuSEZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaWdtb2lkIGV4dGVuZHMgRWxlbWVudFdpc2VBY3RpdmF0aW9uIHtcbiAgY29uc3RydWN0b3IoeFRlbnNvcjogVGVuc29yLCB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcih4VGVuc29yLCB5VGVuc29yLCBuZXcgU2lnbW9pZEZ1bmMoKSk7XG4gIH1cbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBTcXVhcmUgZXh0ZW5kcyBFbGVtZW50V2lzZUFjdGl2YXRpb24ge1xuICBjb25zdHJ1Y3Rvcih4VGVuc29yOiBUZW5zb3IsIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKHhUZW5zb3IsIHlUZW5zb3IsIG5ldyBTcXVhcmVGdW5jKCkpO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtFbGVtZW50V2lzZUNvc3RGdW5jdGlvbiwgU3F1YXJlQ29zdEZ1bmN9IGZyb20gJy4uL21hdGgvY29zdF9mdW5jdGlvbnMnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgRWxlbWVudFdpc2VDb3N0PFQgZXh0ZW5kcyBOREFycmF5PiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgb25lT3Zlck5TY2FsYXI6IFNjYWxhcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByb3RlY3RlZCB4MVRlbnNvcjogVGVuc29yLCBwcm90ZWN0ZWQgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByb3RlY3RlZCB5VGVuc29yOiBUZW5zb3IsIHByb3RlY3RlZCBmdW5jOiBFbGVtZW50V2lzZUNvc3RGdW5jdGlvbikge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5vbmVPdmVyTlNjYWxhciA9IFNjYWxhci5uZXcoMSAvIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgY29uc3QgZWxlbWVudFdpc2VDb3N0ID0gdGhpcy5mdW5jLmNvc3QobWF0aCwgeDEsIHgyKTtcbiAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGVsZW1lbnRXaXNlQ29zdCk7XG4gICAgICBjb25zdCByZXN1bHQgPSBtYXRoLnNjYWxhclRpbWVzQXJyYXkodGhpcy5vbmVPdmVyTlNjYWxhciwgc3VtKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy55VGVuc29yLCBrZWVwKHJlc3VsdCkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHgxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB4MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MVRlbnNvciwga2VlcCh0aGlzLmZ1bmMuZGVyKG1hdGgsIHgxLCB4MikpKTtcbiAgICAgIH1cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngyVGVuc29yLCBrZWVwKHRoaXMuZnVuYy5kZXIobWF0aCwgeDIsIHgxKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmZ1bmMuZGlzcG9zZSgpO1xuICAgIHRoaXMub25lT3Zlck5TY2FsYXIuZGlzcG9zZSgpO1xuICB9XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWVhblNxdWFyZWRDb3N0IGV4dGVuZHMgRWxlbWVudFdpc2VDb3N0PEFycmF5MUQ+IHtcbiAgY29uc3RydWN0b3IoeDFUZW5zb3I6IFRlbnNvciwgeDJUZW5zb3I6IFRlbnNvciwgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoeDFUZW5zb3IsIHgyVGVuc29yLCB5VGVuc29yLCBuZXcgU3F1YXJlQ29zdEZ1bmMoKSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBFeHAgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogRXhwb25lbnRhdGlvbiBvcGVyYXRpb24gLSBlXnguXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHhUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB5VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldCh0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5leHAoeCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnlUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnhUZW5zb3IpKSB7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bCh5LCBkeSkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMaW5lYXJDb21iaW5hdGlvbiBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKlxuICAgKiBBIDItdGVuc29yIGxpbmVhciBjb21iaW5hdGlvbiBvcGVyYXRpb24uXG4gICAqXG4gICAqIENvbWJpbmVzIHRlbnNvcnMgeDEgYW5kIHgyIChvZiB0aGUgc2FtZSBzaGFwZSkgd2l0aCB3ZWlnaHRzIGMxICYgYzI7XG4gICAqIENvbXB1dGVzIGMxKngxICsgYzIqeDIuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgeDFUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSB4MlRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBjMVRlbnNvcjogVGVuc29yLCBwcml2YXRlIGMyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIG91dFRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBjb25zdCBjMSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMVRlbnNvcikuYXNTY2FsYXIoKTtcbiAgICBjb25zdCBjMiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMlRlbnNvcikuYXNTY2FsYXIoKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy5vdXRUZW5zb3IsIGtlZXAobWF0aC5zY2FsZWRBcnJheUFkZChjMSwgeDEsIGMyLCB4MikpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGMxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLmMxVGVuc29yKTtcbiAgICBjb25zdCBjMiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5jMlRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy5vdXRUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGMxLCBkeSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGMyLCBkeSkpKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy5jMVRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZG90UHJvZHVjdDEgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHgxLCBkeSk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLmMxVGVuc29yLCBrZWVwKG1hdGguc3VtKGRvdFByb2R1Y3QxKSkpO1xuICAgICAgfVxuXG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLmMyVGVuc29yKSkge1xuICAgICAgICBjb25zdCBkb3RQcm9kdWN0MiA9IG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIGR5KTtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMuYzJUZW5zb3IsIGtlZXAobWF0aC5zdW0oZG90UHJvZHVjdDIpKSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi4vZ3JhcGhfdXRpbCc7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2cgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICAvKipcbiAgICogTmF0dXJhbCBsb2cgb3BlcmF0aW9uIC0gbG4oeClcbiAgICovXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgeFRlbnNvcjogVGVuc29yLCBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChtYXRoLmxvZyh4KSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcik7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueFRlbnNvcikpIHtcbiAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueFRlbnNvciwga2VlcChtYXRoLmRpdmlkZShkeSwgeCkpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TWF0cml4T3JpZW50YXRpb24sIE5EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtBcnJheTFELCBBcnJheTJELCBOREFycmF5fSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgY2xhc3MgTWF0TXVsIGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIHgxVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeDJUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIGZlZWRGb3J3YXJkKG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgeDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDFUZW5zb3IpO1xuICAgIGNvbnN0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDIgJiYgeDIuc2hhcGUubGVuZ3RoID09PSAyKSB7XG4gICAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgICB0aGlzLnlUZW5zb3IsIGtlZXAobWF0aC5tYXRNdWwoeDEgYXMgQXJyYXkyRCwgeDIgYXMgQXJyYXkyRCkpKTtcbiAgICAgIH0gZWxzZSBpZiAoeDEuc2hhcGUubGVuZ3RoID09PSAyICYmIHgyLnNoYXBlLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgICAga2VlcChtYXRoLm1hdHJpeFRpbWVzVmVjdG9yKHgxIGFzIEFycmF5MkQsIHgyIGFzIEFycmF5MUQpKSk7XG4gICAgICB9IGVsc2UgaWYgKHgxLnNoYXBlLmxlbmd0aCA9PT0gMSAmJiB4Mi5zaGFwZS5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueVRlbnNvcixcbiAgICAgICAgICAgIGtlZXAobWF0aC52ZWN0b3JUaW1lc01hdHJpeCh4MSBhcyBBcnJheTFELCB4MiBhcyBBcnJheTJEKSkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGxldCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgbGV0IHgyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngyVGVuc29yKTtcbiAgICBsZXQgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKTtcblxuICAgIGlmICh4MS5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgIHgxID0geDEucmVzaGFwZShbMSwgeDEuc2l6ZV0pO1xuICAgICAgZHkgPSBkeS5yZXNoYXBlKFsxLCBkeS5zaXplXSk7XG4gICAgfVxuICAgIGlmICh4Mi5zaGFwZS5sZW5ndGggPT09IDEpIHtcbiAgICAgIHgyID0geDIucmVzaGFwZShbeDIuc2l6ZSwgMV0pO1xuICAgICAgZHkgPSBkeS5yZXNoYXBlKFtkeS5zaXplLCAxXSk7XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgLy8geSA9IHgxICogeDJcbiAgICAgIC8vIGR4MSA9IGR5ICogeDJUXG4gICAgICAvLyBkeDIgPSB4MVQgKiBkeVxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MVRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZHgxID0gbWF0aC5tYXRNdWwoXG4gICAgICAgICAgICBkeSBhcyBBcnJheTJELCB4MiBhcyBBcnJheTJELCBNYXRyaXhPcmllbnRhdGlvbi5SRUdVTEFSLFxuICAgICAgICAgICAgTWF0cml4T3JpZW50YXRpb24uVFJBTlNQT1NFRCk7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueDFUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKHRoaXMueDFUZW5zb3Iuc2hhcGUubGVuZ3RoID09PSAxID8gZHgxLmFzMUQoKSA6IGR4MSkpO1xuICAgICAgfVxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy54MlRlbnNvcikpIHtcbiAgICAgICAgY29uc3QgZHgyID0gbWF0aC5tYXRNdWwoXG4gICAgICAgICAgICB4MSBhcyBBcnJheTJELCBkeSBhcyBBcnJheTJELCBNYXRyaXhPcmllbnRhdGlvbi5UUkFOU1BPU0VELFxuICAgICAgICAgICAgTWF0cml4T3JpZW50YXRpb24uUkVHVUxBUik7XG4gICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgIHRoaXMueDJUZW5zb3IsXG4gICAgICAgICAgICBrZWVwKHRoaXMueDJUZW5zb3Iuc2hhcGUubGVuZ3RoID09PSAxID8gZHgyLmFzMUQoKSA6IGR4MikpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBjb252X3V0aWwgZnJvbSAnLi4vbWF0aC9jb252X3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkyRCwgQXJyYXkzRCwgTkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE1heFBvb2wgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBwcml2YXRlIHBhZDogbnVtYmVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yLFxuICAgICAgcHJpdmF0ZSBmaWVsZFNpemU6IG51bWJlciwgcHJpdmF0ZSBzdHJpZGUgPSAxLCBwYWQ/OiBudW1iZXIpIHtcbiAgICBzdXBlcigpO1xuXG4gICAgaWYgKHBhZCAhPSBudWxsKSB7XG4gICAgICB0aGlzLnBhZCA9IHBhZDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5wYWQgPSBjb252X3V0aWwuY29tcHV0ZURlZmF1bHRQYWQoXG4gICAgICAgICAgeFRlbnNvci5zaGFwZSBhcyBbbnVtYmVyLCBudW1iZXIsIG51bWJlcl0sIHRoaXMuZmllbGRTaXplLFxuICAgICAgICAgIHRoaXMuc3RyaWRlKTtcbiAgICB9XG5cbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgdXRpbC5pc0ludCh0aGlzLnBhZCksXG4gICAgICAgIGBUaGUgemVybyBwYWRkaW5nICgke3RoaXMucGFkfSkgbXVzdCBiZSBhbiBpbnRlZ2VyLiBDaGFuZ2UgdGhlIGAgK1xuICAgICAgICAgICAgYHN0cmlkZSBhbmQvb3IgemVybyBwYWQgcGFyYW1ldGVyc2ApO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgaW5mZXJlbmNlQXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnlUZW5zb3IsXG4gICAgICAgICAga2VlcChtYXRoLm1heFBvb2woeCwgdGhpcy5maWVsZFNpemUsIHRoaXMuc3RyaWRlLCB0aGlzLnBhZCkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnhUZW5zb3IpIGFzIEFycmF5M0Q7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKSBhcyBBcnJheTNEO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueFRlbnNvcixcbiAgICAgICAgICBrZWVwKG1hdGgubWF4UG9vbEJhY2twcm9wKFxuICAgICAgICAgICAgICBkeSwgeCwgdGhpcy5maWVsZFNpemUsIHRoaXMuc3RyaWRlLCB0aGlzLnBhZCkpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuLi9ncmFwaF91dGlsJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXksIFNjYWxhcn0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIE11bHRpcGx5IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgLyoqXG4gICAqIEVsZW1lbnQtd2lzZSBtdWx0aXBseSBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB4MVRlbnNvcjogVGVuc29yLCBwcml2YXRlIHgyVGVuc29yOiBUZW5zb3IsXG4gICAgICBwcml2YXRlIHlUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnQoXG4gICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MVRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuc2l6ZUZyb21TaGFwZSh4MlRlbnNvci5zaGFwZSkgPT09IDEgfHxcbiAgICAgICAgICAgIHV0aWwuYXJyYXlzRXF1YWwoeDFUZW5zb3Iuc2hhcGUsIHgyVGVuc29yLnNoYXBlKSxcbiAgICAgICAgJ09uZSBvZiB0MSBvciB0MiBtdXN0IGJlIGEgc2NhbGFyLCBvciB0MSBhbmQgdDIgbXVzdCBoYXZlICcgK1xuICAgICAgICAgICAgJ3RoZSBzYW1lIHNoYXBlJyk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHQxID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngxVGVuc29yKTtcbiAgICBjb25zdCB0MiA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MlRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBsZXQgcmVzdWx0OiBOREFycmF5O1xuICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0MS5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHQxLCB0Mik7XG4gICAgICB9IGVsc2UgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0Mi5zaGFwZSkpIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zY2FsYXJUaW1lc0FycmF5KHQyLCB0MSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKHQxLCB0Mik7XG4gICAgICB9XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMueVRlbnNvciwga2VlcChyZXN1bHQpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB4MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy54MVRlbnNvcik7XG4gICAgY29uc3QgeDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueDJUZW5zb3IpO1xuICAgIGNvbnN0IGR5ID0gZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMueVRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLngxVGVuc29yKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMueDFUZW5zb3Iuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3QgbXVsID0gbWF0aC5lbGVtZW50V2lzZU11bChkeSwgeDIpO1xuXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDFUZW5zb3IsIGtlZXAobWF0aC5zdW0obXVsKSkpO1xuXG4gICAgICAgIH0gZWxzZSBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHgyLnNoYXBlKSkge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy54MVRlbnNvciwga2VlcChtYXRoLnNjYWxhclRpbWVzQXJyYXkoeDIsIGR5KSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngxVGVuc29yLCBrZWVwKG1hdGguZWxlbWVudFdpc2VNdWwoeDIsIGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueDJUZW5zb3IpKSB7XG4gICAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodGhpcy54MlRlbnNvci5zaGFwZSkpIHtcbiAgICAgICAgICBjb25zdCBtdWwgPSBtYXRoLmVsZW1lbnRXaXNlTXVsKGR5LCB4MSk7XG5cbiAgICAgICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy54MlRlbnNvciwga2VlcChtYXRoLnN1bShtdWwpKSk7XG5cbiAgICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUoeDEuc2hhcGUpKSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KFxuICAgICAgICAgICAgICB0aGlzLngyVGVuc29yLCBrZWVwKG1hdGguc2NhbGFyVGltZXNBcnJheSh4MSwgZHkpKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMueDJUZW5zb3IsIGtlZXAobWF0aC5lbGVtZW50V2lzZU11bCh4MSwgZHkpKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtOREFycmF5LCBTY2FsYXJ9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBPcGVyYXRpb24ge1xuICBhYnN0cmFjdCBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCk6XG4gICAgICB2b2lkO1xuXG4gIGFic3RyYWN0IGJhY2tQcm9wKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApOiB2b2lkO1xuXG4gIGRpc3Bvc2VUcmFuc2llbnRBcnJheXMoXG4gICAgICBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5czogVGVuc29yQXJyYXlNYXApIHt9XG5cbiAgZGlzcG9zZSgpIHt9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheX0gZnJvbSAnLi4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4uL3RlbnNvcl9hcnJheV9tYXAnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuLi91dGlsJztcblxuaW1wb3J0IHtPcGVyYXRpb259IGZyb20gJy4vb3AnO1xuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGNsYXNzIFJlZHVjZVN1bSBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIC8qKiBFbGVtZW50LXdpc2UgYWRkIG9wZXJhdGlvbi4gQnJvYWRjYXN0cyBpZiBvbmUgb2YgdGhlIHRlbnNvcnMgaXMgc2NhbGFyLiAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHg6IFRlbnNvciwgcHJpdmF0ZSBvdXRUZW5zb3I6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gICAgdXRpbC5hc3NlcnRTaGFwZXNNYXRjaChvdXRUZW5zb3Iuc2hhcGUsIFtdKTtcbiAgfVxuXG4gIHByaXZhdGUgb25lczogTkRBcnJheTtcblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueCk7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KHRoaXMub3V0VGVuc29yLCBrZWVwKG1hdGguc3VtKHgpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgaWYgKCFncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMueCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dFRlbnNvcik7XG4gICAgICBpZiAodGhpcy5vbmVzID09IG51bGwpIHtcbiAgICAgICAgY29uc3QgeEFycmF5ID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLngpO1xuICAgICAgICB0aGlzLm9uZXMgPSBOREFycmF5Lnplcm9zTGlrZSh4QXJyYXkpO1xuICAgICAgICB0aGlzLm9uZXMuZmlsbCgxKTtcbiAgICAgIH1cbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLngsIGtlZXAobWF0aC5zY2FsYXJUaW1lc0FycmF5KGR5LCB0aGlzLm9uZXMpKSk7XG4gICAgfSk7XG4gIH1cbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4uL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4uL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4uL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vdXRpbCc7XG5cbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wJztcblxuZXhwb3J0IGNsYXNzIFJlc2hhcGU8VDEgZXh0ZW5kcyBOREFycmF5LCBUMiBleHRlbmRzIE5EQXJyYXk+IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB4VGVuc29yOiBUZW5zb3IsIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICBjb25zdCB4U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh4VGVuc29yLnNoYXBlKTtcbiAgICBjb25zdCB5U2l6ZSA9IHV0aWwuc2l6ZUZyb21TaGFwZSh5VGVuc29yLnNoYXBlKTtcbiAgICB1dGlsLmFzc2VydChcbiAgICAgICAgeFNpemUgPT09IHlTaXplLFxuICAgICAgICBgVGhlIGlucHV0IHNpemUgKCR7eFNpemV9KSBhbmQgb3V0cHV0IHNpemUgKCR7eVNpemV9KSBtdXN0IG1hdGNoYCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMueFRlbnNvcikgYXMgVDE7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KFxuICAgICAgICAgIHRoaXMueVRlbnNvciwga2VlcChtYXRoLnJlc2hhcGU8VDEsIFQyPih4LCB0aGlzLnlUZW5zb3Iuc2hhcGUpKSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgZHkgPSBncmFkaWVudEFycmF5cy5nZXQodGhpcy55VGVuc29yKSBhcyBUMjtcblxuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICB0aGlzLnhUZW5zb3IsIGtlZXAobWF0aC5yZXNoYXBlPFQyLCBUMT4oZHksIHRoaXMueFRlbnNvci5zaGFwZSkpKTtcbiAgICB9KTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge1RlbnNvcn0gZnJvbSAnLi4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7QXJyYXkxRCwgTkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBTb2Z0bWF4IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBsb2dpdHNUZW5zb3I6IFRlbnNvciwgcHJpdmF0ZSBvdXRwdXQ6IFRlbnNvcikge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGxvZ2l0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5sb2dpdHNUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgcmV0dXJuIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5vdXRwdXQsIGtlZXAobWF0aC5zb2Z0bWF4KGxvZ2l0cykpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJhY2tQcm9wKCkge1xuICAgIHRocm93IEVycm9yKCdTb2Z0bWF4IGJhY2twcm9wIGlzIG5vdCB5ZXQgaW1wbGVtZW50ZWQnKTtcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgU29mdG1heENyb3NzRW50cm9weUNvc3QgZXh0ZW5kcyBPcGVyYXRpb24ge1xuICBjb25zdHJ1Y3RvcihcbiAgICAgIHByaXZhdGUgbG9naXRzVGVuc29yOiBUZW5zb3IsIHByaXZhdGUgbGFiZWxUZW5zb3I6IFRlbnNvcixcbiAgICAgIHByaXZhdGUgeVRlbnNvcjogVGVuc29yKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLnNvZnRtYXhUZW5zb3IgPSBuZXcgVGVuc29yKGxvZ2l0c1RlbnNvci5zaGFwZSk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGxvZ2l0cyA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy5sb2dpdHNUZW5zb3IpIGFzIEFycmF5MUQ7XG4gICAgY29uc3QgbGFiZWwgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubGFiZWxUZW5zb3IpIGFzIEFycmF5MUQ7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBjb25zdCBzb2Z0bWF4UmVzdWx0ID0gbWF0aC5zb2Z0bWF4KGxvZ2l0cyk7XG5cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5zb2Z0bWF4VGVuc29yLCBrZWVwKHNvZnRtYXhSZXN1bHQpKTtcbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQoXG4gICAgICAgICAgdGhpcy55VGVuc29yLFxuICAgICAgICAgIGtlZXAoY3Jvc3NFbnRyb3B5Q29zdChtYXRoLCBzb2Z0bWF4UmVzdWx0LCBsYWJlbCwgdGhpcy5lcHNpbG9uKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgYmFja1Byb3AoXG4gICAgICBtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IHNvZnRtYXggPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuc29mdG1heFRlbnNvcik7XG4gICAgY29uc3QgbGFiZWwgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMubGFiZWxUZW5zb3IpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgZ3JhZGllbnRBcnJheXMuc2V0KHRoaXMubG9naXRzVGVuc29yLCBrZWVwKG1hdGguc3ViKHNvZnRtYXgsIGxhYmVsKSkpO1xuICAgIH0pO1xuICB9XG5cbiAgZGlzcG9zZVRyYW5zaWVudEFycmF5cyhcbiAgICAgIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGluZmVyZW5jZUFycmF5cy5kaXNwb3NlQXJyYXkodGhpcy5zb2Z0bWF4VGVuc29yKTtcbiAgfVxuXG4gIGRpc3Bvc2UoKSB7XG4gICAgdGhpcy5lcHNpbG9uLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIHByaXZhdGUgc29mdG1heFRlbnNvcjogVGVuc29yO1xuICBwcml2YXRlIGVwc2lsb24gPSBTY2FsYXIubmV3KDFlLTUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY3Jvc3NFbnRyb3B5Q29zdChcbiAgICBtYXRoOiBOREFycmF5TWF0aCwgeTogQXJyYXkxRCwgdGFyZ2V0OiBBcnJheTFELCBlcHNpbG9uOiBTY2FsYXIpOiBTY2FsYXIge1xuICB1dGlsLmFzc2VydChcbiAgICAgIHkuc2l6ZSA9PT0gdGFyZ2V0LnNpemUsICdUaGUgb3V0cHV0IGFuZCB0YXJnZXQgbXVzdCBiZSB0aGUgc2FtZSBzaXplJyk7XG5cbiAgcmV0dXJuIG1hdGguc2NvcGUoKCkgPT4ge1xuICAgIGNvbnN0IHlQbHVzRXBzID0gbWF0aC5zY2FsYXJQbHVzQXJyYXkoZXBzaWxvbiwgeSk7XG4gICAgY29uc3QgbG9nT3V0cHV0ID0gbWF0aC5sb2coeVBsdXNFcHMpO1xuICAgIGNvbnN0IHRhckxvZ091dHB1dCA9IG1hdGguZWxlbWVudFdpc2VNdWwodGFyZ2V0LCBsb2dPdXRwdXQpO1xuICAgIGNvbnN0IGNvc3RWZWN0b3IgPSBtYXRoLm5lZyh0YXJMb2dPdXRwdXQpO1xuICAgIHJldHVybiBtYXRoLnN1bShjb3N0VmVjdG9yKTtcbiAgfSk7XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbi8qKlxuICogU3BsaXQgb3BzIGFyZSB1c2VkIHRvIGFjY3VtdWxhdGUgYmFja3Byb3AgZGVyaXZhdGl2ZXMgd2hlbiBhIG5vZGUncyBvdXRwdXRcbiAqIHRlbnNvciBpcyBjb25zdW1lZCBieSBtdWx0aXBsZSBub2Rlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFNwbGl0IGV4dGVuZHMgT3BlcmF0aW9uIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBpbnB1dDogVGVuc29yLCBwcml2YXRlIG91dHB1dHM6IFRlbnNvcltdKSB7XG4gICAgc3VwZXIoKTtcbiAgICBvdXRwdXRzLmZvckVhY2gob3V0cHV0ID0+IHtcbiAgICAgIHV0aWwuYXNzZXJ0U2hhcGVzTWF0Y2goaW5wdXQuc2hhcGUsIG91dHB1dC5zaGFwZSk7XG4gICAgfSk7XG4gIH1cblxuICBmZWVkRm9yd2FyZChtYXRoOiBOREFycmF5TWF0aCwgaW5mZXJlbmNlQXJyYXlzOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIGNvbnN0IGlucHV0QXJyYXkgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMuaW5wdXQpO1xuICAgIHRoaXMub3V0cHV0cy5mb3JFYWNoKG91dHB1dCA9PiB7XG4gICAgICBpbmZlcmVuY2VBcnJheXMuc2V0KG91dHB1dCwgaW5wdXRBcnJheSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgaWYgKCFncmFwaF91dGlsLnNob3VsZEJhY2tQcm9wKHRoaXMuaW5wdXQpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IGR4ID0gbWF0aC5hZGQoXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0cHV0c1swXSksXG4gICAgICAgICAgZ3JhZGllbnRBcnJheXMuZ2V0KHRoaXMub3V0cHV0c1sxXSkpO1xuICAgICAgLy8gU3VtIGFjcm9zcyBhbGwgdGhlIGRlcml2YXRpdmVzIG9mIHRoZSBjb25zdW1lcnMgb2YgdGhpcyBub2RlLlxuICAgICAgdGhpcy5vdXRwdXRzLnNsaWNlKDIpLmZvckVhY2gob3V0cHV0ID0+IHtcbiAgICAgICAgZHggPSBtYXRoLmFkZChkeCwgZ3JhZGllbnRBcnJheXMuZ2V0KG91dHB1dCkpO1xuICAgICAgfSk7XG4gICAgICBncmFkaWVudEFycmF5cy5zZXQodGhpcy5pbnB1dCwga2VlcChkeCkpO1xuICAgIH0pO1xuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7VGVuc29yfSBmcm9tICcuLi9ncmFwaCc7XG5pbXBvcnQgKiBhcyBncmFwaF91dGlsIGZyb20gJy4uL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuLi9tYXRoL25kYXJyYXknO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4uL3V0aWwnO1xuXG5pbXBvcnQge09wZXJhdGlvbn0gZnJvbSAnLi9vcCc7XG5cbmV4cG9ydCBjbGFzcyBTdWJ0cmFjdCBleHRlbmRzIE9wZXJhdGlvbiB7XG4gIHByaXZhdGUgZHlTaXplU2NhbGFyOiBTY2FsYXI7XG5cbiAgLyoqXG4gICAqIEVsZW1lbnQtd2lzZSBzdWJ0cmFjdCBvcGVyYXRpb24uIEJyb2FkY2FzdHMgaWYgb25lIG9mIHRoZSB0ZW5zb3JzIGlzXG4gICAqIHNjYWxhci5cbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSB0MTogVGVuc29yLCBwcml2YXRlIHQyOiBUZW5zb3IsIHByaXZhdGUgb3V0VGVuc29yOiBUZW5zb3IpIHtcbiAgICBzdXBlcigpO1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodDEuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLnNpemVGcm9tU2hhcGUodDIuc2hhcGUpID09PSAxIHx8XG4gICAgICAgICAgICB1dGlsLmFycmF5c0VxdWFsKHQxLnNoYXBlLCB0Mi5zaGFwZSksXG4gICAgICAgICdPbmUgb2YgdDEgb3IgdDIgbXVzdCBiZSBhIHNjYWxhciwgb3IgdDEgYW5kIHQyIG11c3QgaGF2ZSAnICtcbiAgICAgICAgICAgICd0aGUgc2FtZSBzaGFwZScpO1xuICB9XG5cbiAgZmVlZEZvcndhcmQobWF0aDogTkRBcnJheU1hdGgsIGluZmVyZW5jZUFycmF5czogVGVuc29yQXJyYXlNYXApIHtcbiAgICBjb25zdCB0MSA9IGluZmVyZW5jZUFycmF5cy5nZXQodGhpcy50MSk7XG4gICAgY29uc3QgdDIgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDIpO1xuXG4gICAgbWF0aC5zY29wZSgoa2VlcCkgPT4ge1xuICAgICAgbGV0IHJlc3VsdDogTkRBcnJheTtcbiAgICAgIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDEuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguc2NhbGFyTWludXNBcnJheSh0MSwgdDIpO1xuICAgICAgfSBlbHNlIGlmICh1dGlsLmlzU2NhbGFyU2hhcGUodDIuc2hhcGUpKSB7XG4gICAgICAgIHJlc3VsdCA9IG1hdGguYXJyYXlNaW51c1NjYWxhcih0MSwgdDIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gbWF0aC5zdWIodDEsIHQyKTtcbiAgICAgIH1cbiAgICAgIGluZmVyZW5jZUFycmF5cy5zZXQodGhpcy5vdXRUZW5zb3IsIGtlZXAocmVzdWx0KSk7XG4gICAgfSk7XG4gIH1cblxuICBiYWNrUHJvcChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBpbmZlcmVuY2VBcnJheXM6IFRlbnNvckFycmF5TWFwLFxuICAgICAgZ3JhZGllbnRBcnJheXM6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgY29uc3QgdDEgPSBpbmZlcmVuY2VBcnJheXMuZ2V0KHRoaXMudDEpO1xuICAgIGNvbnN0IHQyID0gaW5mZXJlbmNlQXJyYXlzLmdldCh0aGlzLnQyKTtcbiAgICBjb25zdCBkeSA9IGdyYWRpZW50QXJyYXlzLmdldCh0aGlzLm91dFRlbnNvcik7XG5cbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICBpZiAoZ3JhcGhfdXRpbC5zaG91bGRCYWNrUHJvcCh0aGlzLnQxKSkge1xuICAgICAgICBpZiAodXRpbC5pc1NjYWxhclNoYXBlKHRoaXMudDEuc2hhcGUpKSB7XG4gICAgICAgICAgY29uc3Qgc3VtID0gbWF0aC5zdW0oZHkpO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy50MSwga2VlcChtYXRoLmRpdmlkZShzdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnQxLCBrZWVwKGR5KSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGdyYXBoX3V0aWwuc2hvdWxkQmFja1Byb3AodGhpcy50MikpIHtcbiAgICAgICAgaWYgKHV0aWwuaXNTY2FsYXJTaGFwZSh0aGlzLnQyLnNoYXBlKSkge1xuICAgICAgICAgIGNvbnN0IHN1bSA9IG1hdGguc3VtKGR5KTtcbiAgICAgICAgICBjb25zdCBuZWdTdW0gPSBtYXRoLm5lZyhzdW0pO1xuICAgICAgICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aGlzLmR5U2l6ZVNjYWxhciA9IFNjYWxhci5uZXcoZHkuc2l6ZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldChcbiAgICAgICAgICAgICAgdGhpcy50Miwga2VlcChtYXRoLmRpdmlkZShuZWdTdW0sIHRoaXMuZHlTaXplU2NhbGFyKSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGdyYWRpZW50QXJyYXlzLnNldCh0aGlzLnQyLCBrZWVwKG1hdGgubmVnKGR5KSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBkaXNwb3NlKCkge1xuICAgIGlmICh0aGlzLmR5U2l6ZVNjYWxhciAhPSBudWxsKSB7XG4gICAgICB0aGlzLmR5U2l6ZVNjYWxhci5kaXNwb3NlKCk7XG4gICAgfVxuICB9XG59XG4iLCIvKiBDb3B5cmlnaHQgMjAxNyBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSovXG5cbmltcG9ydCB7Tm9kZSwgVGVuc29yLCBWYXJpYWJsZU5vZGV9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5TWF0aH0gZnJvbSAnLi9tYXRoL21hdGgnO1xuaW1wb3J0IHtTZXNzaW9uUnVudGltZX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBPcHRpbWl6ZXIge1xuICBwcm90ZWN0ZWQgdmFyaWFibGVOb2RlczogVmFyaWFibGVOb2RlW107XG4gIHByb3RlY3RlZCBzcGVjaWZpZWRWYXJpYWJsZU5vZGVzOiBWYXJpYWJsZU5vZGVbXXxudWxsO1xuXG4gIGNvbnN0cnVjdG9yKHNwZWNpZmllZFZhcmlhYmxlTGlzdD86IE5vZGVbXSkge1xuICAgIGlmIChzcGVjaWZpZWRWYXJpYWJsZUxpc3QgIT0gbnVsbCkge1xuICAgICAgdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzID0gc3BlY2lmaWVkVmFyaWFibGVMaXN0IGFzIFZhcmlhYmxlTm9kZVtdO1xuICAgIH1cbiAgfVxuXG4gIGFic3RyYWN0IGJlZm9yZUJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgYWZ0ZXJFeGFtcGxlKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCxcbiAgICAgIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKTogdm9pZDtcblxuICBhYnN0cmFjdCBhZnRlckJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsXG4gICAgICBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCk6IHZvaWQ7XG5cbiAgYWJzdHJhY3QgZGlzcG9zZSgpOiB2b2lkO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG4vKipcbiAqIERlZmF1bHQgY29tcGFyaXNvbiBmdW5jdGlvbiBmb3IgdGhlIHByaW9yaXR5IHF1ZXVlLlxuICogQHBhcmFtIGEgVGhlIGZpcnN0IGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSBiIFRoZSBzZWNvbmQgZWxlbWVudCB0byBjb21wYXJlLlxuICogQHJldHVybiBcImEgPiBiXCIgcmV0dXJucyA+IDAuIFwiYSA8IGJcIiByZXR1cm5zIDwgMC4gXCJhID09PSBiXCIgcmV0dXJucyAwLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVmYXVsdENvbXBhcmU8VD4oYTogVCwgYjogVCk6IG51bWJlciB7XG4gIGlmIChhID09PSBiKSB7XG4gICAgcmV0dXJuIDA7XG4gIH0gZWxzZSBpZiAoYSA8IGIpIHtcbiAgICByZXR1cm4gLTE7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIDE7XG4gIH1cbn1cblxuLyoqXG4gKiBBIENvbXBhcmF0b3IgaXMgYSB1c2VyLXByb3ZpZGVkIGZ1bmN0aW9uIHRoYXQgY29tcGFyZXMgdHdvIFQgaW5zdGFuY2VzLiBUaGVcbiAqIGNvbnZlbnRpb24gZm9yIGRlZmF1bHRDb21wYXJlIGlzIGV4cGVjdGVkIHRvIGJlIGZvbGxvd2VkIHRvIG1haW50YWluIHRoZVxuICogYmluYXJ5IG1pbi1oZWFwIGludGVncml0eS5cbiAqIEBwYXJhbSBhIFRoZSBmaXJzdCBlbGVtZW50IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0gYiBUaGUgc2Vjb25kIGVsZW1lbnQgdG8gY29tcGFyZS5cbiAqL1xuZXhwb3J0IHR5cGUgQ29tcGFyYXRvcjxUPiA9IChhOiBULCBiOiBUKSA9PiBudW1iZXI7XG5cbi8qKlxuICogSW5kZXhPYnNlcnZlciBpcyBhIHVzZXItcHJvdmlkZWQgY2FsbGJhY2sgdGhhdCBpbmZvcm1zIHRoZSBjYWxsZXIgd2hlbiBhblxuICogZWxlbWVudCBpbiB0aGUgcHJpb3JpdHkgcXVldWUncyBiaW5hcnkgbWluLWhlYXAgaGFzIGJlZW4gcmVsb2NhdGVkLlxuICogQHBhcmFtIHQgVGhlIGVsZW1lbnQgdGhhdCB3YXMgcmVsb2NhdGVkLlxuICogQHBhcmFtIG5ld0luZGV4IFRoZSBuZXcgbG9jYXRpb24gaW4gdGhlIGJpbmFyeSBtaW4taGVhcCBvZiB0aGUgZWxlbWVudC5cbiAqL1xuZXhwb3J0IHR5cGUgSW5kZXhPYnNlcnZlcjxUPiA9ICh0OiBULCBuZXdJbmRleDogbnVtYmVyKSA9PiB2b2lkO1xuXG4vKipcbiAqIEEgcHJpb3JpdHkgcXVldWUsIGltcGxlbWVudGVkIGluIHRlcm1zIG9mIGEgYmluYXJ5IG1pbi1oZWFwLiBMb3dlciBwcmlvcml0eVxuICogbnVtYmVycyBhcmUgY29uc2lkZXJlZCBoaWdoZXIgcHJpb3JpdHkuXG4gKiBlbnF1ZXVlLCBkZXF1ZXVlLCBhbmQgdXBkYXRlIGFyZSBhbGwgTyhsb2cgTikgd2l0aCByZXNwZWN0IHRvIHRoZSBudW1iZXIgb2ZcbiAqIGVsZW1lbnRzIGluIHRoZSBxdWV1ZS5cbiAqL1xuZXhwb3J0IGNsYXNzIFByaW9yaXR5UXVldWU8VD4ge1xuICBwcml2YXRlIGhlYXA6IFRbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAcGFyYW0gY29tcGFyYXRvciBBIGZ1bmN0aW9uIHRoYXQgY29tcGFyZXMgdHdvIHF1ZXVlIGVsZW1lbnRzLlxuICAgKiBAcGFyYW0gaW5kZXhPYnNlcnZlciBBbiBvcHRpb25hbCBjYWxsYmFjayByYWlzZWQgd2hlbiB0aGUgcHJpb3JpdHkgcXVldWVcbiAgICogY2hhbmdlcyB0aGUgb3JkZXIgb2YgZWxlbWVudHMgaW4gaXRzIG1pbi1oZWFwLiBVc2VmdWwgZm9yIHRyYWNraW5nIHRoZVxuICAgKiBwb3NpdGlvbnMgb2YgZWxlbWVudHMgdGhhdCBuZWVkIHVwZGF0aW5nLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgICBwcml2YXRlIGNvbXBhcmF0b3I6IENvbXBhcmF0b3I8VD4sXG4gICAgICBwcml2YXRlIGluZGV4T2JzZXJ2ZXI/OiBJbmRleE9ic2VydmVyPFQ+KSB7fVxuXG4gIC8qKlxuICAgKiBBZGQgYW4gZWxlbWVudCB0byB0aGUgcHJpb3JpdHkgcXVldWUuXG4gICAqIEBwYXJhbSB0IFRoZSBlbGVtZW50IHRvIGVucXVldWUuXG4gICAqL1xuICBlbnF1ZXVlKHQ6IFQpIHtcbiAgICB0aGlzLmhlYXAucHVzaCh0KTtcbiAgICB0aGlzLm9uSW5kZXhDaGFuZ2VkKHQsIHRoaXMuaGVhcC5sZW5ndGggLSAxKTtcbiAgICB0aGlzLnNpZnRVcCh0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGFuIGVsZW1lbnQgZnJvbSB0aGUgcHJpb3JpdHkgcXVldWUuXG4gICAqIEByZXR1cm4gVGhlIGVsZW1lbnQgaW4gdGhlIHByaW9yaXR5IHF1ZXVlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHlcbiAgICogKGxvd2VzdCBudW1lcmljIHByaW9yaXR5IHZhbHVlKS5cbiAgICovXG4gIGRlcXVldWUoKTogVCB7XG4gICAgaWYgKHRoaXMuZW1wdHkoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdkZXF1ZXVlIGNhbGxlZCBvbiBlbXB0eSBwcmlvcml0eSBxdWV1ZS4nKTtcbiAgICB9XG4gICAgY29uc3QgdCA9IHRoaXMuaGVhcFswXTtcbiAgICB0aGlzLnN3YXAoMCwgdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIHRoaXMuaGVhcC5wb3AoKTtcbiAgICB0aGlzLnNpZnREb3duKDApO1xuICAgIHJldHVybiB0O1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgYW4gZWxlbWVudCBhdCB0aGUgc3BlY2lmaWVkIGluZGV4LiBUaGlzIGNhbiBiZSBhIGZ1bGwgZWxlbWVudFxuICAgKiByZXBsYWNlbWVudCwgb3IgaXQgY2FuIGJlIGFuIGluLXBsYWNlIHVwZGF0ZS4gVGhlIHByaW9yaXR5IGlzIGFzc3VtZWQgdG8gYmVcbiAgICogY2hhbmdlZCwgYW5kIHRoZSBpbnRlcm5hbCBzdG9yYWdlIGlzIHVwZGF0ZWQuIFRoaXMgZnVuY3Rpb24gaXMgb25seSB1c2VmdWxcbiAgICogaWYgdGhlIHN0b3JhZ2UgaW5kZXggb2YgdGhlIHVwZGF0ZWQgZWxlbWVudCBpcyBrbm93bjsgY29uc3RydWN0IHRoZVxuICAgKiBQcmlvcml0eVF1ZXVlIHdpdGggYW4gSW5kZXhPYnNlcnZlciB0byB0cmFjayBlbGVtZW50IGxvY2F0aW9ucy5cbiAgICogQHBhcmFtIG5ld1QgVGhlIG5ldyBlbGVtZW50IHRvIHJlcGxhY2UgaW4gdGhlIHByaW9yaXR5IHF1ZXVlLlxuICAgKiBAcGFyYW0gaW5kZXggVGhlIGluZGV4IHRvIGluc2VydCB0aGUgbmV3IGVsZW1lbnQgaW50by5cbiAgICovXG4gIHVwZGF0ZShuZXdUOiBULCBpbmRleDogbnVtYmVyKSB7XG4gICAgLyogSWYgdGhlIGVsZW1lbnQgaXMgYXQgdGhlIHZlcnkgZW5kIG9mIHRoZSBoZWFwLCBubyBzaWZ0aW5nIGlzIG5lY2Vzc2FyeSxcbiAgICAgKiBpdCBjYW4gYmUgc2FmZWx5IHJlbW92ZWQuICovXG4gICAgY29uc3QgbGFzdCA9IChpbmRleCA9PT0gdGhpcy5oZWFwLmxlbmd0aCAtIDEpO1xuICAgIGlmICghbGFzdCkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCB0aGlzLmhlYXAubGVuZ3RoIC0gMSk7XG4gICAgfVxuICAgIHRoaXMuaGVhcC5wb3AoKTtcbiAgICBpZiAoIWxhc3QpIHtcbiAgICAgIC8qIFRoZSBlbGVtZW50IGF0ICdpbmRleCcgaGFzIGJlZW4gcmVtb3ZlZCwgYW5kIHJlcGxhY2VkIHdpdGggd2hhdGV2ZXIgd2FzXG4gICAgICAgKiBhdCB0aGUgZW5kIG9mIHRoZSBoZWFwLiBTaW5jZSB0aGF0IGVsZW1lbnQgbWlnaHQgaGF2ZSBjb21lIGZyb20gYVxuICAgICAgICogZGlmZmVyZW50IHN1YnRyZWUgKGFuZCBub3QgYmUgYSBkaXJlY3QgZGVzY2VuZGFudCBvZiB0aGUgbm9kZSBhdFxuICAgICAgICogJ2luZGV4JyksIHdlIG1pZ2h0IG5lZWQgdG8gc2lmdCB0aGlzIG5ldyB2YWx1ZSB1cCBpbnN0ZWFkIG9mIGRvd24uIFRlc3RcbiAgICAgICAqIGJvdGggZGlyZWN0aW9ucywgYW5kIHNpZnQgdG8gd2hlcmV2ZXIgdGhlIG5vZGUgbmVlZHMgdG8gZ28uXG4gICAgICAgKi9cbiAgICAgIGlmICh0aGlzLnNpZnRVcEluZGV4KGluZGV4KSAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5zaWZ0VXAoaW5kZXgpO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLnNpZnREb3duSW5kZXgoaW5kZXgpICE9PSAtMSkge1xuICAgICAgICB0aGlzLnNpZnREb3duKGluZGV4KTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5lbnF1ZXVlKG5ld1QpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByZWRpY2F0ZSBmb3IgdGVzdGluZyB3aGV0aGVyIHRoZSBQcmlvcml0eVF1ZXVlIGlzIGVtcHR5LlxuICAgKiBAcmV0dXJuIFRydWUgaWYgdGhlIFByaW9yaXR5UXVldWUgaXMgZW1wdHksIG90aGVyd2lzZSBGYWxzZS5cbiAgICovXG4gIGVtcHR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmhlYXAubGVuZ3RoID09PSAwO1xuICB9XG5cbiAgcHJpdmF0ZSBvbkluZGV4Q2hhbmdlZCh0OiBULCBuZXdJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKHRoaXMuaW5kZXhPYnNlcnZlcikge1xuICAgICAgdGhpcy5pbmRleE9ic2VydmVyKHQsIG5ld0luZGV4KTtcbiAgICB9XG4gIH1cblxuICAvKlxuICAgKiBTdGFuZGFyZCB6ZXJvLWluZGV4ZWQgYmluYXJ5IGhlYXAgYXJyYXkgbGF5b3V0OlxuICAgKiAgIFBhcmVudChOKSA9IEZsb29yKChOIC0gMSkgLyAyKVxuICAgKiAgIExlZnRDaGlsZChOKSA9IChOICogMikgKyAxXG4gICAqICAgUmlnaHRDaGlsZChOKSA9IChOICogMikgKyAyXG4gICAqL1xuXG4gIHByaXZhdGUgZ2V0UGFyZW50SW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKGluZGV4ID09PSAwKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIHJldHVybiBNYXRoLmZsb29yKChpbmRleCAtIDEpIC8gMik7XG4gIH1cblxuICBwcml2YXRlIGdldExlZnRDaGlsZEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGluZGV4ICogMiArIDE7XG4gICAgcmV0dXJuIGNhbmRpZGF0ZSA8IHRoaXMuaGVhcC5sZW5ndGggPyBjYW5kaWRhdGUgOiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0UmlnaHRDaGlsZEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGluZGV4ICogMiArIDI7XG4gICAgcmV0dXJuIGNhbmRpZGF0ZSA8IHRoaXMuaGVhcC5sZW5ndGggPyBjYW5kaWRhdGUgOiAtMTtcbiAgfVxuXG4gIHByaXZhdGUgc2lmdFVwSW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgY29uc3QgcGFyZW50SW5kZXggPSB0aGlzLmdldFBhcmVudEluZGV4KGluZGV4KTtcbiAgICBpZiAocGFyZW50SW5kZXggPT09IC0xKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbXBhcmUocGFyZW50SW5kZXgsIGluZGV4KSA+IDApIHtcbiAgICAgIHJldHVybiBwYXJlbnRJbmRleDtcbiAgICB9XG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgcHJpdmF0ZSBzaWZ0VXAoaW5kZXg6IG51bWJlcikge1xuICAgIGxldCBzaWZ0SW5kZXggPSB0aGlzLnNpZnRVcEluZGV4KGluZGV4KTtcbiAgICB3aGlsZSAoc2lmdEluZGV4ICE9PSAtMSkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCBzaWZ0SW5kZXgpO1xuICAgICAgaW5kZXggPSBzaWZ0SW5kZXg7XG4gICAgICBzaWZ0SW5kZXggPSB0aGlzLnNpZnRVcEluZGV4KGluZGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNpZnREb3duSW5kZXgoaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gICAgaWYgKGluZGV4ID49IHRoaXMuaGVhcC5sZW5ndGgpIHtcbiAgICAgIHJldHVybiAtMTtcbiAgICB9XG4gICAgbGV0IGxhcmdlc3RDaGlsZEluZGV4ID0gaW5kZXg7XG4gICAgY29uc3QgbGVmdENoaWxkSW5kZXggPSB0aGlzLmdldExlZnRDaGlsZEluZGV4KGluZGV4KTtcbiAgICBpZiAoKGxlZnRDaGlsZEluZGV4ICE9PSAtMSkgJiZcbiAgICAgICAgKHRoaXMuY29tcGFyZShsZWZ0Q2hpbGRJbmRleCwgbGFyZ2VzdENoaWxkSW5kZXgpIDwgMCkpIHtcbiAgICAgIGxhcmdlc3RDaGlsZEluZGV4ID0gbGVmdENoaWxkSW5kZXg7XG4gICAgfVxuICAgIGNvbnN0IHJpZ2h0Q2hpbGRJbmRleCA9IHRoaXMuZ2V0UmlnaHRDaGlsZEluZGV4KGluZGV4KTtcbiAgICBpZiAoKHJpZ2h0Q2hpbGRJbmRleCAhPT0gLTEpICYmXG4gICAgICAgICh0aGlzLmNvbXBhcmUocmlnaHRDaGlsZEluZGV4LCBsYXJnZXN0Q2hpbGRJbmRleCkgPCAwKSkge1xuICAgICAgbGFyZ2VzdENoaWxkSW5kZXggPSByaWdodENoaWxkSW5kZXg7XG4gICAgfVxuICAgIHJldHVybiAobGFyZ2VzdENoaWxkSW5kZXggPT09IGluZGV4KSA/IC0xIDogbGFyZ2VzdENoaWxkSW5kZXg7XG4gIH1cblxuICBwcml2YXRlIHNpZnREb3duKGluZGV4OiBudW1iZXIpIHtcbiAgICBsZXQgc2lmdEluZGV4ID0gdGhpcy5zaWZ0RG93bkluZGV4KGluZGV4KTtcbiAgICB3aGlsZSAoc2lmdEluZGV4ICE9PSAtMSkge1xuICAgICAgdGhpcy5zd2FwKGluZGV4LCBzaWZ0SW5kZXgpO1xuICAgICAgaW5kZXggPSBzaWZ0SW5kZXg7XG4gICAgICBzaWZ0SW5kZXggPSB0aGlzLnNpZnREb3duSW5kZXgoaW5kZXgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY29tcGFyZShhSW5kZXg6IG51bWJlciwgYkluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmNvbXBhcmF0b3IodGhpcy5oZWFwW2FJbmRleF0sIHRoaXMuaGVhcFtiSW5kZXhdKTtcbiAgfVxuXG4gIHByaXZhdGUgc3dhcChhOiBudW1iZXIsIGI6IG51bWJlcikge1xuICAgIGNvbnN0IHRlbXAgPSB0aGlzLmhlYXBbYV07XG4gICAgdGhpcy5oZWFwW2FdID0gdGhpcy5oZWFwW2JdO1xuICAgIHRoaXMuaGVhcFtiXSA9IHRlbXA7XG4gICAgdGhpcy5vbkluZGV4Q2hhbmdlZCh0aGlzLmhlYXBbYV0sIGEpO1xuICAgIHRoaXMub25JbmRleENoYW5nZWQodGhpcy5oZWFwW2JdLCBiKTtcbiAgfVxufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0dyYXBoLCBOb2RlLCBUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0ICogYXMgZ3JhcGhfdXRpbCBmcm9tICcuL2dyYXBoX3V0aWwnO1xuaW1wb3J0IHtJbnB1dFByb3ZpZGVyfSBmcm9tICcuL2lucHV0X3Byb3ZpZGVyJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQgKiBhcyBvcGVyYXRpb25fZW1pdHRlciBmcm9tICcuL29wZXJhdGlvbl9lbWl0dGVyJztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge09wdGltaXplcn0gZnJvbSAnLi9vcHRpbWl6ZXInO1xuaW1wb3J0ICogYXMgc2Vzc2lvbl91dGlsIGZyb20gJy4vc2Vzc2lvbl91dGlsJztcbmltcG9ydCB7VGVuc29yQXJyYXlNYXB9IGZyb20gJy4vdGVuc29yX2FycmF5X21hcCc7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gJy4vdXRpbCc7XG5cbi8qKlxuICogRmVlZEVudHJ5IGFzc29jaWF0ZXMgYSB0ZW5zb3Igd2l0aCB1c2VyLXByb3ZpZGVkIE5EQXJyYXkgZGF0YS5cbiAqL1xuZXhwb3J0IHR5cGUgRmVlZEVudHJ5ID0ge1xuICB0ZW5zb3I6IFRlbnNvcixcbiAgZGF0YTogTkRBcnJheXxJbnB1dFByb3ZpZGVyXG59O1xuXG4vKipcbiAqIEEgRmVlZERpY3Rpb25hcnkgaG9sZHMgYSBtYXAgZnJvbSB0ZW5zb3JzIHRvIHVzZXItcHJvdmlkZWQgTkRBcnJheXMuIEZlZWRcbiAqIGRpY3Rpb25hcmllcyByZXByZXNlbnQgdGhlICdlbnRyeSBwb2ludHMnIG9mIGV2YWx1YXRpb24sIHNpbmNlIGdyYXBoIG5vZGVzXG4gKiB0aGF0IGFyZSByZXBsYWNlZCBieSBmZWVkcyBkb24ndCBuZWVkIHRvIGhhdmUgdGhlaXIgaW5wdXQgbm9kZXMgZXZhbHVhdGVkLlxuICogRmVlZCBkaWN0aW9uYXJpZXMgdXN1YWxseSBwcm92aWRlIE5EQXJyYXkgZGF0YSBmb3IgUGxhY2Vob2xkZXIgbm9kZXMsIGJ1dCBhbnlcbiAqIG5vZGUgaW4gdGhlIGdyYXBoIGNhbiBiZSByZXBsYWNlZCBieSBhIGZlZWQgZGljdGlvbmFyeSBlbnRyeS5cbiAqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBjbGFzcyBGZWVkRGljdGlvbmFyeSB7XG4gIGRpY3Q6IHtbdGVuc29ySUQ6IG51bWJlcl06IEZlZWRFbnRyeX0gPSB7fTtcblxuICAvKipcbiAgICogT3B0aW9uYWxseSBjb25zdHJ1Y3QgYSBGZWVkRGljdGlvbmFyeSBmcm9tIGFuIGFycmF5IG9mIGVudHJpZXMuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBPcHRpb25hbCBhcnJheSBvZiBGZWVkRW50cnkgb2JqZWN0cy5cbiAgICovXG4gIGNvbnN0cnVjdG9yKGZlZWRFbnRyaWVzPzogRmVlZEVudHJ5W10pIHtcbiAgICBpZiAoZmVlZEVudHJpZXMpIHtcbiAgICAgIGZlZWRFbnRyaWVzLmZvckVhY2goZW50cnkgPT4gdGhpcy5kaWN0W2VudHJ5LnRlbnNvci5pZF0gPSBlbnRyeSk7XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBlbnVtIENvc3RSZWR1Y3Rpb24ge1xuICBOT05FLFxuICBTVU0sXG4gIE1FQU5cbn1cblxuLyoqXG4gKiBBIFNlc3Npb24gbWFpbnRhaW5zIHRoZSBydW50aW1lIHN0YXRlIHJlcXVpcmVkIHRvIGVmZmljaWVudGx5IGV2YWx1YXRlIG5vZGVzLlxuICogT24gdGhlaXIgb3duLCBncmFwaCBvYmplY3RzIGFyZSB2ZXJ5IGxpZ2h0d2VpZ2h0IGxvZ2ljYWwgdG9wb2xvZ2llczsgdGhleVxuICogaGF2ZSBubyByZWxhdGlvbnNoaXAgd2l0aCB0aGUgR1BVLiBTZXNzaW9ucyBlbmNhcHN1bGF0ZSB0aGUgZXZhbHVhdGlvbiBvZlxuICogbm9kZXMsIHRoZSBtYW5hZ2VtZW50IG9mIEdQVSByZXNvdXJjZXMsIHRoZSBjYWNoaW5nIG9mIGV2YWx1YXRpb24gcGF0aHMsIGFuZFxuICogYW55dGhpbmcgZWxzZSByZXF1aXJlZCB0byBldmFsdWF0ZSBvciB0cmFpbiBhIG5ldHdvcmsuXG4gKi9cbmV4cG9ydCBjbGFzcyBTZXNzaW9uIHtcbiAgLyoqXG4gICAqIEBwYXJhbSBncmFwaCBUaGUgZ3JhcGggdG8gYXNzb2NpYXRlIHdpdGggdGhpcyBTZXNzaW9uLlxuICAgKiBAcGFyYW0gbWF0aCBUaGUgTkRBcnJheU1hdGggaW50ZXJmYWNlIHRoYXQgdGhpcyBTZXNzaW9uIHNob3VsZCB1c2UuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGdyYXBoOiBHcmFwaCwgcHJpdmF0ZSBtYXRoOiBOREFycmF5TWF0aCkge31cblxuICAvKipcbiAgICogUmVsZWFzZSBhbGwgc3lzdGVtIHJlc291cmNlcyBhc3NvY2lhdGVkIHdpdGggdGhpcyBTZXNzaW9uLlxuICAgKi9cbiAgZGlzcG9zZSgpIHtcbiAgICB0aGlzLmFjdGl2YXRpb25BcnJheU1hcC5kaXNwb3NlKCk7XG4gICAgT2JqZWN0LmtleXModGhpcy5ydW50aW1lQ2FjaGUpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IHJ1bnRpbWUgPSB0aGlzLnJ1bnRpbWVDYWNoZVtrZXldO1xuICAgICAgaWYgKHJ1bnRpbWUub3BlcmF0aW9ucykge1xuICAgICAgICBydW50aW1lLm9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5kaXNwb3NlKCkpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucnVudGltZUNhY2hlID0ge307XG4gICAgaWYgKHRoaXMuYmF0Y2hTaXplU2NhbGFyICE9IG51bGwpIHtcbiAgICAgIHRoaXMuYmF0Y2hTaXplU2NhbGFyLmRpc3Bvc2UoKTtcbiAgICB9XG4gICAgdGhpcy5vbmVTY2FsYXIuZGlzcG9zZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV2YWx1YXRlIGEgbGlzdCBvZiB0ZW5zb3JzLCB1c2luZyB0aGUgcHJvdmlkZWQgZmVlZCBlbnRyaWVzIHRvIHByb3ZpZGVcbiAgICogdXBzdHJlYW0gTkRBcnJheSBpbnB1dC5cbiAgICogV2hlbiB1c2luZyBhIGBOREFycmF5TWF0aGAgb2JqZWN0IGluIHNhZmUgbW9kZSB0aGlzIG11c3QgYmUgdXNlZCBpbiBhXG4gICAqIG1hdGguc2NvcGUoKS5cbiAgICogQHBhcmFtIHRlbnNvcnMgVGhlIGxpc3Qgb2YgdGVuc29ycyB0byBldmFsdWF0ZS5cbiAgICogQHBhcmFtIGZlZWRFbnRyaWVzIExpc3Qgb2YgYEZlZWRFbnRyeWAgdG8gcmVhZCB3aGVuIHJlcGxhY2luZyBncmFwaFxuICAgKiB0ZW5zb3JzIHdpdGggTkRBcnJheXMuXG4gICAqIEByZXR1cm4gVGhlIGNvbXB1dGVkIHZhbHVlcyBvZiB0aGUgdGVuc29ycy5cbiAgICovXG4gIGV2YWxBbGwodGVuc29yczogVGVuc29yW10sIGZlZWRFbnRyaWVzOiBGZWVkRW50cnlbXSk6IE5EQXJyYXlbXSB7XG4gICAgcmV0dXJuIHRoaXMubWF0aC5zY29wZSgoKSA9PiB7XG4gICAgICBjb25zdCBmZWVkID0gbmV3IEZlZWREaWN0aW9uYXJ5KGZlZWRFbnRyaWVzKTtcbiAgICAgIGNvbnN0IHJ1bnRpbWUgPSB0aGlzLmdldE9yQ3JlYXRlUnVudGltZSh0ZW5zb3JzLCBmZWVkKTtcblxuICAgICAgY29uc3QgYWN0aXZhdGlvbnMgPSB0aGlzLmFjdGl2YXRpb25BcnJheU1hcDtcblxuICAgICAgc2Vzc2lvbl91dGlsLmRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICAgICAgICBydW50aW1lLm5vZGVzLCBhY3RpdmF0aW9ucyk7XG4gICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZVRyYW5zaWVudE9wZXJhdGlvbkFycmF5cyhcbiAgICAgICAgICBydW50aW1lLm9wZXJhdGlvbnMsIHRoaXMuYWN0aXZhdGlvbkFycmF5TWFwLCB0aGlzLmdyYWRpZW50QXJyYXlNYXApO1xuXG4gICAgICBzZXNzaW9uX3V0aWwuYWRkUGVyc2lzdGVudEFycmF5c1RvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgc2Vzc2lvbl91dGlsLmxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgICAgICAgIGZlZWQsIGFjdGl2YXRpb25zLCB0aGlzLm1hdGgpO1xuXG4gICAgICBydW50aW1lLm9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5mZWVkRm9yd2FyZCh0aGlzLm1hdGgsIGFjdGl2YXRpb25zKSk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdHMgPSB0ZW5zb3JzLm1hcCh4ID0+IGFjdGl2YXRpb25zLmdldCh4KSk7XG4gICAgICB0ZW5zb3JzLmZvckVhY2goeCA9PiBhY3RpdmF0aW9ucy5kZWxldGUoeCkpO1xuXG4gICAgICBzZXNzaW9uX3V0aWwucmVsZWFzZUZlZWREaWN0aW9uYXJ5SW5wdXRzRnJvbVRlbnNvckFycmF5TWFwKFxuICAgICAgICAgIGZlZWQsIGFjdGl2YXRpb25zLCB0aGlzLm1hdGgpO1xuXG4gICAgICByZXR1cm4gcmVzdWx0cztcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZSBhIHRlbnNvciwgdXNpbmcgdGhlIHByb3ZpZGVkIGZlZWQgZW50cmllcyB0byBwcm92aWRlXG4gICAqIHVwc3RyZWFtIE5EQXJyYXkgaW5wdXQuXG4gICAqXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciB0byBldmFsdWF0ZS5cbiAgICogQHBhcmFtIGZlZWRFbnRyaWVzIExpc3Qgb2YgYEZlZWRFbnRyeWAgdG8gcmVhZCB3aGVuIHJlcGxhY2luZyBncmFwaFxuICAgKiB0ZW5zb3JzIHdpdGggTkRBcnJheXMuXG4gICAqIEByZXR1cm4gVGhlIGNvbXB1dGVkIHZhbHVlIG9mIHRoZSB0ZW5zb3IuXG4gICAqL1xuICBldmFsKHRlbnNvcjogVGVuc29yLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10pOiBOREFycmF5IHtcbiAgICByZXR1cm4gdGhpcy5ldmFsQWxsKFt0ZW5zb3JdLCBmZWVkRW50cmllcylbMF07XG4gIH1cblxuICAvKipcbiAgICogVHJhaW5zIGEgYmF0Y2guXG4gICAqIFJldHVybnMgYSByZWR1Y2VkIGNvc3QgaWYgdGhlIGNvc3RSZWR1Y3Rpb24gcGFyYW1ldGVyIGlzIHNldC5cbiAgICogV2hlbiB1c2luZyBhIGBOREFycmF5TWF0aGAgb2JqZWN0IGluIHNhZmUgbW9kZSB0aGlzIG11c3QgYmUgdXNlZCBpbiBhXG4gICAqIG1hdGguc2NvcGUoKS5cbiAgICogQHBhcmFtIGNvc3RUZW5zb3IgQSB0ZW5zb3IgcmVwcmVzZW50aW5nIHRoZSBjb3N0IHRvIG9wdGltaXplLiBTaG91bGQgYmUgYVxuICAgKiBzY2FsYXIuXG4gICAqIEBwYXJhbSBmZWVkRW50cmllcyBGZWVkIGVudHJpZXMgZm9yIHRoaXMgdHJhaW4gcnVuLiBQcm92aWRlcyBpbnB1dHMuXG4gICAqIEBwYXJhbSBiYXRjaFNpemUgQmF0Y2ggc2l6ZSBmb3IgdGhpcyB0cmFpbiBsb29wLlxuICAgKiBAcGFyYW0gb3B0aW1pemVyIEFuIG9wdGltaXplciB0byBwZXJmb3JtIHdlaWdodCB1cGRhdGVzLlxuICAgKiBAcGFyYW0gY29zdFJlZHVjdGlvbiBBbiBvcHRpb24gdG8gYWxsb3cgdGhlIHVzZXIgdG8gZ2V0IGEgc3VtbWVkLCBhdmVyYWdlZCxcbiAgICogb3Igbm8gY29zdCBiYWNrLlxuICAgKiBAcmV0dXJuIFRoZSByZWR1Y2VkIGNvc3QsIGlmIGNvc3QgcmVkdWN0aW9uIGlzIG5vdCBOT05FLiBUaGUgdXNlciBpc1xuICAgKiByZXNwb25zaWJsZSBmb3IgZGlzcG9zaW5nIHRoZSBjb3N0IE5EQXJyYXkgYmV0d2VlbiB0cmFpbiBsb29wcy5cbiAgICovXG4gIHRyYWluKFxuICAgICAgY29zdFRlbnNvcjogVGVuc29yLCBmZWVkRW50cmllczogRmVlZEVudHJ5W10sIGJhdGNoU2l6ZTogbnVtYmVyLFxuICAgICAgb3B0aW1pemVyOiBPcHRpbWl6ZXIsIGNvc3RSZWR1Y3Rpb24gPSBDb3N0UmVkdWN0aW9uLk5PTkUpOiBTY2FsYXIge1xuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmlzU2NhbGFyU2hhcGUoY29zdFRlbnNvci5zaGFwZSksXG4gICAgICAgICdDb3N0IHRlbnNvciBmb3IgdHJhaW5pbmcgbXVzdCBiZSBhIHNjYWxhciB2YWx1ZS4nKTtcblxuICAgIGlmICh0aGlzLnByZXZCYXRjaFNpemUgIT09IGJhdGNoU2l6ZSkge1xuICAgICAgdGhpcy5wcmV2QmF0Y2hTaXplID0gYmF0Y2hTaXplO1xuICAgICAgdGhpcy5iYXRjaFNpemVTY2FsYXIgPSBTY2FsYXIubmV3KGJhdGNoU2l6ZSk7XG4gICAgfVxuXG4gICAgY29uc3QgZmVlZCA9IG5ldyBGZWVkRGljdGlvbmFyeShmZWVkRW50cmllcyk7XG4gICAgc2Vzc2lvbl91dGlsLnRocm93SWZGZWVkRGljdGlvbmFyeUNvbnRhaW5zTkRBcnJheXMoZmVlZCk7XG5cbiAgICBjb25zdCBydW50aW1lID0gdGhpcy5nZXRPckNyZWF0ZVJ1bnRpbWUoW2Nvc3RUZW5zb3JdLCBmZWVkKTtcbiAgICBjb25zdCBpbmZlcmVuY2VPcGVyYXRpb25zID0gcnVudGltZS5vcGVyYXRpb25zO1xuICAgIGNvbnN0IGJhY2tQcm9wT3BlcmF0aW9ucyA9IHJ1bnRpbWUub3BlcmF0aW9ucy5zbGljZSgpLnJldmVyc2UoKTtcbiAgICBjb25zdCBhY3RpdmF0aW9ucyA9IHRoaXMuYWN0aXZhdGlvbkFycmF5TWFwO1xuICAgIGNvbnN0IGdyYWRpZW50cyA9IHRoaXMuZ3JhZGllbnRBcnJheU1hcDtcbiAgICBncmFkaWVudHMuc2V0KGNvc3RUZW5zb3IsIHRoaXMub25lU2NhbGFyKTtcblxuICAgIHNlc3Npb25fdXRpbC5hZGRQZXJzaXN0ZW50QXJyYXlzVG9UZW5zb3JBcnJheU1hcChcbiAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuXG4gICAgb3B0aW1pemVyLmJlZm9yZUJhdGNoKFxuICAgICAgICB0aGlzLm1hdGgsIGJhdGNoU2l6ZSwgcnVudGltZSwgYWN0aXZhdGlvbnMsIGdyYWRpZW50cyk7XG5cbiAgICByZXR1cm4gdGhpcy5tYXRoLnNjb3BlKChrZWVwLCB0cmFjaykgPT4ge1xuICAgICAgbGV0IGNvc3QgPSB0cmFjayhTY2FsYXIubmV3KDApKTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBiYXRjaFNpemU7ICsraSkge1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25PdXRwdXRzKFxuICAgICAgICAgICAgcnVudGltZS5ub2RlcywgYWN0aXZhdGlvbnMpO1xuICAgICAgICBzZXNzaW9uX3V0aWwuZGlzcG9zZUFuZEluaXRpYWxpemVPcGVyYXRpb25JbnB1dEdyYWRpZW50cyhcbiAgICAgICAgICAgIHJ1bnRpbWUubm9kZXMsIGdyYWRpZW50cyk7XG4gICAgICAgIHNlc3Npb25fdXRpbC5kaXNwb3NlVHJhbnNpZW50T3BlcmF0aW9uQXJyYXlzKFxuICAgICAgICAgICAgcnVudGltZS5vcGVyYXRpb25zLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgICBzZXNzaW9uX3V0aWwubG9hZElucHV0c0Zyb21GZWVkRGljdGlvbmFyeVRvVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgICBpbmZlcmVuY2VPcGVyYXRpb25zLmZvckVhY2goXG4gICAgICAgICAgICBvcCA9PiBvcC5mZWVkRm9yd2FyZCh0aGlzLm1hdGgsIGFjdGl2YXRpb25zKSk7XG4gICAgICAgIGJhY2tQcm9wT3BlcmF0aW9ucy5mb3JFYWNoKFxuICAgICAgICAgICAgb3AgPT4gb3AuYmFja1Byb3AodGhpcy5tYXRoLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKSk7XG5cbiAgICAgICAgb3B0aW1pemVyLmFmdGVyRXhhbXBsZSh0aGlzLm1hdGgsIHJ1bnRpbWUsIGFjdGl2YXRpb25zLCBncmFkaWVudHMpO1xuXG4gICAgICAgIHNlc3Npb25fdXRpbC5yZWxlYXNlRmVlZERpY3Rpb25hcnlJbnB1dHNGcm9tVGVuc29yQXJyYXlNYXAoXG4gICAgICAgICAgICBmZWVkLCBhY3RpdmF0aW9ucywgdGhpcy5tYXRoKTtcblxuICAgICAgICBjb3N0ID0gdGhpcy51cGRhdGVDb3N0Rm9yRXhhbXBsZShcbiAgICAgICAgICAgIGNvc3QsIGFjdGl2YXRpb25zLmdldChjb3N0VGVuc29yKSwgY29zdFJlZHVjdGlvbik7XG4gICAgICB9XG5cbiAgICAgIG9wdGltaXplci5hZnRlckJhdGNoKFxuICAgICAgICAgIHRoaXMubWF0aCwgYmF0Y2hTaXplLCBydW50aW1lLCBhY3RpdmF0aW9ucywgZ3JhZGllbnRzKTtcblxuICAgICAgcmV0dXJuIHRoaXMudXBkYXRlQ29zdEZvckJhdGNoKGNvc3QsIGNvc3RSZWR1Y3Rpb24pO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVDb3N0Rm9yRXhhbXBsZShcbiAgICAgIHRvdGFsQ29zdDogU2NhbGFyLCBjdXJyQ29zdDogU2NhbGFyLFxuICAgICAgY29zdFJlZHVjdGlvbjogQ29zdFJlZHVjdGlvbik6IFNjYWxhciB7XG4gICAgaWYgKGNvc3RSZWR1Y3Rpb24gPT09IENvc3RSZWR1Y3Rpb24uTUVBTiB8fFxuICAgICAgICBjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLlNVTSkge1xuICAgICAgcmV0dXJuIHRoaXMubWF0aC5hZGQodG90YWxDb3N0LCBjdXJyQ29zdCk7XG4gICAgfVxuICAgIHJldHVybiB0b3RhbENvc3Q7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZUNvc3RGb3JCYXRjaCh0b3RhbENvc3Q6IFNjYWxhciwgY29zdFJlZHVjdGlvbjogQ29zdFJlZHVjdGlvbik6XG4gICAgICBTY2FsYXIge1xuICAgIGlmIChjb3N0UmVkdWN0aW9uID09PSBDb3N0UmVkdWN0aW9uLk1FQU4pIHtcbiAgICAgIHJldHVybiB0aGlzLm1hdGguZGl2aWRlKHRvdGFsQ29zdCwgdGhpcy5iYXRjaFNpemVTY2FsYXIpO1xuICAgIH1cbiAgICByZXR1cm4gdG90YWxDb3N0O1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRPckNyZWF0ZVJ1bnRpbWUodGVuc29yczogVGVuc29yW10sIGZlZWQ6IEZlZWREaWN0aW9uYXJ5KTpcbiAgICAgIFNlc3Npb25SdW50aW1lIHtcbiAgICBjb25zdCBrZXkgPSB0aGlzLm1ha2VSdW50aW1lQ2FjaGVLZXkodGVuc29ycywgZmVlZCk7XG4gICAgbGV0IHJ1bnRpbWUgPSB0aGlzLnJ1bnRpbWVDYWNoZVtrZXldO1xuICAgIGlmIChydW50aW1lID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGxldCBub2RlcyA9XG4gICAgICAgICAgc2Vzc2lvbl91dGlsLmdldE9yZGVyZWRFdmFsdWF0aW9uU2V0RnJvbUV2YWxUZW5zb3IodGVuc29ycywgZmVlZCk7XG4gICAgICAvLyBJbiBpbmZlcmVuY2UgbW9kZSBzcGxpdCBub2RlcyBhcmUgbm90IG5lZWRlZCwgYnV0IHRoZWlyIGNvc3QgaXNcbiAgICAgIC8vIG5lZ2xpZ2libGUsIGFuZCBhbHdheXMgYWRkaW5nIHRoZW0gaW4gYWxsb3dzIGZvciBjYWNoaW5nIG9mIDEgcnVudGltZVxuICAgICAgLy8gZm9yIGJvdGggdHJhaW4vZXZhbC5cbiAgICAgIG5vZGVzID0gc2Vzc2lvbl91dGlsLmFkZFNwbGl0Tm9kZXMobm9kZXMpO1xuICAgICAgc2Vzc2lvbl91dGlsLnJlbW92ZUZlZWREaWN0aW9uYXJ5Tm9kZXNGcm9tRXZhbHVhdGlvblNldChmZWVkLCBub2Rlcyk7XG4gICAgICBzZXNzaW9uX3V0aWwudGhyb3dFcnJvcklmRXZhbHVhdGlvblNldENvbnRhaW5zUGxhY2Vob2xkZXJOb2Rlcyhub2Rlcyk7XG4gICAgICBjb25zdCBvcGVyYXRpb25zID0gb3BlcmF0aW9uX2VtaXR0ZXIuZW1pdEZyb21HcmFwaE5vZGVzKG5vZGVzKTtcbiAgICAgIHJ1bnRpbWUgPSB7bm9kZXMsIG9wZXJhdGlvbnN9O1xuICAgICAgdGhpcy5ydW50aW1lQ2FjaGVba2V5XSA9IHJ1bnRpbWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJ1bnRpbWU7XG4gIH1cblxuICBwcml2YXRlIG1ha2VSdW50aW1lQ2FjaGVLZXkodGVuc29yczogVGVuc29yW10sIGZlZWQ6IEZlZWREaWN0aW9uYXJ5KTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGVuc29ycy5tYXAoeCA9PiB4LmlkKS5zb3J0KCkuam9pbignXycpICsgJ19fJyArXG4gICAgICAgIE9iamVjdC5rZXlzKGZlZWQuZGljdCkuc29ydCgpLmpvaW4oJ18nKTtcbiAgfVxuXG4gIC8qKiBNYXBzIGVhY2ggb3V0cHV0IHRlbnNvciBvZiB0aGUgZ3JhcGggdG8gaXRzIGFjdGl2YXRpb24gdmFsdWUuICovXG4gIGFjdGl2YXRpb25BcnJheU1hcCA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICAvKiogTWFwcyBlYWNoIHRlbnNvciBvZiB0aGUgZ3JhcGggdG8gaXRzIGRlcml2YXRpdmUgd3J0IHRoZSBjb3N0IGZ1bmN0aW9uLiAqL1xuICBncmFkaWVudEFycmF5TWFwID0gbmV3IFRlbnNvckFycmF5TWFwKCk7XG4gIHByaXZhdGUgcnVudGltZUNhY2hlOiB7W2tleTogc3RyaW5nXTogU2Vzc2lvblJ1bnRpbWV9ID0ge307XG4gIC8qKiBCYXRjaCBzaXplIG9mIHRoZSBwcmV2aW91cyB0cmFpbigpIGNhbGwuICovXG4gIHByaXZhdGUgcHJldkJhdGNoU2l6ZTogbnVtYmVyO1xuICBwcml2YXRlIGJhdGNoU2l6ZVNjYWxhcjogU2NhbGFyO1xuICBwcml2YXRlIG9uZVNjYWxhciA9IFNjYWxhci5uZXcoMSk7XG59XG5cbi8qKiBAaGlkZGVuICovXG5leHBvcnQgdHlwZSBTZXNzaW9uUnVudGltZSA9IHtcbiAgbm9kZXM6IE5vZGVbXTsgb3BlcmF0aW9uczogT3BlcmF0aW9uW107XG59O1xuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge0NvbnN0YW50Tm9kZSwgTm9kZSwgUGxhY2Vob2xkZXJOb2RlLCBTcGxpdE5vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCAqIGFzIGdyYXBoX3V0aWwgZnJvbSAnLi9ncmFwaF91dGlsJztcbmltcG9ydCB7SW5wdXRQcm92aWRlcn0gZnJvbSAnLi9pbnB1dF9wcm92aWRlcic7XG5pbXBvcnQge05EQXJyYXlNYXRofSBmcm9tICcuL21hdGgvbWF0aCc7XG5pbXBvcnQge05EQXJyYXl9IGZyb20gJy4vbWF0aC9uZGFycmF5JztcbmltcG9ydCB7T3BlcmF0aW9ufSBmcm9tICcuL29wcy9vcCc7XG5pbXBvcnQge0ZlZWREaWN0aW9uYXJ5fSBmcm9tICcuL3Nlc3Npb24nO1xuaW1wb3J0IHtUZW5zb3JBcnJheU1hcH0gZnJvbSAnLi90ZW5zb3JfYXJyYXlfbWFwJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi91dGlsJztcblxuLyoqXG4gKiBDcmVhdGVzIGFuIGFycmF5IG9mIGdyYXBoIG5vZGVzIHRoYXQgc3RvcCB0cmF2ZXJzYWwsIGJhc2VkIG9uIHRoZSBjb250ZW50c1xuICogb2YgdGhlIHByb3ZpZGVkIEZlZWREaWN0aW9uYXJ5LiBUaGlzIGlzIGEgc2ltcGxlIDE6MSBleHRyYWN0aW9uIG9mIG5vZGVzIGZyb21cbiAqIHRoZSBGZWVkRGljdGlvbmFyeS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZmVlZERpY3Rpb25hcnkgVGhlIEZlZWREaWN0aW9uYXJ5IHRvIHNjYW4gZm9yIHRlcm1pbmF0aW9uIG5vZGVzLlxuICogQHJldHVybiBhbiBhcnJheSBvZiBOb2RlcyB3aGljaCBoYWx0IHRyYXZlcnNhbCB3aGVuIHZpc2l0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUZXJtaW5hdGluZ05vZGVzRnJvbUZlZWREaWN0aW9uYXJ5KFxuICAgIGZlZWREaWN0aW9uYXJ5OiBGZWVkRGljdGlvbmFyeSk6IE5vZGVbXSB7XG4gIHJldHVybiBPYmplY3Qua2V5cyhmZWVkRGljdGlvbmFyeS5kaWN0KVxuICAgICAgLm1hcCh0ZW5zb3JJRCA9PiBmZWVkRGljdGlvbmFyeS5kaWN0Wyt0ZW5zb3JJRF0udGVuc29yLm5vZGUpO1xufVxuXG4vKipcbiAqIEdpdmVuIGEgdGVuc29yIGFuZCBhIGZlZWQgZGljdGlvbmFyeSwgY29tcHV0ZXMgdGhlIHNldCBvZiBub2RlcyB0aGF0IG5lZWQgdG9cbiAqIGJlIGV2YWx1YXRlZCB0byBwZXJmb3JtIGluZmVyZW5jZS5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbFRlbnNvcnMgVGhlIGxpc3Qgb2YgdGVuc29ycyB0byBldmVudHVhbGx5IGJlIGV2YWx1YXRlZC5cbiAqIEBwYXJhbSBmZWVkRGljdGlvbmFyeSBUaGUgcG9wdWxhdGVkIGZlZWQgZGljdGlvbmFyeS5cbiAqIEByZXR1cm4gVGhlIHNldCBvZiBub2RlcyB0byBldmFsdWF0ZSwgaW4gZXZhbHVhdGlvbiBvcmRlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE9yZGVyZWRFdmFsdWF0aW9uU2V0RnJvbUV2YWxUZW5zb3IoXG4gICAgZXZhbFRlbnNvcnM6IFRlbnNvcltdLCBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnkpOiBOb2RlW10ge1xuICBjb25zdCB0ZXJtaW5hdGluZ05vZGVzID1cbiAgICAgIGdldFRlcm1pbmF0aW5nTm9kZXNGcm9tRmVlZERpY3Rpb25hcnkoZmVlZERpY3Rpb25hcnkpO1xuICBjb25zdCBldmFsTm9kZXMgPSBldmFsVGVuc29ycy5tYXAoeCA9PiB4Lm5vZGUpO1xuICBjb25zdCB1bm9yZGVyZWRFdmFsdWF0aW9uU2V0ID1cbiAgICAgIGdyYXBoX3V0aWwuZ2V0VW5vcmRlcmVkRXZhbHVhdGlvblNldChldmFsTm9kZXMsIHRlcm1pbmF0aW5nTm9kZXMpO1xuICBjb25zdCBvcmRlcmVkRXZhbHVhdGlvblNldCA9XG4gICAgICBncmFwaF91dGlsLmdldE9yZGVyZWRFdmFsdWF0aW9uU2V0KHVub3JkZXJlZEV2YWx1YXRpb25TZXQpO1xuICByZXR1cm4gb3JkZXJlZEV2YWx1YXRpb25TZXQ7XG59XG5cbi8qKlxuICogVHJhdmVyc2VzIHRoZSBwcm92aWRlZCBub2RlIGFycmF5IGFuZCBhZGRzIGFsbCBwZXJzaXN0ZW50IG5vZGUgTkRBcnJheXMgdG9cbiAqIHRoZSBwcm92aWRlZCBUZW5zb3JBcnJheU1hcC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gc2Nhbi5cbiAqIEBwYXJhbSB0ZW5zb3JBcnJheU1hcCBUaGUgbWFwIHRoYXQgcmVjZWl2ZXMgdGhlIE5EQXJyYXlzIGZyb20gcGVyc2lzdGVudFxuICogbm9kZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZGRQZXJzaXN0ZW50QXJyYXlzVG9UZW5zb3JBcnJheU1hcChcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIHRlbnNvckFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKG5vZGUgaW5zdGFuY2VvZiBWYXJpYWJsZU5vZGUgfHwgbm9kZSBpbnN0YW5jZW9mIENvbnN0YW50Tm9kZSkge1xuICAgICAgdGVuc29yQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBub2RlLmRhdGEpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogQGhpZGRlblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VmFyaWFibGVOb2Rlc0Zyb21FdmFsdWF0aW9uU2V0KGV2YWx1YXRpb25TZXQ6IE5vZGVbXSk6XG4gICAgVmFyaWFibGVOb2RlW10ge1xuICBjb25zdCBub2RlczogVmFyaWFibGVOb2RlW10gPSBbXTtcbiAgZXZhbHVhdGlvblNldC5mb3JFYWNoKG5vZGUgPT4ge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgVmFyaWFibGVOb2RlKSB7XG4gICAgICBub2Rlcy5wdXNoKG5vZGUpO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiBub2Rlcztcbn1cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0aHJvd0lmRmVlZERpY3Rpb25hcnlDb250YWluc05EQXJyYXlzKFxuICAgIGZlZWREaWN0aW9uYXJ5OiBGZWVkRGljdGlvbmFyeSkge1xuICBPYmplY3Qua2V5cyhmZWVkRGljdGlvbmFyeS5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICBpZiAoZmVlZERpY3Rpb25hcnkuZGljdFsrdGVuc29ySURdLmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ3RyYWluaW5nIHJlcXVpcmVzIEZlZWREaWN0aW9uYXJ5IGVudHJpZXMgdG8gYmUgSW5wdXRQcm92aWRlcnMnICtcbiAgICAgICAgICAnYW5kIG5vdCBOREFycmF5cy4nKTtcbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEBoaWRkZW5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvYWRJbnB1dHNGcm9tRmVlZERpY3Rpb25hcnlUb1RlbnNvckFycmF5TWFwKFxuICAgIGJhdGNoRmVlZDogRmVlZERpY3Rpb25hcnksIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCwgbWF0aDogTkRBcnJheU1hdGgpIHtcbiAgT2JqZWN0LmtleXMoYmF0Y2hGZWVkLmRpY3QpLmZvckVhY2godGVuc29ySUQgPT4ge1xuICAgIGNvbnN0IGZlZWRFbnRyeSA9IGJhdGNoRmVlZC5kaWN0Wyt0ZW5zb3JJRF07XG5cbiAgICBsZXQgZGF0YTogTkRBcnJheTtcbiAgICBpZiAoZmVlZEVudHJ5LmRhdGEgaW5zdGFuY2VvZiBOREFycmF5KSB7XG4gICAgICBkYXRhID0gZmVlZEVudHJ5LmRhdGEgYXMgTkRBcnJheTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgcHJvdmlkZXIgPSBmZWVkRW50cnkuZGF0YSBhcyBJbnB1dFByb3ZpZGVyO1xuICAgICAgZGF0YSA9IHByb3ZpZGVyLmdldE5leHRDb3B5KG1hdGgpO1xuICAgIH1cblxuICAgIHV0aWwuYXNzZXJ0KFxuICAgICAgICB1dGlsLmFycmF5c0VxdWFsKGZlZWRFbnRyeS50ZW5zb3Iuc2hhcGUsIGRhdGEuc2hhcGUpLFxuICAgICAgICBgRXJyb3IgbG9hZGluZyBGZWVkRW50cnk6IGZlZWRpbmcgTkRBcnJheSBvZiBzaGFwZSAke2RhdGEuc2hhcGV9IGAgK1xuICAgICAgICAgICAgYGRvZXMgbm90IG1hdGNoIFRlbnNvciAoaWQ6ICR7ZmVlZEVudHJ5LnRlbnNvci5pZH0pIHNoYXBlOiBgICtcbiAgICAgICAgICAgIGAke2ZlZWRFbnRyeS50ZW5zb3Iuc2hhcGV9LmApO1xuICAgIGFjdGl2YXRpb25zLnNldChmZWVkRW50cnkudGVuc29yLCBkYXRhKTtcbiAgfSk7XG59XG5cblxuLyoqXG4gKiBAaGlkZGVuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWxlYXNlRmVlZERpY3Rpb25hcnlJbnB1dHNGcm9tVGVuc29yQXJyYXlNYXAoXG4gICAgYmF0Y2hGZWVkOiBGZWVkRGljdGlvbmFyeSwgYWN0aXZhdGlvbnM6IFRlbnNvckFycmF5TWFwLCBtYXRoOiBOREFycmF5TWF0aCkge1xuICBPYmplY3Qua2V5cyhiYXRjaEZlZWQuZGljdCkuZm9yRWFjaCh0ZW5zb3JJRCA9PiB7XG4gICAgY29uc3QgZmVlZEVudHJ5ID0gYmF0Y2hGZWVkLmRpY3RbK3RlbnNvcklEXTtcblxuICAgIGlmICghKGZlZWRFbnRyeS5kYXRhIGluc3RhbmNlb2YgTkRBcnJheSkpIHtcbiAgICAgIGNvbnN0IHByb3ZpZGVyID0gZmVlZEVudHJ5LmRhdGEgYXMgSW5wdXRQcm92aWRlcjtcblxuICAgICAgY29uc3QgZmVlZEVudHJ5QXJyYXkgPSBhY3RpdmF0aW9ucy5nZXQoZmVlZEVudHJ5LnRlbnNvcik7XG4gICAgICBwcm92aWRlci5kaXNwb3NlQ29weShtYXRoLCBmZWVkRW50cnlBcnJheSk7XG4gICAgfVxuXG4gICAgYWN0aXZhdGlvbnMuZGVsZXRlKGZlZWRFbnRyeS50ZW5zb3IpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBSZW1vdmVzIGFsbCBub2RlcyBmcm9tIHRoZSBwcm92aWRlZCBOb2RlIGFycmF5IHdob3NlIG91dHB1dCB0ZW5zb3JzIGV4aXN0IGluXG4gKiB0aGUgcHJvdmlkZWQgZmVlZCBkaWN0aW9uYXJ5LiBBZnRlciBjYWxsaW5nIHRoaXMsIHRoZSBOb2RlIGFycmF5IHNob3VsZFxuICogY29udGFpbiB6ZXJvIFBsYWNlaG9sZGVyIG5vZGVzLCBvciB0aGUgdXNlciBoYXMgZmFpbGVkIHRvIHByb3ZpZGUgYSBmZWVkIGZvclxuICogYSBQbGFjZWhvbGRlciBub2RlLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBmZWVkRGljdGlvbmFyeSBUaGUgRmVlZERpY3Rpb25hcnkgdG8gcHJvY2Vzcy5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBhcnJheSBvZiBub2RlcyB0byByZW1vdmUgaW5wdXQgbm9kZXMgZnJvbS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZUZlZWREaWN0aW9uYXJ5Tm9kZXNGcm9tRXZhbHVhdGlvblNldChcbiAgICBmZWVkRGljdGlvbmFyeTogRmVlZERpY3Rpb25hcnksIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSkge1xuICBsZXQgaSA9IDA7XG4gIHdoaWxlIChpIDwgZXZhbHVhdGlvblNldC5sZW5ndGgpIHtcbiAgICBjb25zdCBub2RlID0gZXZhbHVhdGlvblNldFtpXTtcbiAgICBpZiAoZmVlZERpY3Rpb25hcnkuZGljdFtub2RlLm91dHB1dC5pZF0gIT0gbnVsbCkge1xuICAgICAgZXZhbHVhdGlvblNldC5zcGxpY2UoaSwgMSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICsraTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBEaXNwb3NlcyBhbnkgTkRBcnJheXMgb24gdGhlIHRlbnNvckFycmF5TWFwIGZyb20gb3BlcmF0aW9uIG91dHB1dHMgYW5kIHNldHNcbiAqIHRoZSB2YWx1ZSB0byBudWxsLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBzZXQgb2Ygbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIHRlbnNvckFycmF5TWFwIFRoZSBtYXAgdG8gZGlzcG9zZSBhbmQgaW5pdGlhbGl6ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3Bvc2VBbmRJbml0aWFsaXplT3BlcmF0aW9uT3V0cHV0cyhcbiAgICBldmFsdWF0aW9uU2V0OiBOb2RlW10sIHRlbnNvckFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgaWYgKCFncmFwaF91dGlsLmlzSW5wdXROb2RlKG5vZGUpKSB7XG4gICAgICBpZiAoIWdyYXBoX3V0aWwuaXNQYXNzdGhyb3VnaE5vZGUobm9kZSwgdGVuc29yQXJyYXlNYXApKSB7XG4gICAgICAgIHRlbnNvckFycmF5TWFwLmRpc3Bvc2VBcnJheShub2RlLm91dHB1dCk7XG4gICAgICB9XG4gICAgICB0ZW5zb3JBcnJheU1hcC5zZXQobm9kZS5vdXRwdXQsIG51bGwpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogRGlzcG9zZXMgYW55IE5EQXJyYXlzIG9uIHRoZSB0ZW5zb3JBcnJheU1hcCBmcm9tIGRlcml2YXRpdmVzIG9mIG9wZXJhdGlvblxuICogaW5wdXRzIGFuZCBzZXRzIHRoZSB2YWx1ZSB0byBudWxsLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBldmFsdWF0aW9uU2V0IFRoZSBzZXQgb2Ygbm9kZXMgdG8gYmUgZXZhbHVhdGVkLlxuICogQHBhcmFtIGdyYWRpZW50cyBUaGUgZ3JhZGllbnQgbWFwIHRvIGRpc3Bvc2UgYW5kIGluaXRpYWxpemUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlQW5kSW5pdGlhbGl6ZU9wZXJhdGlvbklucHV0R3JhZGllbnRzKFxuICAgIGV2YWx1YXRpb25TZXQ6IE5vZGVbXSwgZ3JhZGllbnRzOiBUZW5zb3JBcnJheU1hcCkge1xuICBldmFsdWF0aW9uU2V0LmZvckVhY2gobm9kZSA9PiB7XG4gICAgT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpLmZvckVhY2goaW5wdXROYW1lID0+IHtcbiAgICAgIGNvbnN0IGlucHV0ID0gbm9kZS5pbnB1dHNbaW5wdXROYW1lXTtcbiAgICAgIGlmIChncmFkaWVudHMuZ2V0KGlucHV0LCB0cnVlKSAhPT0gZ3JhZGllbnRzLmdldChub2RlLm91dHB1dCwgdHJ1ZSkpIHtcbiAgICAgICAgZ3JhZGllbnRzLmRpc3Bvc2VBcnJheShpbnB1dCk7XG4gICAgICB9XG4gICAgICBncmFkaWVudHMuc2V0KGlucHV0LCBudWxsKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cblxuLyoqXG4gKiBDYWxscyB1bmRlcmx5aW5nIG9wZXJhdGlvbiBkaXNwb3NlVHJhbnNpZW50QXJyYXlzIG1ldGhvZHMgd2hpY2ggY2xlYW4gdXAgYW55XG4gKiBOREFycmF5cyB3aGljaCBvcGVyYXRpb25zIG1heSBoYXZlIGNyZWF0ZWQgZHVyaW5nIGEgcnVuLlxuICpcbiAqIEBoaWRkZW5cbiAqIEBwYXJhbSBvcGVyYXRpb25Ob2RlcyBUaGUgYXJyYXkgb2YgTm9kZXMgdG8gdHJhdmVyc2UuXG4gKiBAcGFyYW0gb3V0cHV0VGVuc29yIFRoZSB0ZW5zb3IgYmVpbmcgZXZhbHVhdGVkLlxuICogQHBhcmFtIG1hcCBUaGUgVGVuc29yQXJyYXlNYXAgdG8gb3BlcmF0ZSBvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3Bvc2VUcmFuc2llbnRPcGVyYXRpb25BcnJheXMoXG4gICAgb3BlcmF0aW9uczogT3BlcmF0aW9uW10sIGFjdGl2YXRpb25zOiBUZW5zb3JBcnJheU1hcCxcbiAgICBncmFkaWVudHM6IFRlbnNvckFycmF5TWFwKSB7XG4gIG9wZXJhdGlvbnMuZm9yRWFjaChvcCA9PiBvcC5kaXNwb3NlVHJhbnNpZW50QXJyYXlzKGFjdGl2YXRpb25zLCBncmFkaWVudHMpKTtcbn1cblxuLyoqXG4gKiBJdGVyYXRlcyB0aGUgcHJvdmlkZWQgTm9kZSBhcnJheSBhbmQgdGhyb3dzIGFuIGV4Y2VwdGlvbiBpZiB0aGVyZSBhcmUgYW55XG4gKiBQbGFjZWhvbGRlciBub2RlcyBwcmVzZW50LiBDYWxsIGFmdGVyIHRoZSBldmFsdWF0aW9uIHNldCBoYXMgYmVlbiBwcnVuZWQgd2l0aFxuICogdGhlIGFjY29tcGFueWluZyBGZWVkRGljdGlvbmFyeSB0byBlbnN1cmUgdGhhdCBhbGwgaW5wdXRzIGhhdmUgYmVlbiByZXNvbHZlZC5cbiAqXG4gKiBAaGlkZGVuXG4gKiBAcGFyYW0gZXZhbHVhdGlvblNldCBUaGUgYXJyYXkgb2Ygbm9kZXMgdG8gc2Nhbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRocm93RXJyb3JJZkV2YWx1YXRpb25TZXRDb250YWluc1BsYWNlaG9sZGVyTm9kZXMoXG4gICAgZXZhbHVhdGlvblNldDogTm9kZVtdKSB7XG4gIGV2YWx1YXRpb25TZXQuZm9yRWFjaChub2RlID0+IHtcbiAgICBpZiAobm9kZSBpbnN0YW5jZW9mIFBsYWNlaG9sZGVyTm9kZSkge1xuICAgICAgY29uc3Qgc2hhcGUgPSAnWycgKyBub2RlLm91dHB1dC5zaGFwZS5qb2luKCcsICcpICsgJ10nO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICdQbGFjZWhvbGRlciBub2RlIFwiJyArIG5vZGUubmFtZSArICdcIiAnICsgc2hhcGUgK1xuICAgICAgICAgICcgbm90IHByZXNlbnQgaW4gZmVlZCBkaWN0aW9uYXJ5LicpO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogSW5qZWN0cyBzcGxpdHMgbm9kZXMgYWZ0ZXIgZXZlcnkgbm9kZSB0aGF0IGhhcyBtdWx0aXBsZSBjb25zdW1lcnMuXG4gKlxuICogQGhpZGRlblxuICogQHBhcmFtIG5vZGVzIFRoZSBub2RlIGxpc3QgaW4gZXZhbHVhdGlvbiBvcmRlci5cbiAqIEByZXR1cm4gVGhlIG5vZGUgbGlzdCB3aXRoIHNwbGl0IG5vZGVzIGluamVjdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYWRkU3BsaXROb2Rlcyhub2RlczogTm9kZVtdKTogTm9kZVtdIHtcbiAgY29uc3Qgbm9kZUlkVG9OdW1Db25zdW1lcnM6IG51bWJlcltdID0gW107XG4gIGNvbnN0IG5vZGVJZFRvU3BsaXROb2RlOiB7W25vZGVJZDogbnVtYmVyXTogU3BsaXROb2RlfSA9IHt9O1xuXG4gIC8vIEZpbmQgbm9kZXMgdGhhdCBoYXZlIG11bHRpcGxlIGNvbnN1bWVycy5cbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IHtcbiAgICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMobm9kZS5pbnB1dHMpO1xuICAgIGtleXMuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgY29uc3QgaW5wdXRUZW5zb3IgPSBub2RlLmlucHV0c1trZXldO1xuICAgICAgY29uc3QgaW5wdXQgPSBpbnB1dFRlbnNvci5ub2RlO1xuICAgICAgaWYgKG5vZGVJZFRvTnVtQ29uc3VtZXJzW2lucHV0LmlkXSA9PSBudWxsKSB7XG4gICAgICAgIG5vZGVJZFRvTnVtQ29uc3VtZXJzW2lucHV0LmlkXSA9IDA7XG4gICAgICB9XG4gICAgICBub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0rKztcbiAgICAgIGlmIChub2RlSWRUb051bUNvbnN1bWVyc1tpbnB1dC5pZF0gPiAxICYmXG4gICAgICAgICAgbm9kZUlkVG9TcGxpdE5vZGVbaW5wdXQuaWRdID09IG51bGwpIHtcbiAgICAgICAgbm9kZUlkVG9TcGxpdE5vZGVbaW5wdXQuaWRdID0gbmV3IFNwbGl0Tm9kZShpbnB1dC5ncmFwaCwgaW5wdXRUZW5zb3IpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcblxuICAvLyBJbmplY3QgYSBzcGxpdCBub2RlIGFmdGVyIGVhY2ggbm9kZSB0aGF0IGhhcyBtdWx0aXBsZSBjb25zdW1lcnMgYW5kXG4gIC8vIHJld2lyZSB0aGUgaW5wdXRzIG9mIHRoZSBjb25zdW1lcnMgdG8gY29uc3VtZSB0aGUgb3V0cHV0IHRlbnNvcnMgb2YgdGhlXG4gIC8vIHNwbGl0IG5vZGUgaW5zdGVhZCBvZiB0aGUgb3JpZ2luYWwgbm9kZS4gRWFjaCBjb25zdW1lciBjb25zdW1lcyBhXG4gIC8vIGRpZmZlcmVudCBvdXRwdXQgdGVuc29yIHNvIHRoYXQgZGVyaXZhdGl2ZXMgYXJlIG5vdCBvdmVyd3JpdHRlbi5cbiAgLy8geC0tPnkgIGJlY29tZXMgeC0tPnMtLT55ICAgd2hlcmUgeSBjb25zdW1lcyB0aGUgMXN0IG91dHB1dCB0ZW5zb3Igb2Ygc1xuICAvLyB8LS0+eiAgICAgICAgICAgICAgfC0tPnogICAgIGFuZCB6IGNvbnN1bWVzIHRoZSAybmQgb3V0cHV0IHRlbnNvciBvZiBzXG4gIGNvbnN0IG5ld05vZGVzOiBOb2RlW10gPSBbXTtcbiAgbm9kZXMuZm9yRWFjaChub2RlID0+IHtcbiAgICBuZXdOb2Rlcy5wdXNoKG5vZGUpO1xuICAgIGlmIChub2RlLmlkIGluIG5vZGVJZFRvU3BsaXROb2RlKSB7XG4gICAgICBjb25zdCBzcGxpdE5vZGUgPSBub2RlSWRUb1NwbGl0Tm9kZVtub2RlLmlkXTtcbiAgICAgIG5ld05vZGVzLnB1c2goc3BsaXROb2RlKTtcbiAgICB9XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKG5vZGUuaW5wdXRzKTtcbiAgICBrZXlzLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGlucHV0VGVuc29yID0gbm9kZS5pbnB1dHNba2V5XTtcbiAgICAgIGNvbnN0IGlucHV0SWQgPSBpbnB1dFRlbnNvci5ub2RlLmlkO1xuICAgICAgaWYgKGlucHV0SWQgaW4gbm9kZUlkVG9TcGxpdE5vZGUpIHtcbiAgICAgICAgbm9kZS5pbnB1dHNba2V5XSA9IG5vZGVJZFRvU3BsaXROb2RlW2lucHV0SWRdLmdldE5ld091dHB1dFRlbnNvcigpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbiAgcmV0dXJuIG5ld05vZGVzO1xufVxuIiwiLyogQ29weXJpZ2h0IDIwMTcgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG49PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0qL1xuXG5pbXBvcnQge05vZGUsIFRlbnNvciwgVmFyaWFibGVOb2RlfSBmcm9tICcuL2dyYXBoJztcbmltcG9ydCB7TkRBcnJheU1hdGh9IGZyb20gJy4vbWF0aC9tYXRoJztcbmltcG9ydCB7TkRBcnJheSwgU2NhbGFyfSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5pbXBvcnQge09wdGltaXplcn0gZnJvbSAnLi9vcHRpbWl6ZXInO1xuaW1wb3J0IHtTZXNzaW9uUnVudGltZX0gZnJvbSAnLi9zZXNzaW9uJztcbmltcG9ydCAqIGFzIHNlc3Npb25fdXRpbCBmcm9tICcuL3Nlc3Npb25fdXRpbCc7XG5pbXBvcnQge1RlbnNvckFycmF5TWFwfSBmcm9tICcuL3RlbnNvcl9hcnJheV9tYXAnO1xuXG5leHBvcnQgY2xhc3MgU0dET3B0aW1pemVyIGV4dGVuZHMgT3B0aW1pemVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBsZWFybmluZ1JhdGU6IG51bWJlciwgc3BlY2lmaWVkVmFyaWFibGVMaXN0PzogTm9kZVtdKSB7XG4gICAgc3VwZXIoc3BlY2lmaWVkVmFyaWFibGVMaXN0KTtcbiAgfVxuXG4gIGJlZm9yZUJhdGNoKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIGJhdGNoU2l6ZTogbnVtYmVyLCBydW50aW1lOiBTZXNzaW9uUnVudGltZSxcbiAgICAgIGFjdGl2YXRpb25BcnJheU1hcDogVGVuc29yQXJyYXlNYXAsIGdyYWRpZW50QXJyYXlNYXA6IFRlbnNvckFycmF5TWFwKSB7XG4gICAgdGhpcy52YXJpYWJsZU5vZGVzID0gdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzID09IG51bGwgP1xuICAgICAgICBzZXNzaW9uX3V0aWwuZ2V0VmFyaWFibGVOb2Rlc0Zyb21FdmFsdWF0aW9uU2V0KHJ1bnRpbWUubm9kZXMpIDpcbiAgICAgICAgdGhpcy5zcGVjaWZpZWRWYXJpYWJsZU5vZGVzO1xuICAgIGlmIChiYXRjaFNpemUgIT09IHRoaXMucHJldkJhdGNoU2l6ZSkge1xuICAgICAgdGhpcy5wcmV2QmF0Y2hTaXplID0gYmF0Y2hTaXplO1xuICAgICAgdGhpcy5jID0gU2NhbGFyLm5ldygtdGhpcy5sZWFybmluZ1JhdGUgLyBiYXRjaFNpemUpO1xuICAgIH1cbiAgICB0aGlzLnZhcmlhYmxlTm9kZXMuZm9yRWFjaChcbiAgICAgICAgbm9kZSA9PiB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLnNldChcbiAgICAgICAgICAgIG5vZGUub3V0cHV0LCBOREFycmF5Lnplcm9zKG5vZGUub3V0cHV0LnNoYXBlKSkpO1xuICB9XG5cbiAgYWZ0ZXJFeGFtcGxlKFxuICAgICAgbWF0aDogTkRBcnJheU1hdGgsIHJ1bnRpbWU6IFNlc3Npb25SdW50aW1lLFxuICAgICAgYWN0aXZhdGlvbkFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCwgZ3JhZGllbnRBcnJheU1hcDogVGVuc29yQXJyYXlNYXApIHtcbiAgICBtYXRoLnNjb3BlKChrZWVwKSA9PiB7XG4gICAgICB0aGlzLnZhcmlhYmxlTm9kZXMhLmZvckVhY2gobm9kZSA9PiB7XG4gICAgICAgIGNvbnN0IGdyYWRpZW50ID0gZ3JhZGllbnRBcnJheU1hcC5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICBjb25zdCBhY2N1bXVsYXRlZEdyYWRpZW50ID0gdGhpcy52YXJpYWJsZUdyYWRpZW50cy5nZXQobm9kZS5vdXRwdXQpO1xuICAgICAgICB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLnNldChcbiAgICAgICAgICAgIG5vZGUub3V0cHV0LCBrZWVwKG1hdGguYWRkKGdyYWRpZW50LCBhY2N1bXVsYXRlZEdyYWRpZW50KSkpO1xuICAgICAgICBhY2N1bXVsYXRlZEdyYWRpZW50LmRpc3Bvc2UoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgYWZ0ZXJCYXRjaChcbiAgICAgIG1hdGg6IE5EQXJyYXlNYXRoLCBiYXRjaFNpemU6IG51bWJlciwgcnVudGltZTogU2Vzc2lvblJ1bnRpbWUsXG4gICAgICBhY3RpdmF0aW9uQXJyYXlNYXA6IFRlbnNvckFycmF5TWFwLCBncmFkaWVudEFycmF5TWFwOiBUZW5zb3JBcnJheU1hcCkge1xuICAgIG1hdGguc2NvcGUoKGtlZXApID0+IHtcbiAgICAgIHRoaXMudmFyaWFibGVOb2RlcyEuZm9yRWFjaChub2RlID0+IHtcbiAgICAgICAgY29uc3Qgb2xkVmFyaWFibGUgPSBhY3RpdmF0aW9uQXJyYXlNYXAuZ2V0KG5vZGUub3V0cHV0KTtcbiAgICAgICAgY29uc3QgZ3JhZGllbnQgPSB0aGlzLnZhcmlhYmxlR3JhZGllbnRzLmdldChub2RlLm91dHB1dCk7XG4gICAgICAgIGNvbnN0IHZhcmlhYmxlID1cbiAgICAgICAgICAgIG1hdGguc2NhbGVkQXJyYXlBZGQodGhpcy5jISwgZ3JhZGllbnQsIHRoaXMub25lISwgb2xkVmFyaWFibGUpO1xuICAgICAgICBhY3RpdmF0aW9uQXJyYXlNYXAuc2V0KG5vZGUub3V0cHV0LCBrZWVwKHZhcmlhYmxlKSk7XG4gICAgICAgIG5vZGUuZGF0YSA9IHZhcmlhYmxlO1xuXG4gICAgICAgIG9sZFZhcmlhYmxlLmRpc3Bvc2UoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cy5kaXNwb3NlKCk7XG4gICAgdGhpcy52YXJpYWJsZUdyYWRpZW50cyA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICB9XG5cbiAgZGlzcG9zZSgpIHtcbiAgICBpZiAodGhpcy5jICE9IG51bGwpIHtcbiAgICAgIHRoaXMuYy5kaXNwb3NlKCk7XG4gICAgfVxuICAgIHRoaXMub25lLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIHNldExlYXJuaW5nUmF0ZShsZWFybmluZ1JhdGU6IG51bWJlcikge1xuICAgIHRoaXMubGVhcm5pbmdSYXRlID0gbGVhcm5pbmdSYXRlO1xuICB9XG5cbiAgcHJpdmF0ZSB2YXJpYWJsZUdyYWRpZW50cyA9IG5ldyBUZW5zb3JBcnJheU1hcCgpO1xuICBwcml2YXRlIHByZXZCYXRjaFNpemU6IG51bWJlcjtcbiAgcHJpdmF0ZSBvbmUgPSBTY2FsYXIubmV3KDEpO1xuICBwcml2YXRlIGM6IFNjYWxhcjtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuaW1wb3J0IHtUZW5zb3J9IGZyb20gJy4vZ3JhcGgnO1xuaW1wb3J0IHtOREFycmF5fSBmcm9tICcuL21hdGgvbmRhcnJheSc7XG5cbi8qKlxuICogVGVuc29yQXJyYXlNYXAgaXMgYW4gaW50ZXJuYWwgbWFwIGZyb20gVGVuc29yIElEcyB0byBOREFycmF5cy4gU2luY2UgTkRBcnJheXNcbiAqIGNhbiBiZSBiYWNrZWQgYnkgV2ViR0wgdGV4dHVyZXMsIHRoZSBUZW5zb3JBcnJheU1hcCBpcyBvbmx5IHVzZWQgaW5zaWRlIG9mIGFcbiAqIFNlc3Npb24uXG4gKi9cbmV4cG9ydCBjbGFzcyBUZW5zb3JBcnJheU1hcCB7XG4gIC8qKlxuICAgKiBBZGQgb3IgcmVwbGFjZSBhbiBlbnRyeSBpbiB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKiBAcGFyYW0gYXJyYXkgVGhlIE5EQXJyYXkgdmFsdWUsIGNhbiBiZSBudWxsLlxuICAgKi9cbiAgc2V0KHRlbnNvcjogVGVuc29yLCBhcnJheTogTkRBcnJheXxudWxsKSB7XG4gICAgdGhpcy5kaWN0W3RlbnNvci5pZF0gPSBhcnJheTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBOREFycmF5IGFzc29jaWF0ZWQgd2l0aCB0aGUgcHJvdmlkZWQgdGVuc29yLiBXaWxsIHRocm93IGFuXG4gICAqIGV4Y2VwdGlvbiBpZiB0aGUgdGVuc29yIGlzIG5vdCBhIGtleSBpbiB0aGUgbWFwLCBvciBpZiB0aGUgYXNzb2NpYXRlZFxuICAgKiBOREFycmF5IGlzIG51bGwuXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciBrZXkuXG4gICAqIEBwYXJhbSBza2lwQ2hlY2tzIEZhbHNlIGJ5IGRlZmF1bHQuIElmIHRydWUgd2lsbCBza2lwIGFsbCBjaGVja3MuXG4gICAqIEByZXR1cm4gVGhlIE5EQXJyYXkgYXNzb2NpYXRlZCB3aXRoIHRoZSB0ZW5zb3IuXG4gICAqL1xuICBnZXQodGVuc29yOiBUZW5zb3IsIHNraXBDaGVja3MgPSBmYWxzZSk6IE5EQXJyYXkge1xuICAgIGlmICghc2tpcENoZWNrcyAmJiB0aGlzLmRpY3RbdGVuc29yLmlkXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RlbnNvciAnICsgdGVuc29yLmlkICsgJyBub3QgaW4gYXJyYXkgbWFwLicpO1xuICAgIH1cbiAgICBjb25zdCBuZGEgPSB0aGlzLmRpY3RbdGVuc29yLmlkXTtcbiAgICBpZiAoIXNraXBDaGVja3MgJiYgbmRhID09PSBudWxsKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RlbnNvciAnICsgdGVuc29yLmlkICsgJyBoYXMgbnVsbCBhcnJheS4nKTtcbiAgICB9XG4gICAgcmV0dXJuIG5kYSE7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRlbnNvci9OREFycmF5IHBhaXIgZnJvbSB0aGUgbWFwLlxuICAgKiBAcGFyYW0gdGVuc29yIFRoZSB0ZW5zb3Iga2V5LlxuICAgKi9cbiAgZGVsZXRlKHRlbnNvcjogVGVuc29yKSB7XG4gICAgZGVsZXRlIHRoaXMuZGljdFt0ZW5zb3IuaWRdO1xuICB9XG5cbiAgZGlzcG9zZUFycmF5KHRlbnNvcjogVGVuc29yKSB7XG4gICAgaWYgKHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgbmRhID0gdGhpcy5kaWN0W3RlbnNvci5pZF07XG4gICAgaWYgKG5kYSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBuZGEuZGlzcG9zZSgpO1xuICAgIHRoaXMuZGljdFt0ZW5zb3IuaWRdID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJuIFRoZSBudW1iZXIgb2YgdGVuc29yL05EQXJyYXkgcGFpcnMgaW4gdGhlIG1hcC5cbiAgICovXG4gIHNpemUoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5kaWN0KS5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogSXRlcmF0ZSBvdmVyIGFsbCBjb250YWluZWQgTkRBcnJheSB2YWx1ZXMgYW5kIGRpc3Bvc2UgdGhlbS5cbiAgICovXG4gIGRpc3Bvc2UoKSB7XG4gICAgT2JqZWN0LmtleXModGhpcy5kaWN0KS5mb3JFYWNoKHRlbnNvcklEID0+IHtcbiAgICAgIGNvbnN0IG5kYSA9IHRoaXMuZGljdFsrdGVuc29ySURdO1xuICAgICAgaWYgKG5kYSkge1xuICAgICAgICBuZGEuZGlzcG9zZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuZGljdCA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIFRlc3RzIHRvIHNlZSBpZiBhIHRlbnNvciBoYXMgYSBudWxsIGFzc29jaWF0ZWQgd2l0aCBpdC4gVGhyb3dzXG4gICAqIGlmIHRoZSB0ZW5zb3IgaXMgbm90IGEga2V5IGluIHRoZSBtYXAuXG4gICAqIEBwYXJhbSB0ZW5zb3IgVGhlIHRlbnNvciBrZXkuXG4gICAqIEByZXR1cm4gVHJ1ZSBpZiB0aGUgYXNzb2NpYXRlZCBOREFycmF5IGlzIG51bGwsIGVsc2UgRmFsc2UuXG4gICAqL1xuICBoYXNOdWxsQXJyYXkodGVuc29yOiBUZW5zb3IpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5kaWN0W3RlbnNvci5pZF0gPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd0ZW5zb3IgJyArIHRlbnNvci5pZCArICcgbm90IGluIGFycmF5IG1hcC4nKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZGljdFt0ZW5zb3IuaWRdID09PSBudWxsO1xuICB9XG5cbiAgcHJpdmF0ZSBkaWN0OiB7W3RlbnNvcklEOiBudW1iZXJdOiBOREFycmF5IHwgbnVsbH0gPSB7fTtcbn1cbiIsIi8qIENvcHlyaWdodCAyMDE3IEdvb2dsZSBJbmMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ki9cblxuZXhwb3J0IHR5cGUgVmVjdG9yID0gbnVtYmVyW10gfCBGbG9hdDY0QXJyYXkgfCBGbG9hdDMyQXJyYXkgfCBJbnQzMkFycmF5IHxcbiAgICBJbnQ4QXJyYXkgfCBJbnQxNkFycmF5O1xuXG4vKiogU2h1ZmZsZXMgdGhlIGFycmF5IHVzaW5nIEZpc2hlci1ZYXRlcyBhbGdvcml0aG0uICovXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gc2h1ZmZsZShhcnJheTogYW55W118VWludDMyQXJyYXl8SW50MzJBcnJheXxcbiAgICAgICAgICAgICAgICAgICAgICAgIEZsb2F0MzJBcnJheSk6IHZvaWQge1xuICBsZXQgY291bnRlciA9IGFycmF5Lmxlbmd0aDtcbiAgbGV0IHRlbXAgPSAwO1xuICBsZXQgaW5kZXggPSAwO1xuICAvLyBXaGlsZSB0aGVyZSBhcmUgZWxlbWVudHMgaW4gdGhlIGFycmF5XG4gIHdoaWxlIChjb3VudGVyID4gMCkge1xuICAgIC8vIFBpY2sgYSByYW5kb20gaW5kZXhcbiAgICBpbmRleCA9IChNYXRoLnJhbmRvbSgpICogY291bnRlcikgfCAwO1xuICAgIC8vIERlY3JlYXNlIGNvdW50ZXIgYnkgMVxuICAgIGNvdW50ZXItLTtcbiAgICAvLyBBbmQgc3dhcCB0aGUgbGFzdCBlbGVtZW50IHdpdGggaXRcbiAgICB0ZW1wID0gYXJyYXlbY291bnRlcl07XG4gICAgYXJyYXlbY291bnRlcl0gPSBhcnJheVtpbmRleF07XG4gICAgYXJyYXlbaW5kZXhdID0gdGVtcDtcbiAgfVxufVxuXG4vKiogQ2xhbXBzIGEgdmFsdWUgdG8gYSBzcGVjaWZpZWQgcmFuZ2UuICovXG5leHBvcnQgZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIHg6IG51bWJlciwgbWF4OiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5tYXgobWluLCBNYXRoLm1pbih4LCBtYXgpKTtcbn1cblxuLyoqIFJldHVybnMgYSBzYW1wbGUgZnJvbSBhIHVuaWZvcm0gW2EsIGJdIGRpc3RyaWJ1dGlvbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kVW5pZm9ybShhOiBudW1iZXIsIGI6IG51bWJlcikge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKSAqIChiIC0gYSkgKyBhO1xufVxuXG4vKipcbiAqIFNhbXBsZXMgZnJvbSBhIGdhdXNzaWFuIGRpc3RyaWJ1dGlvbi5cbiAqXG4gKiBAcGFyYW0gbWVhbiBUaGUgbWVhbi4gRGVmYXVsdCBpcyAwLlxuICogQHBhcmFtIHN0ZERldiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uLiBEZWZhdWx0IGlzIDEuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByYW5kR2F1c3MobWVhbiA9IDAsIHN0ZERldiA9IDEsIHRydW5jYXRlZCA9IGZhbHNlKTogbnVtYmVyIHtcbiAgbGV0IHYxOiBudW1iZXIsIHYyOiBudW1iZXIsIHM6IG51bWJlcjtcbiAgZG8ge1xuICAgIHYxID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHYyID0gMiAqIE1hdGgucmFuZG9tKCkgLSAxO1xuICAgIHMgPSB2MSAqIHYxICsgdjIgKiB2MjtcbiAgfSB3aGlsZSAocyA+IDEpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IE1hdGguc3FydCgtMiAqIE1hdGgubG9nKHMpIC8gcykgKiB2MTtcbiAgaWYgKHRydW5jYXRlZCAmJiByZXN1bHQgPiAyKSB7XG4gICAgcmV0dXJuIHJhbmRHYXVzcyhtZWFuLCBzdGREZXYsIHRydWUpO1xuICB9XG4gIHJldHVybiBtZWFuICsgc3RkRGV2ICogcmVzdWx0O1xufVxuXG4vKiogUmV0dXJucyBzcXVhcmVkIGV1Y2xlZGlhbiBkaXN0YW5jZSBiZXR3ZWVuIHR3byB2ZWN0b3JzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3RTcXVhcmVkKGE6IFZlY3RvciwgYjogVmVjdG9yKTogbnVtYmVyIHtcbiAgbGV0IHJlc3VsdCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYS5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGRpZmYgPSBhW2ldIC0gYltpXTtcbiAgICByZXN1bHQgKz0gZGlmZiAqIGRpZmY7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydChleHByOiBib29sZWFuLCBtc2c6IHN0cmluZykge1xuICBpZiAoIWV4cHIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0U2hhcGVzTWF0Y2goXG4gICAgc2hhcGVBOiBudW1iZXJbXSwgc2hhcGVCOiBudW1iZXJbXSwgZXJyb3JNZXNzYWdlUHJlZml4ID0gJycpOiB2b2lkIHtcbiAgYXNzZXJ0KFxuICAgICAgYXJyYXlzRXF1YWwoc2hhcGVBLCBzaGFwZUIpLFxuICAgICAgZXJyb3JNZXNzYWdlUHJlZml4ICsgYFNoYXBlcyAke3NoYXBlQX0gYW5kICR7c2hhcGVCfSBtdXN0IG1hdGNoYCk7XG59XG5cbi8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKGFycjogYW55W10sIHJldD86IG51bWJlcltdKTogbnVtYmVyW10ge1xuICByZXQgPSAocmV0ID09PSB1bmRlZmluZWQgPyBbXSA6IHJldCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJyLmxlbmd0aDsgKytpKSB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoYXJyW2ldKSkge1xuICAgICAgZmxhdHRlbihhcnJbaV0sIHJldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldC5wdXNoKGFycltpXSk7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQ7XG59XG5cbmV4cG9ydCB0eXBlIEFycmF5RGF0YSA9IG51bWJlcnxudW1iZXJbXXxudW1iZXJbXVtdfG51bWJlcltdW11bXXxudW1iZXJbXVtdW11bXTtcblxuZXhwb3J0IGZ1bmN0aW9uIGluZmVyU2hhcGUoYXJyOiBBcnJheURhdGEpOiBudW1iZXJbXSB7XG4gIGNvbnN0IHNoYXBlOiBudW1iZXJbXSA9IFtdO1xuICB3aGlsZSAoYXJyIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICBzaGFwZS5wdXNoKGFyci5sZW5ndGgpO1xuICAgIGFyciA9IGFyclswXTtcbiAgfVxuICByZXR1cm4gc2hhcGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaXplRnJvbVNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IG51bWJlciB7XG4gIGlmIChzaGFwZS5sZW5ndGggPT09IDApIHtcbiAgICAvLyBTY2FsYXIuXG4gICAgcmV0dXJuIDE7XG4gIH1cbiAgbGV0IHNpemUgPSBzaGFwZVswXTtcbiAgZm9yIChsZXQgaSA9IDE7IGkgPCBzaGFwZS5sZW5ndGg7IGkrKykge1xuICAgIHNpemUgKj0gc2hhcGVbaV07XG4gIH1cbiAgcmV0dXJuIHNpemU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1NjYWxhclNoYXBlKHNoYXBlOiBudW1iZXJbXSk6IGJvb2xlYW4ge1xuICByZXR1cm4gc2hhcGUubGVuZ3RoID09PSAwO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG5leHBvcnQgZnVuY3Rpb24gYXJyYXlzRXF1YWwobjE6IGFueVtdfEZsb2F0MzJBcnJheSwgbjI6IGFueVtdfEZsb2F0MzJBcnJheSkge1xuICBpZiAobjEubGVuZ3RoICE9PSBuMi5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBuMS5sZW5ndGg7IGkrKykge1xuICAgIGlmIChuMVtpXSAhPT0gbjJbaV0pIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0ludChhOiBudW1iZXIpOiBib29sZWFuIHtcbiAgcmV0dXJuIGEgJSAxID09PSAwO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdGFuaCh4OiBudW1iZXIpOiBudW1iZXIge1xuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYW55XG4gIGlmICgoTWF0aCBhcyBhbnkpLnRhbmggIT0gbnVsbCkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1hbnlcbiAgICByZXR1cm4gKE1hdGggYXMgYW55KS50YW5oKHgpO1xuICB9XG4gIGlmICh4ID09PSBJbmZpbml0eSkge1xuICAgIHJldHVybiAxO1xuICB9IGVsc2UgaWYgKHggPT09IC1JbmZpbml0eSkge1xuICAgIHJldHVybiAtMTtcbiAgfSBlbHNlIHtcbiAgICBjb25zdCBlMnggPSBNYXRoLmV4cCgyICogeCk7XG4gICAgcmV0dXJuIChlMnggLSAxKSAvIChlMnggKyAxKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2l6ZVRvU3F1YXJpc2hTaGFwZShzaXplOiBudW1iZXIpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgZm9yIChsZXQgYSA9IE1hdGguZmxvb3IoTWF0aC5zcXJ0KHNpemUpKTsgYSA+IDE7IC0tYSkge1xuICAgIGlmIChzaXplICUgYSA9PT0gMCkge1xuICAgICAgcmV0dXJuIFthLCBzaXplIC8gYV07XG4gICAgfVxuICB9XG4gIHJldHVybiBbMSwgc2l6ZV07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTaHVmZmxlZEluZGljZXMobjogbnVtYmVyKTogVWludDMyQXJyYXkge1xuICBjb25zdCBzaHVmZmxlZEluZGljZXMgPSBuZXcgVWludDMyQXJyYXkobik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbjsgKytpKSB7XG4gICAgc2h1ZmZsZWRJbmRpY2VzW2ldID0gaTtcbiAgfVxuICBzaHVmZmxlKHNodWZmbGVkSW5kaWNlcyk7XG4gIHJldHVybiBzaHVmZmxlZEluZGljZXM7XG59XG4iXX0= diff --git a/demos/nn-art/cppn.ts b/demos/nn-art/cppn.ts new file mode 100644 index 0000000000..5f25b2263e --- /dev/null +++ b/demos/nn-art/cppn.ts @@ -0,0 +1,186 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Array1D, Array2D, gpgpu_util, GPGPUContext, NDArrayMathGPU, Scalar, webgl_util} from '../deeplearnjs'; + +import * as nn_art_util from './nn_art_util'; + +const MAX_LAYERS = 10; + +export type ColorMode = 'rgb'|'rgba'|'hsv'|'hsva'|'yuv'|'yuva'|'bw'; +const colorModeOutputDimensions: {[colorMode in ColorMode]: number} = { + 'rgb': 3, + 'rgba': 4, + 'hsv': 3, + 'hsva': 4, + 'yuv': 3, + 'yuva': 4, + 'bw': 1 +}; + +export type ActivationFunction = 'tanh'|'sin'|'relu'|'step'; +const activationFunctionMap: { + [activationFunction in ActivationFunction]: + (math: NDArrayMathGPU, ndarray: Array2D) => Array2D +} = { + 'tanh': (math: NDArrayMathGPU, ndarray: Array2D) => math.tanh(ndarray), + 'sin': (math: NDArrayMathGPU, ndarray: Array2D) => math.sin(ndarray), + 'relu': (math: NDArrayMathGPU, ndarray: Array2D) => math.relu(ndarray), + 'step': (math: NDArrayMathGPU, ndarray: Array2D) => math.step(ndarray) +}; + +const NUM_IMAGE_SPACE_VARIABLES = 3; // x, y, r +const NUM_LATENT_VARIABLES = 2; + +export class CPPN { + private math: NDArrayMathGPU; + private gl: WebGLRenderingContext; + private gpgpu: GPGPUContext; + private renderShader: WebGLProgram; + private addLatentVariablesShader: WebGLProgram; + + private inputAtlas: Array2D; + private weights: Array2D[] = []; + + private z1Counter = 0; + private z2Counter = 0; + private z1Scale: number; + private z2Scale: number; + private numLayers: number; + + private colorModeNames: ColorMode[] = + ['rgb', 'rgba', 'hsv', 'hsva', 'yuv', 'yuva', 'bw']; + private activationFunctionNames: ActivationFunction[] = + ['tanh', 'sin', 'relu', 'step']; + + private selectedColorModeName: ColorMode; + private selectedActivationFunctionName: ActivationFunction; + + private isInferring = false; + + constructor(private inferenceCanvas: HTMLCanvasElement) { + this.gl = gpgpu_util.createWebGLContext(this.inferenceCanvas); + this.gpgpu = new GPGPUContext(this.gl); + this.math = new NDArrayMathGPU(this.gpgpu); + + const maxTextureSize = webgl_util.queryMaxTextureSize(this.gl); + const canvasSize = Math.floor(Math.sqrt(maxTextureSize)); + this.inferenceCanvas.width = canvasSize; + this.inferenceCanvas.height = canvasSize; + + this.renderShader = nn_art_util.getRenderShader(this.gpgpu, canvasSize); + this.addLatentVariablesShader = nn_art_util.getAddLatentVariablesShader( + this.gpgpu, NUM_IMAGE_SPACE_VARIABLES); + this.inputAtlas = nn_art_util.createInputAtlas( + canvasSize, NUM_IMAGE_SPACE_VARIABLES, NUM_LATENT_VARIABLES); + } + + generateWeights(neuronsPerLayer: number, weightsStdev: number) { + for (let i = 0; i < this.weights.length; i++) { + this.weights[i].dispose(); + } + this.weights = []; + + this.weights.push(Array2D.randTruncatedNormal( + [neuronsPerLayer, NUM_IMAGE_SPACE_VARIABLES + NUM_LATENT_VARIABLES], 0, + weightsStdev)); + for (let i = 0; i < MAX_LAYERS; i++) { + this.weights.push(Array2D.randTruncatedNormal( + [neuronsPerLayer, neuronsPerLayer], 0, weightsStdev)); + } + this.weights.push(Array2D.randTruncatedNormal( + [4 /** max output channels */, neuronsPerLayer], 0, weightsStdev)); + } + + setColorMode(colorMode: ColorMode) { + this.selectedColorModeName = colorMode; + } + + setActivationFunction(activationFunction: ActivationFunction) { + this.selectedActivationFunctionName = activationFunction; + } + + setNumLayers(numLayers: number) { + this.numLayers = numLayers; + } + + setZ1Scale(z1Scale: number) { + this.z1Scale = z1Scale; + } + + setZ2Scale(z2Scale: number) { + this.z2Scale = z2Scale; + } + + start() { + this.isInferring = true; + this.runInferenceLoop(); + } + + private runInferenceLoop() { + if (!this.isInferring) { + return; + } + + const colorModeIndex = + this.colorModeNames.indexOf(this.selectedColorModeName); + const outputDimensions = + colorModeOutputDimensions[this.selectedColorModeName]; + + this.z1Counter += 1 / this.z1Scale; + this.z2Counter += 1 / this.z2Scale; + const z1 = Math.sin(this.z1Counter); + const z2 = Math.cos(this.z2Counter); + + const intermediateResults = []; + + // Add the latent variables. + const addLatentVariablesResultTex = + this.math.getTextureManager().acquireTexture(this.inputAtlas.shape); + nn_art_util.addLatentVariables( + this.gpgpu, this.addLatentVariablesShader, this.inputAtlas.getTexture(), + addLatentVariablesResultTex, this.inputAtlas.shape, z1, z2); + const inputAtlasWithLatentVariables = + Array2D.make(this.inputAtlas.shape, { + texture: addLatentVariablesResultTex, + textureShapeRC: this.inputAtlas.shape + }); + intermediateResults.push(inputAtlasWithLatentVariables); + + let lastOutput = inputAtlasWithLatentVariables; + + this.math.scope(() => { + for (let i = 0; i < this.numLayers; i++) { + const matmulResult = this.math.matMul(this.weights[i], lastOutput); + + lastOutput = (i === this.numLayers - 1) ? + this.math.sigmoid(matmulResult) : + activationFunctionMap[this.selectedActivationFunctionName]( + this.math, matmulResult); + } + nn_art_util.render( + this.gpgpu, this.renderShader, lastOutput.getTexture(), + outputDimensions, colorModeIndex); + }); + + inputAtlasWithLatentVariables.dispose(); + + requestAnimationFrame(() => this.runInferenceLoop()); + } + + stopInferenceLoop() { + this.isInferring = false; + } +} diff --git a/demos/nn-art/nn-art-demo.html b/demos/nn-art/nn-art-demo.html new file mode 100644 index 0000000000..4723393536 --- /dev/null +++ b/demos/nn-art/nn-art-demo.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + Abstract CPPN Art + + + + + + + + + + diff --git a/demos/nn-art/nn-art.html b/demos/nn-art/nn-art.html new file mode 100644 index 0000000000..025624d009 --- /dev/null +++ b/demos/nn-art/nn-art.html @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + diff --git a/demos/nn-art/nn-art.ts b/demos/nn-art/nn-art.ts new file mode 100644 index 0000000000..36fe423d48 --- /dev/null +++ b/demos/nn-art/nn-art.ts @@ -0,0 +1,126 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +import '../demo-header'; +import '../demo-footer'; + +import {Array1D, Array2D, gpgpu_util, GPGPUContext, NDArrayMathGPU, Scalar, webgl_util} from '../deeplearnjs'; +// tslint:disable-next-line:no-unused-variable +import {PolymerElement, PolymerHTMLElement} from '../polymer-spec'; + +import {ActivationFunction, ColorMode, CPPN} from './cppn'; +import * as nn_art_util from './nn_art_util'; + +const CANVAS_UPSCALE_FACTOR = 3; + +const NUM_IMAGE_SPACE_VARIABLES = 3; // x, y, r +const NUM_LATENT_VARIABLES = 2; + +const MAX_NUM_LAYERS = 15; +const MAT_WIDTH = 30; +// Standard deviations for gaussian weight initialization. +const WEIGHTS_STDEV = .6; + +// tslint:disable-next-line:variable-name +export const NNArtPolymer = PolymerElement({is: 'nn-art', properties: {}}); + +export class NNArt extends NNArtPolymer { + private cppn: CPPN; + + private inferenceCanvas: HTMLCanvasElement; + + private z1Scale: number; + private z2Scale: number; + private numLayers: number; + + ready() { + this.inferenceCanvas = + this.querySelector('#inference') as HTMLCanvasElement; + + this.cppn = new CPPN(this.inferenceCanvas); + + this.inferenceCanvas.style.width = + this.inferenceCanvas.width * CANVAS_UPSCALE_FACTOR + 'px'; + this.inferenceCanvas.style.height = + this.inferenceCanvas.height * CANVAS_UPSCALE_FACTOR + 'px'; + + const currentColorElement = + this.querySelector('#colormode') as HTMLInputElement; + this.querySelector('#color-selector')!.addEventListener( + // tslint:disable-next-line:no-any + 'click', (event: any) => { + const colorMode = + (event.target as HTMLElement).getAttribute('data-val') as + ColorMode; + currentColorElement.value = colorMode; + this.cppn.setColorMode(colorMode); + }); + this.cppn.setColorMode('rgb'); + + const currentActivationFnElement = + this.querySelector('#activation-fn') as HTMLInputElement; + this.querySelector('#activation-selector')!.addEventListener( + // tslint:disable-next-line:no-any + 'click', (event: any) => { + const activationFn = + (event.target as HTMLElement).getAttribute('data-val') as + ActivationFunction; + currentActivationFnElement.value = activationFn; + this.cppn.setActivationFunction(activationFn); + }); + this.cppn.setActivationFunction('tanh'); + + const layersSlider = + this.querySelector('#layers-slider') as HTMLInputElement; + const layersCountElement = + this.querySelector('#layers-count') as HTMLDivElement; + layersSlider!.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + this.numLayers = (event as any).target.value; + layersCountElement.innerText = '' + this.numLayers; + this.cppn.setNumLayers(this.numLayers); + }); + this.numLayers = +layersSlider.value; + layersCountElement.innerText = '' + this.numLayers; + this.cppn.setNumLayers(this.numLayers); + + const z1Slider = this.querySelector('#z1-slider') as HTMLInputElement; + z1Slider.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + this.z1Scale = (event as any).target.value; + this.cppn.setZ1Scale(this.z1Scale); + }); + this.z1Scale = +z1Slider.value; + this.cppn.setZ1Scale(this.z1Scale); + + const z2Slider = this.querySelector('#z2-slider') as HTMLInputElement; + z2Slider.addEventListener('input', (event) => { + // tslint:disable-next-line:no-any + this.z2Scale = (event as any).target.value; + this.cppn.setZ2Scale(this.z1Scale); + }); + this.z2Scale = +z2Slider.value; + this.cppn.setZ2Scale(this.z2Scale); + + const randomizeButton = this.querySelector('#random') as HTMLButtonElement; + randomizeButton.addEventListener('click', () => { + this.cppn.generateWeights(MAT_WIDTH, WEIGHTS_STDEV); + }); + + this.cppn.generateWeights(MAT_WIDTH, WEIGHTS_STDEV); + this.cppn.start(); + } +} + +document.registerElement(NNArt.prototype.is, NNArt); diff --git a/demos/nn-art/nn_art_util.ts b/demos/nn-art/nn_art_util.ts new file mode 100644 index 0000000000..5e2a2bb7d0 --- /dev/null +++ b/demos/nn-art/nn_art_util.ts @@ -0,0 +1,179 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Array2D, GPGPUContext, webgl_util} from '../deeplearnjs'; + +export function createInputAtlas( + imageSize: number, inputNumDimensions: number, numLatentVariables: number) { + const coords = new Float32Array( + imageSize * imageSize * (inputNumDimensions + numLatentVariables)); + let dst = 0; + for (let d = 0; d < inputNumDimensions + numLatentVariables; d++) { + for (let i = 0; i < imageSize * imageSize; i++) { + const x = i % imageSize; + const y = Math.floor(i / imageSize); + const coord = imagePixelToNormalizedCoord( + x, y, imageSize, imageSize, numLatentVariables); + coords[dst++] = coord[d]; + } + } + + return Array2D.new( + [inputNumDimensions + numLatentVariables, imageSize * imageSize], coords); +} + +export function getAddLatentVariablesShader( + gpgpu: GPGPUContext, inputNumDimensions: number): WebGLProgram { + const fragmentShaderSource = ` + precision highp float; + uniform sampler2D source; + varying vec2 resultUV; + + uniform vec2 z; + + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + vec2 outputCR = floor(gl_FragCoord.xy); + if (outputCR[1] == ${inputNumDimensions}.0) { + gl_FragColor = vec4(z[0], 0, 0, 0); + } else if (outputCR[1] > ${inputNumDimensions}.0) { + gl_FragColor = vec4(z[1], 0, 0, 0); + } else { + gl_FragColor = texture2D(source, resultUV); + } + }`; + return gpgpu.createProgram(fragmentShaderSource); +} + +export function addLatentVariables( + gpgpu: GPGPUContext, addZShader: WebGLProgram, sourceTex: WebGLTexture, + resultTex: WebGLTexture, shapeRowCol: [number, number], z1: number, + z2: number) { + gpgpu.setOutputMatrixTexture(resultTex, shapeRowCol[0], shapeRowCol[1]); + gpgpu.setProgram(addZShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + const zLoc = gpgpu.getUniformLocation('z'); + gpgpu.gl.uniform2f(zLoc, z1, z2); + gpgpu.executeProgram(); +} + +export function getRenderShader( + gpgpu: GPGPUContext, imageSize: number): WebGLProgram { + const fragmentShaderSource = ` + precision highp float; + uniform sampler2D source; + varying vec2 resultUV; + + uniform int colorMode; + uniform float outputNumDimensions; + + const float destinationSize = ${imageSize}.0; + + const mat3 yuv2rgb = mat3( + 1, 1, 1, + 0, -.34413, 1.772, + 1.402, -.71414, 0); + + vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + } + + void main() { + vec2 outputCR = floor(gl_FragCoord.xy); + float inputC = outputCR.y * destinationSize + outputCR.x; + float u = (inputC + 0.5) / ${imageSize * imageSize}.0; + + vec4 inputR = vec4(0.0, 1.0, 2.0, 3.0); + vec4 v = (inputR + 0.5) / outputNumDimensions; + + vec4 values = vec4( + texture2D(source, vec2(u, v[0])).r, + texture2D(source, vec2(u, v[1])).r, + texture2D(source, vec2(u, v[2])).r, + texture2D(source, vec2(u, v[3])).r); + + if (colorMode == 0) { + // RGB + gl_FragColor = vec4(values.rgb, 1.0); + } else if (colorMode == 1) { + // RGBA + gl_FragColor = values; + } else if (colorMode == 2) { + // HSV + vec3 rgb = hsv2rgb(values.rgb); + gl_FragColor = vec4(rgb, 1.0); + } else if (colorMode == 3) { + // HSVA + vec3 rgb = hsv2rgb(values.rgb); + gl_FragColor = vec4(rgb, values[3]); + } else if (colorMode == 4 || colorMode == 5) { + // YUV + values[0] = clamp(values[0], 0.2, 0.8); + values[1] = values[1] - 0.5; + values[2] = values[2] - 0.5; + vec3 rgb = yuv2rgb * values.rgb; + if (colorMode == 4) { + // YUV + gl_FragColor = vec4(rgb, 1.0); + } else if (colorMode == 5) { + // YUVA + gl_FragColor = vec4(rgb, values.a); + } + } else if (colorMode == 6) { + gl_FragColor = vec4(values[0], values[0], values[0], 1.0); + } + }`; + + return gpgpu.createProgram(fragmentShaderSource); +} + +export function render( + gpgpu: GPGPUContext, renderShader: WebGLProgram, sourceTex: WebGLTexture, + outputNumDimensions: number, colorMode: number) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + const colorModeLoc = gpgpu.getUniformLocation('colorMode'); + gpgpu.gl.uniform1i(colorModeLoc, colorMode); + const outputNumDimensionsLoc = + gpgpu.getUniformLocation('outputNumDimensions'); + gpgpu.gl.uniform1f(outputNumDimensionsLoc, outputNumDimensions); + gpgpu.executeProgram(); +} + +// Normalizes x, y to -.5 <=> +.5, adds a radius term, and pads zeros with the +// number of z parameters that will get added by the add z shader. +export function imagePixelToNormalizedCoord( + x: number, y: number, imageWidth: number, imageHeight: number, + zSize: number): number[] { + const halfWidth = imageWidth * 0.5; + const halfHeight = imageHeight * 0.5; + const normX = (x - halfWidth) / imageWidth; + const normY = (y - halfHeight) / imageHeight; + + const r = Math.sqrt(normX * normX + normY * normY); + + const result = [normX, normY, r]; + + // Pad with zeros the number of latent terms, these get added on the GPU as + // uniforms. + for (let i = 0; i < zSize; i++) { + result.push(0); + } + return result; +} diff --git a/demos/one_plus_one/index.html b/demos/one_plus_one/index.html new file mode 100644 index 0000000000..5d93ccc4fb --- /dev/null +++ b/demos/one_plus_one/index.html @@ -0,0 +1,22 @@ + + + + + +1 + 1 = +
Calculating...
+ + \ No newline at end of file diff --git a/demos/one_plus_one/one_plus_one.ts b/demos/one_plus_one/one_plus_one.ts new file mode 100644 index 0000000000..5839012b6b --- /dev/null +++ b/demos/one_plus_one/one_plus_one.ts @@ -0,0 +1,52 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ +import {Graph, NDArrayMath, NDArrayMathGPU, Scalar, Session, Tensor} from '../deeplearnjs'; + +class Adder { + inputTensorA: Tensor; + inputTensorB: Tensor; + sum: Tensor; + session: Session; + math: NDArrayMath = new NDArrayMathGPU(); + setupSession(): void { + const graph = new Graph(); + + this.inputTensorA = graph.placeholder('A', []); + this.inputTensorB = graph.placeholder('B', []); + this.sum = graph.add(this.inputTensorA, this.inputTensorB); + this.session = new Session(graph, this.math); + } + + computeSum(a: number, b: number): number { + const feeds = [ + {tensor: this.inputTensorA, data: Scalar.new(a)}, + {tensor: this.inputTensorB, data: Scalar.new(b)} + ]; + let result; + this.math.scope(() => { + result = this.session.eval(this.sum, feeds).get(); + }); + return result; + } +} + + +const adder = new Adder(); +adder.setupSession(); +const result = adder.computeSum(1, 1); + +const outputEl = document.getElementById('output'); +if (!outputEl) throw new Error('output element not found'); +outputEl.innerText = String(result); diff --git a/demos/paint-image/bundle.js b/demos/paint-image/bundle.js new file mode 100644 index 0000000000..d78bd7f054 --- /dev/null +++ b/demos/paint-image/bundle.js @@ -0,0 +1,8333 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + var curLowerBounds; + var curUpperBounds; + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound; + } + else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + }; + InMemoryDataset.prototype.isNormalized = function (dataIndex) { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + }; + InMemoryDataset.prototype.removeNormalization = function (dataIndex) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + if (!this.isNormalized(dataIndex)) { + return; + } + this.dataset[dataIndex] = this.normalizeExamplesToRange(this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + }; + InMemoryDataset.prototype.unnormalizeExamples = function (examples, dataIndex) { + if (!this.isNormalized(dataIndex)) { + return examples; + } + return this.normalizeExamplesToRange(examples, this.normalizationInfo[dataIndex].lowerBound, this.normalizationInfo[dataIndex].upperBound, this.normalizationInfo[dataIndex].minValues, this.normalizationInfo[dataIndex].maxValues); + }; + InMemoryDataset.prototype.dispose = function () { + if (this.dataset == null) { + return; + } + for (var i = 0; i < this.dataset.length; i++) { + for (var j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + }; + return InMemoryDataset; +}()); +exports.InMemoryDataset = InMemoryDataset; + +},{"./math/ndarray":23,"./util":87}],8:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_layers_1 = require("./graph_layers"); +var concat3d_util = require("./math/concat3d_util"); +var conv_util = require("./math/conv_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var Graph = (function () { + function Graph() { + this.nodes = []; + this.layers = new graph_layers_1.GraphLayers(this); + } + Graph.prototype.variable = function (name, data) { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + }; + Graph.prototype.placeholder = function (name, shape) { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + }; + Graph.prototype.constant = function (value) { + var finalValue; + if (typeof value === 'number') { + finalValue = ndarray_1.Scalar.new(value); + } + else if (value instanceof ndarray_1.NDArray) { + finalValue = value; + } + else if (value instanceof Array) { + var vals = new Float32Array(util.flatten(value)); + finalValue = ndarray_1.NDArray.make(util.inferShape(value), { values: vals }); + } + else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + }; + Graph.prototype.reshape = function (x, shape) { + return this.addNodeAndReturnOutput(new ReshapeNode(this, 'Reshape', x, shape)); + }; + Graph.prototype.fusedLinearCombination = function (x1, x2, c1, c2) { + return this.addNodeAndReturnOutput(new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + }; + Graph.prototype.add = function (x1, x2) { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + }; + Graph.prototype.subtract = function (x1, x2) { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + }; + Graph.prototype.multiply = function (x1, x2) { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + }; + Graph.prototype.divide = function (x1, x2) { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + }; + Graph.prototype.reduceSum = function (x) { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + }; + Graph.prototype.concat3d = function (x1, x2, axis) { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + }; + Graph.prototype.matmul = function (x1, x2) { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + }; + Graph.prototype.conv2d = function (x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new Convolution2DNode(this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + }; + Graph.prototype.maxPool = function (x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + return this.addNodeAndReturnOutput(new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + }; + Graph.prototype.exp = function (x) { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + }; + Graph.prototype.log = function (x) { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + }; + Graph.prototype.relu = function (x) { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + }; + Graph.prototype.tanh = function (x) { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + }; + Graph.prototype.sigmoid = function (x) { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + }; + Graph.prototype.square = function (x) { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + }; + Graph.prototype.softmax = function (x) { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + }; + Graph.prototype.softmaxCrossEntropyCost = function (x, target) { + return this.addNodeAndReturnOutput(new SoftmaxCrossEntropyCostNode(this, x, target)); + }; + Graph.prototype.meanSquaredCost = function (label, prediction) { + return this.addNodeAndReturnOutput(new MeanSquaredCostNode(this, label, prediction)); + }; + Graph.prototype.argmax = function (x) { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + }; + Graph.prototype.argmaxEquals = function (x1, x2) { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + }; + Graph.prototype.addNodeAndReturnOutput = function (node) { + this.nodes.push(node); + node.validate(); + return node.output; + }; + Graph.prototype.getNodes = function () { + return this.nodes; + }; + return Graph; +}()); +exports.Graph = Graph; +var Tensor = (function () { + function Tensor(shape) { + this.shape = shape; + this.id = Tensor.nextID++; + } + return Tensor; +}()); +Tensor.nextID = 0; +exports.Tensor = Tensor; +var Node = (function () { + function Node(graph, name, inputs, output) { + this.graph = graph; + this.name = name; + this.inputs = inputs; + this.output = output; + this.id = Node.nextID++; + output.node = this; + } + return Node; +}()); +Node.nextID = 0; +exports.Node = Node; +var VariableNode = (function (_super) { + __extends(VariableNode, _super); + function VariableNode(graph, name, data) { + var _this = _super.call(this, graph, name, {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + VariableNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + }; + return VariableNode; +}(Node)); +exports.VariableNode = VariableNode; +var PlaceholderNode = (function (_super) { + __extends(PlaceholderNode, _super); + function PlaceholderNode(graph, name, shape) { + return _super.call(this, graph, name, {}, new Tensor(shape)) || this; + } + PlaceholderNode.prototype.validate = function () { }; + return PlaceholderNode; +}(Node)); +exports.PlaceholderNode = PlaceholderNode; +var ConstantNode = (function (_super) { + __extends(ConstantNode, _super); + function ConstantNode(graph, data) { + var _this = _super.call(this, graph, 'Constant', {}, new Tensor(data.shape)) || this; + _this.data = data; + return _this; + } + ConstantNode.prototype.validate = function () { + util.assert(this.data != null, 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + }; + return ConstantNode; +}(Node)); +exports.ConstantNode = ConstantNode; +var ReshapeNode = (function (_super) { + __extends(ReshapeNode, _super); + function ReshapeNode(graph, name, x, shape) { + var _this = _super.call(this, graph, name, { x: x }, new Tensor(shape)) || this; + _this.name = name; + _this.x = x; + _this.shape = shape; + return _this; + } + ReshapeNode.prototype.validate = function () { + var xSize = util.sizeFromShape(this.x.shape); + var shapeSize = util.sizeFromShape(this.shape); + util.assert(xSize === shapeSize, 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + }; + return ReshapeNode; +}(Node)); +ReshapeNode.X = 'x'; +exports.ReshapeNode = ReshapeNode; +var FusedLinearCombinationNode = (function (_super) { + __extends(FusedLinearCombinationNode, _super); + function FusedLinearCombinationNode(graph, t1, t2, c1, c2) { + var _this = _super.call(this, graph, 'Linear Combination', { t1: t1, t2: t2, c1: c1, c2: c2 }, new Tensor(t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.c1 = c1; + _this.c2 = c2; + return _this; + } + FusedLinearCombinationNode.prototype.validate = function () { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error('Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error('Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + }; + return FusedLinearCombinationNode; +}(Node)); +FusedLinearCombinationNode.T1 = 't1'; +FusedLinearCombinationNode.T2 = 't2'; +FusedLinearCombinationNode.C1 = 'c1'; +FusedLinearCombinationNode.C2 = 'c2'; +exports.FusedLinearCombinationNode = FusedLinearCombinationNode; +var AddNode = (function (_super) { + __extends(AddNode, _super); + function AddNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Add', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + AddNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return AddNode; +}(Node)); +AddNode.T1 = 't1'; +AddNode.T2 = 't2'; +exports.AddNode = AddNode; +var SubtractNode = (function (_super) { + __extends(SubtractNode, _super); + function SubtractNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Subtract', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + SubtractNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return SubtractNode; +}(Node)); +SubtractNode.T1 = 't1'; +SubtractNode.T2 = 't2'; +exports.SubtractNode = SubtractNode; +var MultiplyNode = (function (_super) { + __extends(MultiplyNode, _super); + function MultiplyNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Multiply', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + MultiplyNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return MultiplyNode; +}(Node)); +MultiplyNode.T1 = 't1'; +MultiplyNode.T2 = 't2'; +exports.MultiplyNode = MultiplyNode; +var DivideNode = (function (_super) { + __extends(DivideNode, _super); + function DivideNode(graph, t1, t2) { + var _this = _super.call(this, graph, 'Divide', { t1: t1, t2: t2 }, new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)) || this; + _this.t1 = t1; + _this.t2 = t2; + return _this; + } + DivideNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + }; + return DivideNode; +}(Node)); +DivideNode.T1 = 't1'; +DivideNode.T2 = 't2'; +exports.DivideNode = DivideNode; +var ReduceSumNode = (function (_super) { + __extends(ReduceSumNode, _super); + function ReduceSumNode(graph, x) { + return _super.call(this, graph, 'ReduceSum', { x: x }, new Tensor([])) || this; + } + ReduceSumNode.prototype.validate = function () { }; + return ReduceSumNode; +}(Node)); +ReduceSumNode.X = 'x'; +exports.ReduceSumNode = ReduceSumNode; +var Concat3DNode = (function (_super) { + __extends(Concat3DNode, _super); + function Concat3DNode(graph, x1, x2, axis) { + var _this = _super.call(this, graph, 'Concat3D', { x1: x1, x2: x2 }, new Tensor(concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis))) || this; + _this.x1 = x1; + _this.x2 = x2; + _this.axis = axis; + return _this; + } + Concat3DNode.prototype.validate = function () { + concat3d_util.assertConcat3DShapesMatch(this.x1.shape, this.x2.shape, this.axis); + }; + return Concat3DNode; +}(Node)); +Concat3DNode.X1 = 'x1'; +Concat3DNode.X2 = 'x2'; +Concat3DNode.AXIS = 'axis'; +exports.Concat3DNode = Concat3DNode; +function getMatMulOutputShape(x1Shape, x2Shape) { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } + else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } + else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} +var MatMulNode = (function (_super) { + __extends(MatMulNode, _super); + function MatMulNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'MatMul', { x1: x1, x2: x2 }, new Tensor(getMatMulOutputShape(x1.shape, x2.shape))) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + MatMulNode.prototype.validate = function () { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } + else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert(this.x1.shape[1] === this.x2.shape[0], 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } + else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert(this.x1.shape[0] === this.x2.shape[0], 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } + else { + throw new Error('Error adding matmul op: inputs must be vectors or matrices.'); + } + }; + return MatMulNode; +}(Node)); +MatMulNode.X1 = 'x1'; +MatMulNode.X2 = 'x2'; +exports.MatMulNode = MatMulNode; +var Convolution2DNode = (function (_super) { + __extends(Convolution2DNode, _super); + function Convolution2DNode(graph, x, w, b, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Convolution 2D', { x: x, w: w, b: b }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad))) || this; + _this.x = x; + _this.w = w; + _this.b = b; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + Convolution2DNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert(this.w.shape.length === 4, 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert(this.b.shape.length === 1, 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + util.assert(this.x.shape[2] === this.w.shape[2], 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + }; + return Convolution2DNode; +}(Node)); +Convolution2DNode.X = 'x'; +Convolution2DNode.W = 'w'; +Convolution2DNode.B = 'b'; +exports.Convolution2DNode = Convolution2DNode; +var MaxPoolNode = (function (_super) { + __extends(MaxPoolNode, _super); + function MaxPoolNode(graph, x, fieldSize, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this, graph, 'Max pool', { x: x }, new Tensor(conv_util.computeOutputShape3D(x.shape, fieldSize, x.shape[2], stride, zeroPad))) || this; + _this.x = x; + _this.fieldSize = fieldSize; + _this.stride = stride; + _this.zeroPad = zeroPad; + return _this; + } + MaxPoolNode.prototype.validate = function () { + util.assert(this.x.shape.length === 3, 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + }; + return MaxPoolNode; +}(Node)); +MaxPoolNode.X = 'x'; +exports.MaxPoolNode = MaxPoolNode; +var ReLUNode = (function (_super) { + __extends(ReLUNode, _super); + function ReLUNode(graph, x) { + return _super.call(this, graph, 'ReLU', { x: x }, new Tensor(x.shape)) || this; + } + ReLUNode.prototype.validate = function () { }; + return ReLUNode; +}(Node)); +ReLUNode.X = 'x'; +exports.ReLUNode = ReLUNode; +var ExpNode = (function (_super) { + __extends(ExpNode, _super); + function ExpNode(graph, x) { + return _super.call(this, graph, 'Exp', { x: x }, new Tensor(x.shape)) || this; + } + ExpNode.prototype.validate = function () { }; + return ExpNode; +}(Node)); +ExpNode.X = 'x'; +exports.ExpNode = ExpNode; +var LogNode = (function (_super) { + __extends(LogNode, _super); + function LogNode(graph, x) { + return _super.call(this, graph, 'Log', { x: x }, new Tensor(x.shape)) || this; + } + LogNode.prototype.validate = function () { }; + return LogNode; +}(Node)); +LogNode.X = 'x'; +exports.LogNode = LogNode; +var TanHNode = (function (_super) { + __extends(TanHNode, _super); + function TanHNode(graph, x) { + return _super.call(this, graph, 'TanH', { x: x }, new Tensor(x.shape)) || this; + } + TanHNode.prototype.validate = function () { }; + return TanHNode; +}(Node)); +TanHNode.X = 'x'; +exports.TanHNode = TanHNode; +var SigmoidNode = (function (_super) { + __extends(SigmoidNode, _super); + function SigmoidNode(graph, x) { + return _super.call(this, graph, 'Sigmoid', { x: x }, new Tensor(x.shape)) || this; + } + SigmoidNode.prototype.validate = function () { }; + return SigmoidNode; +}(Node)); +SigmoidNode.X = 'x'; +exports.SigmoidNode = SigmoidNode; +var SquareNode = (function (_super) { + __extends(SquareNode, _super); + function SquareNode(graph, x) { + return _super.call(this, graph, 'Square', { x: x }, new Tensor(x.shape)) || this; + } + SquareNode.prototype.validate = function () { }; + return SquareNode; +}(Node)); +SquareNode.X = 'x'; +exports.SquareNode = SquareNode; +var SoftmaxCrossEntropyCostNode = (function (_super) { + __extends(SoftmaxCrossEntropyCostNode, _super); + function SoftmaxCrossEntropyCostNode(graph, x, target) { + var _this = _super.call(this, graph, 'SoftmaxCrossEntropyCost', { x: x, target: target }, new Tensor([])) || this; + _this.x = x; + _this.target = target; + return _this; + } + SoftmaxCrossEntropyCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x.shape, this.target.shape), 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + }; + return SoftmaxCrossEntropyCostNode; +}(Node)); +SoftmaxCrossEntropyCostNode.X = 'x'; +SoftmaxCrossEntropyCostNode.TARGET = 'target'; +exports.SoftmaxCrossEntropyCostNode = SoftmaxCrossEntropyCostNode; +var SoftmaxNode = (function (_super) { + __extends(SoftmaxNode, _super); + function SoftmaxNode(graph, x) { + var _this = _super.call(this, graph, 'Softmax', { x: x }, new Tensor(x.shape)) || this; + _this.x = x; + return _this; + } + SoftmaxNode.prototype.validate = function () { + util.assert(this.x.shape.length === 1, 'The input to a softmax must be a 1-D tensor'); + util.assert(this.x.shape[0] >= 2, 'The input to a softmax must have at least 2 values'); + }; + return SoftmaxNode; +}(Node)); +SoftmaxNode.X = 'x'; +exports.SoftmaxNode = SoftmaxNode; +var MeanSquaredCostNode = (function (_super) { + __extends(MeanSquaredCostNode, _super); + function MeanSquaredCostNode(graph, label, prediction) { + var _this = _super.call(this, graph, 'Mean Squared Cost', { label: label, prediction: prediction }, new Tensor([])) || this; + _this.label = label; + _this.prediction = prediction; + return _this; + } + MeanSquaredCostNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.label.shape, this.prediction.shape), 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + }; + return MeanSquaredCostNode; +}(Node)); +MeanSquaredCostNode.LABEL = 'label'; +MeanSquaredCostNode.PREDICTION = 'prediction'; +exports.MeanSquaredCostNode = MeanSquaredCostNode; +var ArgMaxNode = (function (_super) { + __extends(ArgMaxNode, _super); + function ArgMaxNode(graph, x) { + var _this = _super.call(this, graph, 'ArgMax', { x: x }, new Tensor([1])) || this; + _this.x = x; + return _this; + } + ArgMaxNode.prototype.validate = function () { + util.assert(util.sizeFromShape(this.x.shape) > 0, 'Error adding argmax op: input tensor must have at least one entry.'); + }; + return ArgMaxNode; +}(Node)); +ArgMaxNode.X = 'x'; +exports.ArgMaxNode = ArgMaxNode; +var ArgMaxEqualsNode = (function (_super) { + __extends(ArgMaxEqualsNode, _super); + function ArgMaxEqualsNode(graph, x1, x2) { + var _this = _super.call(this, graph, 'ArgMaxEquals', { x1: x1, x2: x2 }, new Tensor([1])) || this; + _this.x1 = x1; + _this.x2 = x2; + return _this; + } + ArgMaxEqualsNode.prototype.validate = function () { + util.assert(util.arraysEqual(this.x1.shape, this.x2.shape), 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + }; + return ArgMaxEqualsNode; +}(Node)); +ArgMaxEqualsNode.X1 = 'x1'; +ArgMaxEqualsNode.X2 = 'x2'; +exports.ArgMaxEqualsNode = ArgMaxEqualsNode; +var SplitNode = (function (_super) { + __extends(SplitNode, _super); + function SplitNode(graph, x) { + var _this = _super.call(this, graph, 'SplitNode', { x: x }, new Tensor(x.shape)) || this; + _this.outputs = []; + return _this; + } + SplitNode.prototype.getNewOutputTensor = function () { + var output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + }; + SplitNode.prototype.validate = function () { }; + return SplitNode; +}(Node)); +SplitNode.X = 'x'; +exports.SplitNode = SplitNode; + +},{"./graph_layers":9,"./math/concat3d_util":16,"./math/conv_util":17,"./math/ndarray":23,"./util":87}],9:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var initializers_1 = require("./initializers"); +var GraphLayers = (function () { + function GraphLayers(g) { + this.g = g; + } + GraphLayers.prototype.dense = function (name, x, units, activation, useBias, kernelInitializer, biasInitializer) { + if (activation === void 0) { activation = null; } + if (useBias === void 0) { useBias = true; } + if (kernelInitializer === void 0) { kernelInitializer = new initializers_1.VarianceScalingInitializer(); } + if (biasInitializer === void 0) { biasInitializer = new initializers_1.ZerosInitializer(); } + var weights = this.g.variable(name + '-weights', kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + var out = this.g.matmul(x, weights); + if (useBias) { + var bias = this.g.variable(name + '-bias', biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + if (activation != null) { + out = activation(out); + } + return out; + }; + return GraphLayers; +}()); +exports.GraphLayers = GraphLayers; + +},{"./initializers":13}],10:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var session_1 = require("./session"); +var DEFAULT_EVAL_INTERVAL_MS = 1500; +var DEFAULT_COST_INTERVAL_MS = 500; +var DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; +var MetricReduction; +(function (MetricReduction) { + MetricReduction[MetricReduction["SUM"] = 0] = "SUM"; + MetricReduction[MetricReduction["MEAN"] = 1] = "MEAN"; +})(MetricReduction = exports.MetricReduction || (exports.MetricReduction = {})); +var GraphRunner = (function () { + function GraphRunner(math, session, eventObserver) { + this.math = math; + this.session = session; + this.eventObserver = eventObserver; + this.lastCostTimestamp = 0; + this.lastEvalTimestamp = 0; + this.totalIdleTimeMs = 0; + this.resetStatistics(); + this.zeroScalar = ndarray_1.Scalar.new(0); + } + GraphRunner.prototype.resetStatistics = function () { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + }; + GraphRunner.prototype.train = function (costTensor, trainFeedEntries, batchSize, optimizer, numBatches, metricTensor, metricFeedEntries, metricBatchSize, metricReduction, evalIntervalMs, costIntervalMs) { + if (metricReduction === void 0) { metricReduction = MetricReduction.MEAN; } + if (evalIntervalMs === void 0) { evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS; } + if (costIntervalMs === void 0) { costIntervalMs = DEFAULT_COST_INTERVAL_MS; } + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = ndarray_1.Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + }; + GraphRunner.prototype.stopTraining = function () { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + }; + GraphRunner.prototype.resumeTraining = function () { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + }; + GraphRunner.prototype.trainNetwork = function () { + var _this = this; + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + var start = performance.now(); + var shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + var costReduction = shouldComputeCost ? session_1.CostReduction.MEAN : session_1.CostReduction.NONE; + this.math.scope(function (keep) { + var avgCost = _this.session.train(_this.costTensor, _this.trainFeedEntries, _this.batchSize, _this.optimizer, costReduction); + if (shouldComputeCost) { + var trainTime = performance.now() - start; + _this.eventObserver.avgCostCallback(avgCost); + if (_this.eventObserver.trainExamplesPerSecCallback != null) { + var examplesPerSec = (_this.batchSize * 1000 / trainTime); + _this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + if (_this.eventObserver.metricCallback != null && + _this.metricFeedEntries != null && + start - _this.lastEvalTimestamp > _this.metricIntervalMs) { + _this.lastEvalTimestamp = start; + if (_this.lastComputedMetric != null) { + _this.lastComputedMetric.dispose(); + } + _this.lastComputedMetric = _this.computeMetric(); + _this.eventObserver.metricCallback(_this.lastComputedMetric); + } + if (_this.eventObserver.totalTimeCallback != null) { + _this.eventObserver.totalTimeCallback((start - _this.trainStartTimestamp) / 1000); + } + _this.batchesTrainedThisRun++; + _this.totalBatchesTrained++; + if (_this.eventObserver.batchesTrainedCallback != null) { + _this.eventObserver.batchesTrainedCallback(_this.totalBatchesTrained); + } + }); + setTimeout(function () { return _this.trainNetwork(); }); + }; + GraphRunner.prototype.infer = function (inferenceTensor, inferenceFeedEntries, inferenceExampleIntervalMs, inferenceExampleCount, numPasses) { + var _this = this; + if (inferenceExampleIntervalMs === void 0) { inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS; } + if (inferenceExampleCount === void 0) { inferenceExampleCount = 5; } + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error('Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + for (var i = 0; i < inferenceFeedEntries.length; i++) { + var feedEntry = inferenceFeedEntries[i]; + if (feedEntry.data instanceof ndarray_1.NDArray) { + throw new Error('Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(function () { return _this.inferNetwork(); }); + } + this.isInferring = true; + }; + GraphRunner.prototype.inferNetwork = function () { + var _this = this; + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + this.math.scope(function (keep, track) { + var feeds = []; + var inferenceValues = []; + var start = performance.now(); + for (var i = 0; i < _this.inferenceExampleCount; i++) { + var ndarrayFeedEntries = []; + for (var j = 0; j < _this.inferenceFeedEntries.length; j++) { + var feedEntry = _this.inferenceFeedEntries[j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: track(feedEntry.data.getNextCopy(_this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + inferenceValues.push(_this.session.eval(_this.inferenceTensor, ndarrayFeedEntries)); + } + if (_this.eventObserver.inferenceExamplesPerSecCallback != null) { + inferenceValues[inferenceValues.length - 1].getValues(); + var inferenceExamplesPerSecTime = performance.now() - start; + var examplesPerSec = (_this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + _this.eventObserver.inferenceExamplesPerSecCallback(examplesPerSec); + } + if (_this.eventObserver.inferenceExamplesCallback != null) { + _this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + _this.inferencePassesThisRun++; + }); + setTimeout(function () { return _this.inferNetwork(); }, this.inferenceExampleIntervalMs); + }; + GraphRunner.prototype.stopInferring = function () { + this.isInferring = false; + }; + GraphRunner.prototype.isInferenceRunning = function () { + return this.isInferring; + }; + GraphRunner.prototype.computeMetric = function () { + var _this = this; + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + var metric = this.zeroScalar; + return this.math.scope(function (keep) { + for (var i = 0; i < _this.metricBatchSize; i++) { + var metricValue = _this.session.eval(_this.metricTensor, _this.metricFeedEntries); + metric = _this.math.add(metric, metricValue); + } + if (_this.metricReduction === MetricReduction.MEAN) { + metric = _this.math.divide(metric, _this.metricBatchSizeScalar); + } + return metric; + }); + }; + GraphRunner.prototype.getTotalBatchesTrained = function () { + return this.totalBatchesTrained; + }; + GraphRunner.prototype.getLastComputedMetric = function () { + return this.lastComputedMetric; + }; + GraphRunner.prototype.setMath = function (math) { + this.math = math; + }; + GraphRunner.prototype.setSession = function (session) { + this.session = session; + }; + GraphRunner.prototype.setInferenceTensor = function (inferenceTensor) { + this.inferenceTensor = inferenceTensor; + }; + GraphRunner.prototype.setInferenceExampleCount = function (inferenceExampleCount) { + this.inferenceExampleCount = inferenceExampleCount; + }; + return GraphRunner; +}()); +exports.GraphRunner = GraphRunner; + +},{"./math/ndarray":23,"./session":83}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var priority_queue = require("./priority_queue"); +var priority_queue_1 = require("./priority_queue"); +function getUnorderedEvaluationSet(nodes, terminatingNodes) { + var terminatingNodeMap = {}; + var seen = {}; + var set = []; + var visit = nodes.slice(); + terminatingNodes.forEach(function (node) { return terminatingNodeMap[node.id] = node; }); + var _loop_1 = function () { + var cur = visit.pop(); + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(function (inputName) { return cur.inputs[inputName]; }) + .forEach(function (input) { return visit.push(input.node); }); + } + set.push(cur); + seen[cur.id] = cur; + } + }; + while (visit.length !== 0) { + _loop_1(); + } + return set; +} +exports.getUnorderedEvaluationSet = getUnorderedEvaluationSet; +function getOrderedEvaluationSet(unorderedEvaluationSet) { + var set = []; + var nodeIndices = {}; + var pendingDependencies = {}; + var nodeQueue = new priority_queue_1.PriorityQueue(function (a, b) { return priority_queue.defaultCompare(pendingDependencies[a.id], pendingDependencies[b.id]); }, function (node, newIndex) { return nodeIndices[node.id] = newIndex; }); + unorderedEvaluationSet.forEach(function (node) { return pendingDependencies[node.id] = 0; }); + unorderedEvaluationSet.forEach(function (node) { return Object.keys(node.inputs) + .map(function (key) { return node.inputs[key]; }) + .forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + }); }); + unorderedEvaluationSet.forEach(function (node) { return nodeQueue.enqueue(node); }); + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + Object.keys(set[0].inputs).map(function (key) { return set[0].inputs[key]; }).forEach(function (input) { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + return set; +} +exports.getOrderedEvaluationSet = getOrderedEvaluationSet; +function isInputNode(node) { + return Object.keys(node.inputs).length === 0; +} +exports.isInputNode = isInputNode; +function shouldBackProp(t) { + return !(t.node instanceof graph_1.ConstantNode); +} +exports.shouldBackProp = shouldBackProp; +function isPassthroughNode(node, map) { + var keys = Object.keys(node.inputs); + for (var i = 0; i < keys.length; i++) { + var input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} +exports.isPassthroughNode = isPassthroughNode; + +},{"./graph":8,"./priority_queue":82}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("./math/conv_util"); +exports.conv_util = conv_util; +var gpgpu_util = require("./math/webgl/gpgpu_util"); +exports.gpgpu_util = gpgpu_util; +var render_ndarray_gpu_util = require("./math/webgl/render_ndarray_gpu_util"); +exports.render_ndarray_gpu_util = render_ndarray_gpu_util; +var webgl_util = require("./math/webgl/webgl_util"); +exports.webgl_util = webgl_util; +var util = require("./util"); +exports.util = util; +var checkpoint_loader_1 = require("./checkpoint_loader"); +exports.CheckpointLoader = checkpoint_loader_1.CheckpointLoader; +var dataset_1 = require("./dataset"); +exports.InMemoryDataset = dataset_1.InMemoryDataset; +var graph_1 = require("./graph"); +exports.Graph = graph_1.Graph; +exports.Tensor = graph_1.Tensor; +var graph_runner_1 = require("./graph_runner"); +exports.GraphRunner = graph_runner_1.GraphRunner; +exports.MetricReduction = graph_runner_1.MetricReduction; +var initializers_1 = require("./initializers"); +exports.ConstantInitializer = initializers_1.ConstantInitializer; +exports.NDArrayInitializer = initializers_1.NDArrayInitializer; +exports.OnesInitializer = initializers_1.OnesInitializer; +exports.RandomNormalInitializer = initializers_1.RandomNormalInitializer; +exports.RandomTruncatedNormalInitializer = initializers_1.RandomTruncatedNormalInitializer; +exports.RandomUniformInitializer = initializers_1.RandomUniformInitializer; +exports.VarianceScalingInitializer = initializers_1.VarianceScalingInitializer; +exports.ZerosInitializer = initializers_1.ZerosInitializer; +var input_provider_1 = require("./input_provider"); +exports.InCPUMemoryShuffledInputProviderBuilder = input_provider_1.InCPUMemoryShuffledInputProviderBuilder; +exports.InGPUMemoryShuffledInputProviderBuilder = input_provider_1.InGPUMemoryShuffledInputProviderBuilder; +var math_1 = require("./math/math"); +exports.MatrixOrientation = math_1.MatrixOrientation; +exports.NDArrayMath = math_1.NDArrayMath; +var math_cpu_1 = require("./math/math_cpu"); +exports.NDArrayMathCPU = math_cpu_1.NDArrayMathCPU; +var math_gpu_1 = require("./math/math_gpu"); +exports.NDArrayMathGPU = math_gpu_1.NDArrayMathGPU; +var ndarray_1 = require("./math/ndarray"); +exports.Array1D = ndarray_1.Array1D; +exports.Array2D = ndarray_1.Array2D; +exports.Array3D = ndarray_1.Array3D; +exports.Array4D = ndarray_1.Array4D; +exports.NDArray = ndarray_1.NDArray; +exports.Scalar = ndarray_1.Scalar; +var gpgpu_context_1 = require("./math/webgl/gpgpu_context"); +exports.GPGPUContext = gpgpu_context_1.GPGPUContext; +var optimizer_1 = require("./optimizer"); +exports.Optimizer = optimizer_1.Optimizer; +var session_1 = require("./session"); +exports.CostReduction = session_1.CostReduction; +exports.Session = session_1.Session; +var sgd_optimizer_1 = require("./sgd_optimizer"); +exports.SGDOptimizer = sgd_optimizer_1.SGDOptimizer; + +},{"./checkpoint_loader":6,"./dataset":7,"./graph":8,"./graph_runner":10,"./initializers":13,"./input_provider":14,"./math/conv_util":17,"./math/math":20,"./math/math_cpu":21,"./math/math_gpu":22,"./math/ndarray":23,"./math/webgl/gpgpu_context":36,"./math/webgl/gpgpu_util":37,"./math/webgl/render_ndarray_gpu_util":49,"./math/webgl/webgl_util":59,"./optimizer":81,"./session":83,"./sgd_optimizer":85,"./util":87}],13:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var VarianceScalingInitializer = (function () { + function VarianceScalingInitializer(scale, mode, distribution) { + if (scale === void 0) { scale = 1.0; } + if (mode === void 0) { mode = 'fan_in'; } + if (distribution === void 0) { distribution = 'normal'; } + this.scale = scale; + this.mode = mode; + this.distribution = distribution; + } + VarianceScalingInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } + else if (this.mode === 'fan_out') { + n = outputUnits; + } + else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } + else { + throw new Error('Unexpected mode for variance scaling initializer: ' + this.mode); + } + if (this.distribution === 'normal') { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, 0.0, Math.sqrt(this.scale / n)); + } + else if (this.distribution === 'uniform') { + return ndarray_1.NDArray.randUniform(weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } + else { + throw new Error('Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + }; + return VarianceScalingInitializer; +}()); +exports.VarianceScalingInitializer = VarianceScalingInitializer; +var ZerosInitializer = (function () { + function ZerosInitializer() { + } + ZerosInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.zeros(weightsShape); + }; + return ZerosInitializer; +}()); +exports.ZerosInitializer = ZerosInitializer; +var OnesInitializer = (function () { + function OnesInitializer() { + } + OnesInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(1); + return values; + }; + return OnesInitializer; +}()); +exports.OnesInitializer = OnesInitializer; +var ConstantInitializer = (function () { + function ConstantInitializer(value) { + if (value === void 0) { value = 0; } + this.value = value; + } + ConstantInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + var values = ndarray_1.NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + }; + return ConstantInitializer; +}()); +exports.ConstantInitializer = ConstantInitializer; +var NDArrayInitializer = (function () { + function NDArrayInitializer(ndarray) { + this.ndarray = ndarray; + } + NDArrayInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return this.ndarray; + }; + return NDArrayInitializer; +}()); +exports.NDArrayInitializer = NDArrayInitializer; +var RandomNormalInitializer = (function () { + function RandomNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randNormal(weightsShape, this.mean, this.stdev); + }; + return RandomNormalInitializer; +}()); +exports.RandomNormalInitializer = RandomNormalInitializer; +var RandomTruncatedNormalInitializer = (function () { + function RandomTruncatedNormalInitializer(mean, stdev) { + if (mean === void 0) { mean = 0; } + if (stdev === void 0) { stdev = .05; } + this.mean = mean; + this.stdev = stdev; + } + RandomTruncatedNormalInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + }; + return RandomTruncatedNormalInitializer; +}()); +exports.RandomTruncatedNormalInitializer = RandomTruncatedNormalInitializer; +var RandomUniformInitializer = (function () { + function RandomUniformInitializer(minval, maxval) { + if (minval === void 0) { minval = -.05; } + if (maxval === void 0) { maxval = .05; } + this.minval = minval; + this.maxval = maxval; + } + RandomUniformInitializer.prototype.initialize = function (weightsShape, inputUnits, outputUnits) { + return ndarray_1.NDArray.randUniform(weightsShape, this.minval, this.maxval); + }; + return RandomUniformInitializer; +}()); +exports.RandomUniformInitializer = RandomUniformInitializer; + +},{"./math/ndarray":23}],14:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +var InMemoryShuffledInputProviderBuilder = (function () { + function InMemoryShuffledInputProviderBuilder(inputs) { + this.inputs = inputs; + this.idx = 0; + this.inputCounter = 0; + this.epoch = 0; + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + var numExamples = this.inputs[0].length; + for (var i = 0; i < this.numInputs; i++) { + util.assert(this.inputs[i].length === numExamples, 'Number of examples must match across different inputs.'); + } + for (var i = 0; i < this.numInputs; i++) { + var inputShape = this.inputs[i][0].shape; + for (var j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + InMemoryShuffledInputProviderBuilder.prototype.getCurrentExampleIndex = function () { + var returnIdx = this.idx; + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + }; + InMemoryShuffledInputProviderBuilder.prototype.getNextInput = function (inputId) { + var currentExampleIndex = this.getCurrentExampleIndex(); + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + }; + InMemoryShuffledInputProviderBuilder.prototype.getEpoch = function () { + return this.epoch; + }; + InMemoryShuffledInputProviderBuilder.prototype.getInputProviders = function () { + var inputProviders = []; + for (var i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + }; + return InMemoryShuffledInputProviderBuilder; +}()); +exports.InMemoryShuffledInputProviderBuilder = InMemoryShuffledInputProviderBuilder; +var InCPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InCPUMemoryShuffledInputProviderBuilder, _super); + function InCPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InCPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return ndarray_1.NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InCPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InCPUMemoryShuffledInputProviderBuilder = InCPUMemoryShuffledInputProviderBuilder; +var InGPUMemoryShuffledInputProviderBuilder = (function (_super) { + __extends(InGPUMemoryShuffledInputProviderBuilder, _super); + function InGPUMemoryShuffledInputProviderBuilder() { + return _super !== null && _super.apply(this, arguments) || this; + } + InGPUMemoryShuffledInputProviderBuilder.prototype.getInputProvider = function (inputId) { + var shuffledInputProvider = this; + return { + getNextCopy: function (math) { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy: function (math, copy) { + copy.dispose(); + } + }; + }; + return InGPUMemoryShuffledInputProviderBuilder; +}(InMemoryShuffledInputProviderBuilder)); +exports.InGPUMemoryShuffledInputProviderBuilder = InGPUMemoryShuffledInputProviderBuilder; + +},{"./math/ndarray":23,"./util":87}],15:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var TanHFunc = (function () { + function TanHFunc() { + } + TanHFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.tanh(x); + }); + }; + TanHFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.scalarMinusArray(ndarray_1.Scalar.ONE, ySquared); + }); + }; + return TanHFunc; +}()); +exports.TanHFunc = TanHFunc; +var ReLUFunc = (function () { + function ReLUFunc() { + } + ReLUFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.relu(x); + }); + }; + ReLUFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.step(x); + }); + }; + return ReLUFunc; +}()); +exports.ReLUFunc = ReLUFunc; +var SigmoidFunc = (function () { + function SigmoidFunc() { + } + SigmoidFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.sigmoid(x); + }); + }; + SigmoidFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + var ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + }; + return SigmoidFunc; +}()); +exports.SigmoidFunc = SigmoidFunc; +var SquareFunc = (function () { + function SquareFunc() { + } + SquareFunc.prototype.output = function (math, x) { + return math.scope(function () { + return math.elementWiseMul(x, x); + }); + }; + SquareFunc.prototype.der = function (math, x, y) { + return math.scope(function () { + return math.scalarTimesArray(ndarray_1.Scalar.TWO, x); + }); + }; + return SquareFunc; +}()); +exports.SquareFunc = SquareFunc; + +},{"./ndarray":23}],16:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function assertConcat3DShapesMatch(x1Shape, x2Shape, axis, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + util.assert(x1Shape.length === 3, errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + util.assert(axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + for (var i = 0; i < 3; i++) { + util.assert((i === axis) || (x1Shape[i] === x2Shape[i]), errorMessagePrefix + + ("Shape (" + x1Shape + ") does not match (" + x2Shape + ") along ") + + "non-concatenated axis."); + } +} +exports.assertConcat3DShapesMatch = assertConcat3DShapesMatch; +function computeConcat3DOutputShape(x1Shape, x2Shape, axis) { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + var outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape; +} +exports.computeConcat3DOutputShape = computeConcat3DOutputShape; + +},{"../util":87}],17:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +function computeOutputShape3D(inputShapeRowColDepth, fieldSize, depth, stride, zeroPad) { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + var inputRows = inputShapeRowColDepth[0]; + var inputCols = inputShapeRowColDepth[1]; + var outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputRows), "The output # of rows (" + outputRows + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + var outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert(util.isInt(outputCols), "The output # of columns (" + outputCols + ") must be an integer. Change " + + "the stride and/or zero pad parameters"); + return [outputRows, outputCols, depth]; +} +exports.computeOutputShape3D = computeOutputShape3D; +function computeDefaultPad(inputShape, fieldSize, stride) { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} +exports.computeDefaultPad = computeDefaultPad; +function computeTexShapeFrom3D(shapeRowColDepth) { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} +exports.computeTexShapeFrom3D = computeTexShapeFrom3D; +function computeWeightsShape4D(inputDepth, outputDepth, fSize) { + return [fSize, fSize, inputDepth, outputDepth]; +} +exports.computeWeightsShape4D = computeWeightsShape4D; +function computeWeightsTexShape(inputDepth, outputDepth, fieldSize) { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} +exports.computeWeightsTexShape = computeWeightsTexShape; +function computeBiasesTexShape(outputDepth) { + return [1, outputDepth]; +} +exports.computeBiasesTexShape = computeBiasesTexShape; +function computeDilatedRC(rc, origStride) { + var rowsDilated = (rc[0] - 1) * origStride + 1; + var colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} +exports.computeDilatedRC = computeDilatedRC; + +},{"../util":87}],18:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function validateShapes(sourceSize, destSize) { + var srcArea = sourceSize[0] * sourceSize[1]; + var dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + var srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + var dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error('copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} +exports.validateShapes = validateShapes; + +},{}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./ndarray"); +var SquareCostFunc = (function () { + function SquareCostFunc() { + this.halfOne = ndarray_1.Scalar.new(0.5); + } + SquareCostFunc.prototype.cost = function (math, x1, x2) { + var diff = math.sub(x1, x2); + var diffSquared = math.elementWiseMul(diff, diff); + var result = math.scalarTimesArray(this.halfOne, diffSquared); + diff.dispose(); + diffSquared.dispose(); + return result; + }; + SquareCostFunc.prototype.der = function (math, x1, x2) { + return math.sub(x1, x2); + }; + SquareCostFunc.prototype.dispose = function () { + this.halfOne.dispose(); + }; + return SquareCostFunc; +}()); +exports.SquareCostFunc = SquareCostFunc; + +},{"./ndarray":23}],20:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2d_util = require("./copy2d_util"); +var ndarray_1 = require("./ndarray"); +var NDArrayMath = (function () { + function NDArrayMath(safeMode) { + this.safeMode = safeMode; + this.ndarrayScopes = []; + this.ndarraysToKeep = []; + this.activeScopeNDArraysToKeep = []; + } + NDArrayMath.prototype.scope = function (scopeFn) { + var _this = this; + this.startScope(); + var keepFn = function (ndarray) { return _this.keep(ndarray); }; + var trackFn = function (ndarray) { return _this.track(ndarray); }; + var result = scopeFn(keepFn, trackFn); + this.endScope(result); + return result; + }; + NDArrayMath.prototype.startScope = function () { + var newScope = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + var newNDArraysToKeep = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + }; + NDArrayMath.prototype.endScope = function (result) { + var _this = this; + for (var i = 0; i < this.activeScope.length; i++) { + var ndarray = this.activeScope[i]; + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof ndarray_1.NDArray && + ndarray.getData() === result.getData())) { + continue; + } + ndarray.dispose(); + } + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + if (result instanceof ndarray_1.NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } + else if (Array.isArray(result)) { + result.forEach(function (r) { + if (r instanceof ndarray_1.NDArray && + !_this.isNDArrayDataInList(r, _this.activeScopeNDArraysToKeep)) { + _this.track(r); + } + }); + } + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + }; + NDArrayMath.prototype.isNDArrayDataInList = function (ndarray, ndarrayList) { + for (var i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + }; + NDArrayMath.prototype.keep = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + }; + NDArrayMath.prototype.track = function (result) { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error('You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + }; + NDArrayMath.prototype.matMul = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = MatrixOrientation.REGULAR; } + var innerShapeA = (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var innerShapeB = (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + util.assert(a.rank === 2 && b.rank === 2, "Error in matMul: inputs must be rank 2, got ranks " + a.rank + + ("and " + b.rank + ".")); + util.assert(innerShapeA === innerShapeB, "Error in matMul: inner shapes (" + innerShapeA + ") and (" + + (innerShapeB + ") of NDArrays with shapes " + a.shape + " and ") + + (b.shape + " and orientations " + MatrixOrientation[aOrientation]) + + (" and " + MatrixOrientation[bOrientation] + " must match.")); + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + }; + NDArrayMath.prototype.vectorTimesMatrix = function (v, matrix) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: first input must be rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: second input must be rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[0], "Error in vectorTimesMatrix: size of first rank 1 input (" + v.size + ") " + + "must match inner dimension of second rank 2 input, but got " + + ("rank " + matrix.rank + ".")); + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + }; + NDArrayMath.prototype.matrixTimesVector = function (matrix, v) { + util.assert(v.rank === 1, "Error in vectorTimesMatrix: second input must rank 1, but got " + + ("rank " + v.rank + ".")); + util.assert(matrix.rank === 2, "Error in vectorTimesMatrix: first input must be a rank 2, but got " + + ("rank " + matrix.rank + ".")); + util.assert(v.size === matrix.shape[1], "Error in vectorTimesMatrix: size of first rank 1 input " + v.size + " " + + "must match inner dimension of second rank 2 input, but got " + + ("shape " + matrix.shape + ".")); + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + }; + NDArrayMath.prototype.dotProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in dotProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + util.assert(v1.size === v2.size, "Error in dotProduct: size of inputs (" + v1.size + ") and (" + + (v2.size + ") must match.")); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + }; + NDArrayMath.prototype.outerProduct = function (v1, v2) { + util.assert(v1.rank === 1 && v2.rank === 1, "Error in outerProduct: inputs must be rank 1, but got ranks " + + (v1.rank + " and " + v2.rank + ".")); + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + }; + NDArrayMath.prototype.clone = function (ndarray) { + return this.track(this.cloneInternal(ndarray)); + }; + NDArrayMath.prototype.reshape = function (ndarray, newShape) { + util.assert(ndarray.size === util.sizeFromShape(newShape), "Error in reshape: old size " + ndarray.size + " must match new size " + + (util.sizeFromShape(newShape) + ".")); + return this.track(this.reshapeInternal(ndarray, newShape)); + }; + NDArrayMath.prototype.slice2D = function (input, begin, size) { + util.assert(begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], "Error in slice2D: requested start position " + begin + " and size " + + (size + " would overflow input of shape " + input.shape + ".")); + return this.track(this.slice2DInternal(input, begin, size)); + }; + NDArrayMath.prototype.copy2D = function (source, sourceBegin, sourceSize, dest, destBegin, destSize) { + util.assert(sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], "Error in copy2D: requested source start position " + sourceBegin + " " + + ("and source size " + sourceSize + " would overflow source NDArray") + + ("of shape " + source.shape + ".")); + util.assert(destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], "Error in copy2D: requested dest start position " + destBegin + " " + + ("and source size " + destSize + " would overflow dest NDArray of") + + ("shape " + dest.shape + ".")); + copy2d_util.validateShapes(sourceSize, destSize); + return this.copy2DInternal(source, sourceBegin, sourceSize, dest, destBegin, destSize); + }; + NDArrayMath.prototype.concat3D = function (ndarray1, ndarray2, axis) { + concat3d_util.assertConcat3DShapesMatch(ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + }; + NDArrayMath.prototype.logSumExp = function (ndarray) { + return this.track(this.logSumExpInternal(ndarray)); + }; + NDArrayMath.prototype.sum = function (ndarray) { + return this.track(this.sumInternal(ndarray)); + }; + NDArrayMath.prototype.argMin = function (ndarray) { + return this.track(this.argMinInternal(ndarray)); + }; + NDArrayMath.prototype.argMax = function (ndarray) { + return this.track(this.argMaxInternal(ndarray)); + }; + NDArrayMath.prototype.argMaxEquals = function (x1, x2) { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + }; + NDArrayMath.prototype.topK = function (ndarray, k) { + util.assert(k <= ndarray.size, "Error in topK: k value (" + k + ") must be less than size of input " + + ("ndarray, got shape " + ndarray.shape + ".")); + var result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + }; + NDArrayMath.prototype.min = function (ndarray) { + return this.track(this.minInternal(ndarray)); + }; + NDArrayMath.prototype.max = function (ndarray) { + return this.track(this.maxInternal(ndarray)); + }; + NDArrayMath.prototype.softmax = function (x) { + var _this = this; + return this.scope(function () { + var lse = _this.logSumExp(x); + var logResult = _this.arrayMinusScalar(x, lse); + return _this.exp(logResult); + }); + }; + NDArrayMath.prototype.switchDim = function (a, newDim) { + util.assert(a.rank === newDim.length, "Error in switchDim: length of input shape " + a.shape + " " + + ("must match size of newDim array " + newDim + ".")); + return this.track(this.switchDimInternal(a, newDim)); + }; + NDArrayMath.prototype.scalarPlusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarPlusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarPlusArrayInternal(c, a)); + }; + NDArrayMath.prototype.scalarMinusArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarMinusArray: first argument must be rank 0, but got " + + ("rank " + c.rank + ".")); + return this.track(this.scalarMinusArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayMinusScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayMinusScalar: second argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.arrayMinusScalarInternal(a, c)); + }; + NDArrayMath.prototype.neg = function (a) { + return this.track(this.negInternal(a)); + }; + NDArrayMath.prototype.add = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + }; + NDArrayMath.prototype.sub = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + }; + NDArrayMath.prototype.elementWiseMul = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + }; + NDArrayMath.prototype.divide = function (a, b) { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + }; + NDArrayMath.prototype.scalarDividedByArray = function (c, a) { + util.assert(c.size === 1, "Error in scalarDividedByArray: first argument must be rank 0, but " + + ("got NDArray of rank " + c.rank + ".")); + return this.track(this.scalarDividedByArrayInternal(c, a)); + }; + NDArrayMath.prototype.arrayDividedByScalar = function (a, c) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: second argument must be rank 0, " + + ("but got NDArray of rank " + c.rank + ".")); + return this.track(this.arrayDividedByScalarInternal(a, c)); + }; + NDArrayMath.prototype.exp = function (ndarray) { + return this.track(this.expInternal(ndarray)); + }; + NDArrayMath.prototype.log = function (ndarray) { + return this.track(this.logInternal(ndarray)); + }; + NDArrayMath.prototype.relu = function (ndarray) { + return this.track(this.reluInternal(ndarray)); + }; + NDArrayMath.prototype.sigmoid = function (ndarray) { + return this.track(this.sigmoidInternal(ndarray)); + }; + NDArrayMath.prototype.tanh = function (ndarray) { + return this.track(this.tanhInternal(ndarray)); + }; + NDArrayMath.prototype.sin = function (ndarray) { + return this.track(this.sinInternal(ndarray)); + }; + NDArrayMath.prototype.step = function (ndarray) { + return this.track(this.stepInternal(ndarray)); + }; + NDArrayMath.prototype.scaledArrayAdd = function (c1, a, c2, b) { + util.assert(c1.size === 1, "Error in scaledArrayAdd: first argument must rank 0, but got " + + (" rank " + c1.rank + ".")); + util.assert(c2.size === 1, "Error in scaledArrayAdd: third argument must be rank 0, but got " + + ("NDArray of rank " + c2.rank + ".")); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + }; + NDArrayMath.prototype.scalarTimesArray = function (c, a) { + util.assert(c.size === 1, "Error in arrayDividedByScalar: first argument must be rank 0, but " + + ("got rank " + c.rank + ".")); + return this.track(this.scalarTimesArrayInternal(c, a)); + }; + NDArrayMath.prototype.elementWiseMulBroadcast = function (a, b) { + util.assert(a.rank === 2, "Error in elementWiseMulBroadcast: first argument must be " + + ("rank 2, but got rank " + a.rank + ".")); + util.assert(b.rank === 2, "Error in elementWiseMulBroadcast: second argument must be " + + ("rank 2, but got rank " + b.rank + ".")); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + }; + NDArrayMath.prototype.conv2d = function (x, weights, biases, stride, zeroPad) { + util.assert(x.rank === 3, "Error in conv2d: x must be rank 3, but got rank " + x.rank + "."); + util.assert(weights.rank === 4, "Error in conv2d: weights must be rank 4, but got rank " + + (weights.rank + ".")); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2d: biases must be rank 1, but got rank " + + (biases.rank + ".")); + } + util.assert(x.shape[2] === weights.shape[2], "Error in conv2d: depth of input (" + x.shape[2] + ") must match " + + ("input depth for weights " + weights.shape[2] + ".")); + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + }; + NDArrayMath.prototype.conv2dBackProp = function (x, dy, weights, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dBackProp: x must be rank 3, but got shape " + + (x.shape + ".")); + util.assert(dy.rank === 3, "Error in conv2dBackProp: dy must be rank 3, but got shape " + + (dy.shape + ".")); + util.assert(weights.rank === 4, "Error in conv2dBackProp: weights must be rank 4, but got shape " + + (weights.shape + ".")); + util.assert(x.shape[2] === weights.shape[2], "Error in conv2dBackProp: depth of x " + x.shape[2] + ") must " + + ("match input depth for weights (" + weights.shape[2] + ".")); + util.assert(dy.shape[2] === weights.shape[3], "Error in conv2dBackProp: depth of dy (" + dy.shape[2] + ") must " + + ("match output depth for weights (" + weights.shape[3] + ").")); + var backpropResult = this.conv2dBackPropInternal(x, dy, weights, stride, pad); + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + return backpropResult; + }; + NDArrayMath.prototype.conv2dTranspose = function (x, weights, biases, stride, pad) { + util.assert(x.rank === 3, "Error in conv2dTranspose: x must be rank 3, but got rank " + + (x.rank + ".")); + util.assert(weights.rank === 4, "Error in conv2dTranspose: weights must be rank 4, but got " + + ("rank " + weights.rank)); + if (biases != null) { + util.assert(biases.rank === 1, "Error in conv2dTranspose: biases must be rank 1, but got ' +\n 'rank " + biases.rank + "."); + } + util.assert(x.shape[2] === weights.shape[3], "Error in conv2dTranspose: depth of input (" + x.shape[2] + ") must " + + ("match input depth for weights " + weights.shape[3] + ".")); + return this.track(this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + }; + NDArrayMath.prototype.maxPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.maxPoolBackprop = function (dy, x, fSize, stride, pad) { + util.assert(dy.rank === 3, "Error in maxPoolBackprop: dy must be rank 3 but got rank " + + (dy.rank + ".")); + util.assert(x.rank === 3, "Error in maxPoolBackprop: x must be rank 3 but got rank " + + (x.rank + ".")); + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + }; + NDArrayMath.prototype.minPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in minPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.avgPool = function (x, fSize, stride, pad) { + util.assert(x.rank === 3, "Error in avgPool: x must be rank 3 but got rank " + x.rank + "."); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + }; + NDArrayMath.prototype.resizeBilinear3D = function (x, newShape2D, alignCorners) { + if (alignCorners === void 0) { alignCorners = false; } + util.assert(x.rank === 3, "Error in resizeBilinear3D: x must be rank 3 but got rank " + x.rank + "."); + util.assert(newShape2D.length === 2, "Error in resizeBilinear3D: new shape must 2D, but got shape " + + (newShape2D + ".")); + return this.track(this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + }; + NDArrayMath.prototype.batchNormalization3D = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + util.assert(x.rank === 3, "Error in batchNormalization3D: x must be rank 3 but got rank " + + (x.rank + ".")); + util.assert(mean.rank === 3 || mean.rank === 1, "Error in batchNormalization3D: mean must be rank 3 or rank 1 but " + + ("got rank " + mean.rank + ".")); + util.assert(variance.rank === 3 || variance.rank === 1, "Error in batchNormalization3D: variance must be rank 3 or rank 1 " + + ("but got rank " + variance.rank + ".")); + if (scale != null) { + util.assert(scale.rank === 3 || scale.rank === 1, "Error in batchNormalization3D: scale must be rank 3 or rank 1 " + + ("but got rank " + scale.rank + ".")); + } + if (offset != null) { + util.assert(offset.rank === 3 || offset.rank === 1, "Error in batchNormalization3D: offset must be rank 3 or rank 1 " + + ("but got rank " + offset.rank + ".")); + } + return this.track(this.batchNormalization3DInternal(x, mean, variance, varianceEpsilon, scale, offset)); + }; + return NDArrayMath; +}()); +exports.NDArrayMath = NDArrayMath; +var MatrixOrientation; +(function (MatrixOrientation) { + MatrixOrientation[MatrixOrientation["REGULAR"] = 0] = "REGULAR"; + MatrixOrientation[MatrixOrientation["TRANSPOSED"] = 1] = "TRANSPOSED"; +})(MatrixOrientation = exports.MatrixOrientation || (exports.MatrixOrientation = {})); + +},{"../util":87,"./concat3d_util":16,"./copy2d_util":18,"./ndarray":23}],21:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var copy2D_util = require("./copy2d_util"); +var math_1 = require("./math"); +var ndarray_1 = require("./ndarray"); +var NDArrayMathCPU = (function (_super) { + __extends(NDArrayMathCPU, _super); + function NDArrayMathCPU(safeMode) { + if (safeMode === void 0) { safeMode = false; } + return _super.call(this, safeMode) || this; + } + NDArrayMathCPU.prototype.cloneInternal = function (ndarray) { + return ndarray_1.NDArray.make(ndarray.shape, { values: new Float32Array(ndarray.getValues()) }); + }; + NDArrayMathCPU.prototype.reshapeInternal = function (ndarray, newShape) { + return this.cloneInternal(ndarray).reshape(newShape); + }; + NDArrayMathCPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.Array2D.zeros(sizeRowCol); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathCPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + var srcValues = source.getValues(); + var dstValues = dest.getValues(); + var n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (var i = 0; i < n; ++i) { + var srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + var srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + var srcOff = srcRow * source.shape[1] + srcCol; + var dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + var dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + var dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + }; + NDArrayMathCPU.prototype.concat3DInternal = function (x1, x2, axis) { + var outputShape = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var values = ndarray_1.NDArray.zeros(outputShape); + for (var i = 0; i < outputShape[0]; i++) { + for (var j = 0; j < outputShape[1]; j++) { + for (var k = 0; k < outputShape[2]; k++) { + var index = [i, j, k]; + var value = void 0; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } + else { + index[axis] -= x1.shape[axis]; + var i2 = index[0], j2 = index[1], k2 = index[2]; + value = x2.get(i2, j2, k2); + } + values.set(value, i, j, k); + } + } + } + return values; + }; + NDArrayMathCPU.prototype.scalarPlusArrayInternal = function (c, a) { + var resultValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + var c1Val = c1.get(); + var c2Val = c2.get(); + for (var i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: cValues }); + }; + NDArrayMathCPU.prototype.scalarTimesArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cVal = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarMinusArrayInternal = function (c, a) { + var negA = this.negInternal(a); + var result = this.scalarPlusArrayInternal(c, negA); + negA.dispose(); + return result; + }; + NDArrayMathCPU.prototype.arrayMinusScalarInternal = function (a, c) { + var negC = this.negInternal(c); + var result = this.scalarPlusArrayInternal(negC, a); + negC.dispose(); + return result; + }; + NDArrayMathCPU.prototype.negInternal = function (a) { + return this.scalarTimesArrayInternal(ndarray_1.Scalar.NEG_ONE, a); + }; + NDArrayMathCPU.prototype.addInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.ONE, b); + }; + NDArrayMathCPU.prototype.subInternal = function (a, b) { + return this.scaledArrayAddInternal(ndarray_1.Scalar.ONE, a, ndarray_1.Scalar.NEG_ONE, b); + }; + NDArrayMathCPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var leftDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var rightDim = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var normalGetter = function (matrix, i, j) { + return matrix.get(i, j); + }; + var transposedGetter = function (matrix, i, j) { + return matrix.get(j, i); + }; + var aGetter = (aOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var bGetter = (bOrientation === math_1.MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + var values = new Float32Array(leftDim * rightDim); + var index = 0; + for (var i = 0; i < leftDim; ++i) { + for (var j = 0; j < rightDim; ++j) { + var sum = 0; + for (var k = 0; k < sharedDim; ++k) { + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return ndarray_1.Array2D.new([leftDim, rightDim], values); + }; + NDArrayMathCPU.prototype.elementWiseMulInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + var maxRow = Math.max(a.shape[0], b.shape[0]); + var maxCol = Math.max(a.shape[1], b.shape[1]); + var values = new Float32Array(maxRow * maxCol); + var index = 0; + for (var row = 0; row < maxRow; row++) { + for (var col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return ndarray_1.Array2D.new([maxRow, maxCol], values); + }; + NDArrayMathCPU.prototype.divideInternal = function (a, b) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var bValues = b.getValues(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.scalarDividedByArrayInternal = function (c, a) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.arrayDividedByScalarInternal = function (a, c) { + var newValues = new Float32Array(a.size); + var aValues = a.getValues(); + var cValue = c.get(); + for (var i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return ndarray_1.NDArray.make(a.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.sumInternal = function (ndarray) { + var sum = 0; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + sum += values[i]; + } + return ndarray_1.Scalar.new(sum); + }; + NDArrayMathCPU.prototype.argMinInternal = function (ndarray) { + var min = Number.MAX_VALUE; + var minIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return ndarray_1.Scalar.new(minIndex); + }; + NDArrayMathCPU.prototype.argMaxInternal = function (ndarray) { + var max = Number.NEGATIVE_INFINITY; + var maxIndex = -1; + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return ndarray_1.Scalar.new(maxIndex); + }; + NDArrayMathCPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var argMax1 = this.argMaxInternal(x1).get(); + var argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return ndarray_1.Scalar.new(NaN); + } + return ndarray_1.Scalar.new(+(argMax1 === argMax2)); + }; + NDArrayMathCPU.prototype.topKInternal = function (ndarray, k) { + var values = ndarray.getValues(); + var valuesAndIndices = []; + for (var i = 0; i < values.length; i++) { + valuesAndIndices.push({ value: values[i], index: i }); + } + valuesAndIndices.sort(function (a, b) { + return b.value - a.value; + }); + var topkValues = new Float32Array(k); + var topkIndices = new Float32Array(k); + for (var i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return { values: ndarray_1.Array1D.new(topkValues), indices: ndarray_1.Array1D.new(topkIndices) }; + }; + NDArrayMathCPU.prototype.minInternal = function (ndarray) { + var values = ndarray.getValues(); + var min = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return ndarray_1.Scalar.new(min); + }; + NDArrayMathCPU.prototype.maxInternal = function (ndarray) { + var values = ndarray.getValues(); + var max = values[0]; + for (var i = 1; i < values.length; ++i) { + var value = values[i]; + if (isNaN(value)) { + return ndarray_1.Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return ndarray_1.Scalar.new(max); + }; + NDArrayMathCPU.prototype.expInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logInternal = function (ndarray) { + var values = ndarray.getValues(); + var newValues = new Float32Array(values.length); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + newValues[i] = Math.log(value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: newValues }); + }; + NDArrayMathCPU.prototype.logSumExpInternal = function (ndarray) { + var xMax = this.max(ndarray); + var a = this.arrayMinusScalar(ndarray, xMax); + var b = this.exp(a); + var c = this.sum(b); + var d = this.log(c); + var result = this.add(xMax, d); + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + return result; + }; + NDArrayMathCPU.prototype.reluInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sigmoidInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.tanhInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.sinInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.stepInternal = function (ndarray) { + var resultValues = new Float32Array(ndarray.size); + var values = ndarray.getValues(); + for (var i = 0; i < values.length; ++i) { + var value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return ndarray_1.NDArray.make(ndarray.shape, { values: resultValues }); + }; + NDArrayMathCPU.prototype.conv2dInternal = function (x, weights, biases, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], inputDepth = _a[2]; + var fieldSize = weights.shape[0]; + var outputDepth = weights.shape[3]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < outputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fieldSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fieldSize + xCCorner); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + for (var d1 = 0; d1 < inputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + var bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathCPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var fSize = weights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR - pad; + var xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + var xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC - pad; + var xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + var xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + var dotProd = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR * origStride - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC * origStride - xCCorner; + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + var bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dTransposeShaderLike = function (x, origWeights, origStride, origPad) { + var fSize = origWeights.shape[0]; + var pad = fSize - 1 - origPad; + var origInputDepth = origWeights.shape[2]; + var origOutputDepth = origWeights.shape[3]; + var _a = x.shape, xRows = _a[0], xCols = _a[1], xDepth = _a[2]; + var xRowsDilated = (xRows - 1) * origStride + 1; + var xColsDilated = (xCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d2 = 0; d2 < origInputDepth; ++d2) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xRCorner = yR - pad; + var xCCorner = yC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (var d1 = 0; d1 < origOutputDepth; ++d1) { + var pixel = x.get(xR, xC, d1); + var weight = origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + var dW = ndarray_1.Array4D.zeros(weightsShape); + var yNumRows = dY.shape[0]; + var yNumCols = dY.shape[1]; + var xNumRows = x.shape[0]; + var xNumCols = x.shape[1]; + for (var wR = 0; wR < fSize; ++wR) { + var yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + var yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + for (var wC = 0; wC < fSize; ++wC) { + var yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + var yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + for (var d1 = 0; d1 < inputDepth; ++d1) { + for (var d2 = 0; d2 < outputDepth; ++d2) { + var dotProd = 0; + for (var yR = yRMin; yR < yRMax; ++yR) { + var xR = wR + yR * stride - zeroPad; + for (var yC = yCMin; yC < yCMax; ++yC) { + var xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + }; + NDArrayMathCPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var numRows = dY.shape[0]; + var numCols = dY.shape[1]; + var values = new Float32Array(outputDepth); + for (var d2 = 0; d2 < outputDepth; ++d2) { + var sum = 0; + for (var r = 0; r < numRows; ++r) { + for (var c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return ndarray_1.Array1D.new(values); + }; + NDArrayMathCPU.prototype.switchDimInternal = function (t, newDim) { + var newShape = new Array(t.rank); + for (var i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + var resultValues = new Float32Array(t.size); + var values = t.getValues(); + var result = ndarray_1.NDArray.make(newShape, { values: resultValues }); + for (var i = 0; i < t.size; ++i) { + var loc = t.indexToLoc(i); + var newLoc = new Array(loc.length); + for (var i_1 = 0; i_1 < newLoc.length; i_1++) { + newLoc[i_1] = loc[newDim[i_1]]; + } + var newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + }; + NDArrayMathCPU.prototype.pool = function (x, fSize, stride, pad, poolType) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D([xRows, xCols, depth], fSize, depth, stride, pad); + var y = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < y.shape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < y.shape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var minMaxValue = (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + var avgValue = 0; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } + else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + }; + NDArrayMathCPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'max'); + }; + NDArrayMathCPU.prototype.maxPoolPositions = function (x, fSize, stride, pad) { + var _a = x.shape, xRows = _a[0], xCols = _a[1], depth = _a[2]; + var outputShape = conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + var maxPositions = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var yR = 0; yR < outputShape[0]; ++yR) { + var xRCorner = yR * stride - pad; + var xRMin = Math.max(0, xRCorner); + var xRMax = Math.min(xRows, fSize + xRCorner); + for (var yC = 0; yC < outputShape[1]; ++yC) { + var xCCorner = yC * stride - pad; + var xCMin = Math.max(0, xCCorner); + var xCMax = Math.min(xCols, fSize + xCCorner); + var maxValue = Number.NEGATIVE_INFINITY; + var maxPosition = -1; + for (var xR = xRMin; xR < xRMax; ++xR) { + var wR = xR - xRCorner; + for (var xC = xCMin; xC < xCMax; ++xC) { + var wC = xC - xCCorner; + var pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + }; + NDArrayMathCPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + var pad = fSize - 1 - origPad; + var _a = dy.shape, dyRows = _a[0], dyCols = _a[1], depth = _a[2]; + var dyRowsDilated = (dyRows - 1) * origStride + 1; + var dxColsDilated = (dyCols - 1) * origStride + 1; + var outputShape = conv_util.computeOutputShape3D([dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + var dx = ndarray_1.Array3D.zeros(outputShape); + for (var d = 0; d < depth; ++d) { + for (var dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (var dxC = 0; dxC < dx.shape[1]; ++dxC) { + var dyRCorner = dxR - pad; + var dyCCorner = dxC - pad; + var dotProd = 0; + for (var wR = 0; wR < fSize; ++wR) { + var dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (var wC = 0; wC < fSize; ++wC) { + var dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + var maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + var curPos = wR * fSize + wC; + var mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + var pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + }; + NDArrayMathCPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'min'); + }; + NDArrayMathCPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + return this.pool(x, fSize, stride, pad, 'avg'); + }; + NDArrayMathCPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var output = ndarray_1.Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + var effectiveInputSize = alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + var effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (var r = 0; r < output.shape[0]; r++) { + for (var c = 0; c < output.shape[1]; c++) { + for (var d = 0; d < output.shape[2]; d++) { + var sourceFracRow = (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + var sourceFracCol = (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + var sourceRowFloor = Math.floor(sourceFracRow); + var sourceRowCeil = Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + var sourceColFloor = Math.floor(sourceFracCol); + var sourceColCeil = Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + var topLeft = x.get(sourceRowFloor, sourceColFloor, d); + var bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + var topRight = x.get(sourceRowFloor, sourceColCeil, d); + var bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + var rowFrac = sourceFracRow - sourceRowFloor; + var colFrac = sourceFracCol - sourceColFloor; + var top_1 = topLeft + (topRight - topLeft) * colFrac; + var bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + var newValue = top_1 + (bottom - top_1) * rowFrac; + output.set(newValue, r, c, d); + } + } + } + return output; + }; + NDArrayMathCPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + if (varianceEpsilon === void 0) { varianceEpsilon = .001; } + var xValues = x.getValues(); + var meanValues = mean.getValues(); + var varianceValues = variance.getValues(); + var scaleValues = scale ? scale.getValues() : new Float32Array([1]); + var offsetValues = offset ? offset.getValues() : new Float32Array([0]); + var outValues = new Float32Array(xValues.length); + for (var i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt(varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return ndarray_1.NDArray.make(x.shape, { values: outValues }); + }; + return NDArrayMathCPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathCPU = NDArrayMathCPU; + +},{"../math/conv_util":17,"../util":87,"./concat3d_util":16,"./copy2d_util":18,"./math":20,"./ndarray":23}],22:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var concat3d_util = require("./concat3d_util"); +var conv_util = require("./conv_util"); +var math_1 = require("./math"); +var ndarray = require("./ndarray"); +var ndarray_1 = require("./ndarray"); +var addscaledmat_gpu = require("./webgl/addscaledmat_gpu"); +var addsubmuldiv_gpu = require("./webgl/addsubmuldiv_gpu"); +var addsubmuldiv_gpu_1 = require("./webgl/addsubmuldiv_gpu"); +var argmaxequals_gpu = require("./webgl/argmaxequals_gpu"); +var argminmax_gpu = require("./webgl/argminmax_gpu"); +var avg_pool_gpu = require("./webgl/avg_pool_gpu"); +var batchnorm_gpu = require("./webgl/batchnorm_gpu"); +var concat3d_gpu = require("./webgl/concat3d_gpu"); +var conv_backprop_gpu = require("./webgl/conv_backprop_gpu"); +var conv_gpu = require("./webgl/conv_gpu"); +var copy_gpu = require("./webgl/copy_gpu"); +var exp_gpu = require("./webgl/exp_gpu"); +var gpgpu_context_1 = require("./webgl/gpgpu_context"); +var gpgpu_util = require("./webgl/gpgpu_util"); +var log_gpu = require("./webgl/log_gpu"); +var logsumexp_gpu = require("./webgl/logsumexp_gpu"); +var max_pool_backprop_gpu = require("./webgl/max_pool_backprop_gpu"); +var max_pool_gpu = require("./webgl/max_pool_gpu"); +var min_pool_gpu = require("./webgl/min_pool_gpu"); +var minmax_gpu = require("./webgl/minmax_gpu"); +var mulmat_gpu = require("./webgl/mulmat_gpu"); +var neg_gpu = require("./webgl/neg_gpu"); +var pool_gpu = require("./webgl/pool_gpu"); +var reducesum_gpu = require("./webgl/reducesum_gpu"); +var relu_gpu = require("./webgl/relu_gpu"); +var reshape_gpu = require("./webgl/reshape_gpu"); +var resize_bilinear_gpu = require("./webgl/resize_bilinear_gpu"); +var shader_compiler = require("./webgl/shader_compiler"); +var sigmoid_gpu = require("./webgl/sigmoid_gpu"); +var step_gpu = require("./webgl/step_gpu"); +var texture_manager_1 = require("./webgl/texture_manager"); +var trig_gpu = require("./webgl/trig_gpu"); +var webgl_util = require("./webgl/webgl_util"); +var ARGMAX_PROG = 'argmax'; +var ARGMAX_EQUALS_PROG = 'argmaxequals'; +var ARGMIN_PROG = 'argmin'; +var BATCHNORM_PROG = 'batchnorm'; +var COPY_PROG = 'copy'; +var CONCAT_PROG = 'concat'; +var ADD_SCALED_MAT_PROG = 'addscaledmat'; +var MATMUL_PROG = 'matmul'; +var RELU_PROG = 'relu'; +var TANH_PROG = 'tanh'; +var SIN_PROG = 'sin'; +var SIGMOID_PROG = 'sigmoid'; +var MAX_PROG = 'max'; +var MIN_PROG = 'min'; +var NEG_PROG = 'neg'; +var EXP_PROG = 'exp'; +var LOG_PROG = 'log'; +var SUM_PROG = 'sum'; +var STEP_PROG = 'step'; +var LOGSUMEXP_PROG = 'logsumexp'; +var RESHAPE_PROG = 'reshape'; +var ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; +var CONV2D_PROG = 'conv'; +var CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +var CONV2D_DERW_PROG = 'conv_derw'; +var CONV2D_DERB_PROG = 'conv_derb'; +var MAX_POOL_PROG = 'maxpool'; +var MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +var MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +var MIN_POOL_PROG = 'minpool'; +var AVG_POOL_PROG = 'avgpool'; +var RESIZE_BILINEAR_PROG = 'resizebilin'; +function makeCopyProgramName(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + var shapeName = sourceShapeRowCol[0] + "_" + sourceShapeRowCol[1]; + var srcSizeName = sourceSizeRowCol[0] + "_" + sourceSizeRowCol[1]; + var dstSizeName = destSizeRowCol[0] + "_" + destSizeRowCol[1]; + return COPY_PROG + "_" + shapeName + "_" + srcSizeName + "_" + dstSizeName; +} +var NDArrayMathGPU = (function (_super) { + __extends(NDArrayMathGPU, _super); + function NDArrayMathGPU(gpgpu, safeMode) { + if (safeMode === void 0) { safeMode = true; } + var _this = _super.call(this, safeMode) || this; + _this.programCache = {}; + if (gpgpu == null) { + var gl = gpgpu_util.createWebGLContext(); + _this.gpgpu = new gpgpu_context_1.GPGPUContext(gl); + _this.gpgpuCreatedLocally = true; + } + else { + _this.gpgpu = gpgpu; + _this.gpgpuCreatedLocally = false; + } + _this.textureManager = new texture_manager_1.TextureManager(_this.gpgpu); + ndarray.initializeGPU(_this.gpgpu, _this.textureManager); + return _this; + } + NDArrayMathGPU.prototype.getGPGPUContext = function () { + return this.gpgpu; + }; + NDArrayMathGPU.prototype.cloneInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), function () { return copy_gpu.getFragmentShaderSource(textureShapeRC, textureShapeRC, textureShapeRC); }); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + copy_gpu.copy(this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeInternal = function (ndarray, newShape) { + var newTexShape; + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error("Reshapes into " + newShape.length + "-dim ndarray is not yet " + + "supported on GPU"); + } + var actualTexShape = ndarray.getTextureShapeRC(newTexShape); + var clonedArray; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } + else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + }; + NDArrayMathGPU.prototype.slice2DInternal = function (input, beginRowCol, sizeRowCol) { + var result = ndarray_1.NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal(input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + }; + NDArrayMathGPU.prototype.copy2DInternal = function (source, sourceBeginRowCol, sourceSizeRowCol, dest, destBeginRowCol, destSizeRowCol) { + var sourceShapeRC = source.getTextureShapeRC(); + var destShapeRC = dest.getTextureShapeRC(); + var program = this.getAndSaveProgram(makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), function () { return copy_gpu.getFragmentShaderSource(sourceShapeRC, sourceSizeRowCol, destSizeRowCol); }); + copy_gpu.copy(this.gpgpu, program, source.getTexture(), sourceShapeRC, sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, destBeginRowCol, destSizeRowCol); + }; + NDArrayMathGPU.prototype.concat3DInternal = function (x1, x2, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1.shape); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2.shape); + var actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + var cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + var actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + var cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + var resultShapeRCD = concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + var program = this.getAndSaveProgram(CONCAT_PROG + "_" + x1.shape + "_" + x2.shape + "_" + axis, function () { return concat3d_gpu.getFragmentShaderSource(x1.shape, x2.shape, resultShapeRCD, axis); }); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + concat3d_gpu.concat3D(this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, resultTexShape); + if (cleanupX1) { + x1.dispose(); + } + if (cleanupX2) { + x2.dispose(); + } + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.scalarPlusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayMinusScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.scalarMinusArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scaledArrayAddInternal = function (c1, a, c2, b) { + var cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + var program = this.getAndSaveProgram(ADD_SCALED_MAT_PROG, function () { return addscaledmat_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + addscaledmat_gpu.addScaledMatrices(this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.scalarTimesArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.negInternal = function (a) { + var program = this.getAndSaveProgram(NEG_PROG, function () { return neg_gpu.getFragmentShaderSource(); }); + var textureShapeRC = a.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + neg_gpu.neg(this.gpgpu, program, a.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reshapeTexture = function (a, newTextureShape) { + var aTexShape = a.getTextureShapeRC(); + var program = this.getAndSaveProgram(RESHAPE_PROG, function () { return reshape_gpu.getFragmentShaderSource(); }); + var resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape(this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], resultTexture, newTextureShape[0], newTextureShape[1]); + return ndarray_1.NDArray.make(a.shape, { texture: resultTexture, textureShapeRC: newTextureShape }); + }; + NDArrayMathGPU.prototype.matMulInternal = function (a, b, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + var outerShapeA = (aOrientation === math_1.MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + var outerShapeB = (bOrientation === math_1.MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + var outShape = [outerShapeA, outerShapeB]; + var outTexShape = webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + var outTexture = this.textureManager.acquireTexture(outTexShape); + var out = new ndarray_1.Array2D(outShape, { texture: outTexture, textureShapeRC: outTexShape }); + var key = shader_compiler.makeShaderKey([a, b], out); + var program = this.getAndSaveProgram(MATMUL_PROG + "_" + key + "_" + aOrientation + "_" + bOrientation, function () { return mulmat_gpu.getFragmentShader(a, b, out, aOrientation, bOrientation); }); + mulmat_gpu.multiplyMatrix(this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, outTexShape); + return out; + }; + NDArrayMathGPU.prototype.elementWiseMulInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '*', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.elementWiseMulBroadcastInternal = function (a, b) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.batchNormalization3DInternal = function (x, mean, variance, varianceEpsilon, scale, offset) { + var xTexShape = x.getTextureShapeRC(); + var cleanupMean = false; + var preferredMeanTexShape = mean.rank === 1 ? [1, mean.size] : xTexShape; + var meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + var cleanupVariance = false; + var preferredVarianceTexShape = variance.rank === 1 ? [1, variance.size] : xTexShape; + var varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + var scaleTexShape = null; + var cleanupScale = false; + if (scale != null) { + var preferredScaleTexShape = scale.rank === 1 ? [1, scale.size] : xTexShape; + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + var offsetTexShape = null; + var cleanupOffset = false; + if (offset != null) { + var preferredOffsetTexShape = offset.rank === 1 ? [1, offset.size] : xTexShape; + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + var resultTexShape = x.getTextureShapeRC(); + var program = this.getAndSaveProgram(BATCHNORM_PROG + "_" + xTexShape + "_" + meanTexShape + "_" + varianceTexShape + "_" + + (scaleTexShape + "_" + offsetTexShape + "_" + varianceEpsilon), function () { return batchnorm_gpu.getFragmentShaderSource(xTexShape, meanTexShape, varianceTexShape, offsetTexShape, scaleTexShape, varianceEpsilon); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + batchnorm_gpu.batchNormalization(this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), meanTexShape, variance.getTexture(), varianceTexShape, offset != null ? offset.getTexture() : null, offset != null ? offsetTexShape : null, scale != null ? scale.getTexture() : null, scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale.dispose(); + } + if (cleanupOffset) { + offset.dispose(); + } + return ndarray_1.NDArray.make(x.shape, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.switchDimInternal = function (a, newDim) { + throw new Error('Not yet implemented!'); + }; + NDArrayMathGPU.prototype.sumInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(SUM_PROG + "_" + numRows + "_" + numColumns, function () { return reducesum_gpu.getFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMinInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMIN_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_PROG + "_" + numRows + "_" + numColumns, function () { return argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argminmax_gpu.argMinMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.argMaxEqualsInternal = function (x1, x2) { + var actualX1TexShape = x1.getTextureShapeRC(); + var actualX2TexShape = x2.getTextureShapeRC(); + var cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + var textureShapeRC = x1.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(ARGMAX_EQUALS_PROG + "_" + numRows + "_" + numColumns, function () { return argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + argmaxequals_gpu.argMaxEquals(this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, numColumns, resultTexture); + if (cleanupX2) { + x2.dispose(); + } + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.topKInternal = function (ndarray, k) { + throw new Error('topK GPU not yet implemented!'); + }; + NDArrayMathGPU.prototype.minInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MIN_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMinFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.maxInternal = function (ndarray) { + var textureShapeRC = ndarray.getTextureShapeRC(); + var numRows = textureShapeRC[0], numColumns = textureShapeRC[1]; + var program = this.getAndSaveProgram(MAX_PROG + "_" + numRows + "_" + numColumns, function () { return minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns); }); + var resultTexture = this.textureManager.acquireTexture([1, 1]); + minmax_gpu.minMax(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, resultTexture); + return new ndarray_1.Scalar({ texture: resultTexture }); + }; + NDArrayMathGPU.prototype.divideInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.scalarDividedByArrayInternal = function (c, a) { + return this.addSubMulDiv(c, a, a.shape, addsubmuldiv_gpu_1.OperandType.SCALAR, '/', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.arrayDividedByScalarInternal = function (a, c) { + return this.addSubMulDiv(a, c, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '/', addsubmuldiv_gpu_1.OperandType.SCALAR); + }; + NDArrayMathGPU.prototype.addInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '+', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.subInternal = function (a, b) { + return this.addSubMulDiv(a, b, a.shape, addsubmuldiv_gpu_1.OperandType.MATRIX, '-', addsubmuldiv_gpu_1.OperandType.MATRIX); + }; + NDArrayMathGPU.prototype.logSumExpInternal = function (ndarray) { + var _a = ndarray.getTextureShapeRC(), numRows = _a[0], numColumns = _a[1]; + var program = this.getAndSaveProgram(LOGSUMEXP_PROG + "_" + numRows + "_" + numColumns, function () { return logsumexp_gpu.getFragmentShaderSource(numRows, numColumns); }); + var result = new ndarray_1.Scalar({ texture: this.textureManager.acquireTexture([1, 1]) }); + reducesum_gpu.reduceSum(this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, result.getTexture()); + return result; + }; + NDArrayMathGPU.prototype.expInternal = function (ndarray) { + var program = this.getAndSaveProgram(EXP_PROG, function () { return exp_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + exp_gpu.exp(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.logInternal = function (ndarray) { + var program = this.getAndSaveProgram(LOG_PROG, function () { return log_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.reluInternal = function (ndarray) { + var program = this.getAndSaveProgram(RELU_PROG, function () { return relu_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + relu_gpu.relu(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sigmoidInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIGMOID_PROG, function () { return sigmoid_gpu.getSigmoidFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + sigmoid_gpu.sigmoid(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.tanhInternal = function (ndarray) { + var program = this.getAndSaveProgram(TANH_PROG, function () { return trig_gpu.getTanhFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.tanh(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.sinInternal = function (ndarray) { + var program = this.getAndSaveProgram(SIN_PROG, function () { return trig_gpu.getSinFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + trig_gpu.sin(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.stepInternal = function (ndarray) { + var program = this.getAndSaveProgram(STEP_PROG, function () { return step_gpu.getFragmentShaderSource(); }); + var textureShapeRC = ndarray.getTextureShapeRC(); + var resultTexture = this.textureManager.acquireTexture(textureShapeRC); + step_gpu.step(this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], textureShapeRC[1], resultTexture); + return ndarray_1.NDArray.make(ndarray.shape, { texture: resultTexture, textureShapeRC: textureShapeRC }); + }; + NDArrayMathGPU.prototype.conv2dInternal = function (x, weights, biases, stride, zeroPad) { + var fieldSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_gpu.getFragmentShaderSource(x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fieldSize, outputDepth, stride, zeroPad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_gpu.convolve(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dBackPropInternal = function (x, dy, weights, stride, pad) { + var fSize = weights.shape[0]; + var inputDepth = weights.shape[2]; + var outputDepth = weights.shape[3]; + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var cleanupX = false; + var actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupY = false; + var actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + var dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + var db = this.conv2dDerBias(dy); + var dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return { dx: dx, db: db, dw: dw }; + }; + NDArrayMathGPU.prototype.conv2dTransposeInternal = function (x, weights, biases, origStride, origPad) { + var origInputDepth = weights.shape[2]; + var origOutputDepth = weights.shape[3]; + var fieldSize = weights.shape[0]; + var progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource(x.shape, fieldSize, origInputDepth, origStride, origPad, biases != null); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var wTexShape = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fieldSize); + var biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupW = false; + var actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + var cleanupB = false; + if (biases != null) { + var actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + var dilatedRC = conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + var pad = fieldSize - 1 - origPad; + var resultShape = conv_util.computeOutputShape3D([dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, origInputDepth, 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.convTranspose(this.gpgpu, program, x.getTexture(), weights.getTexture(), biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerWeights = function (x, dY, fSize, stride, zeroPad) { + var inputDepth = x.shape[2]; + var outputDepth = dY.shape[2]; + var progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource(x.shape, fSize, outputDepth, stride, zeroPad); + }); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var yShape = conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride, zeroPad); + var yTexShape = conv_util.computeTexShapeFrom3D(yShape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derWeights(this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + var weightsShape = conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return ndarray_1.NDArray.make(weightsShape, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.conv2dDerBias = function (dY) { + var outputDepth = dY.shape[2]; + var progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + var program = this.getAndSaveProgram(progKey, function () { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + var yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + var cleanupY = false; + var actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + var resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + conv_backprop_gpu.derBias(this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + if (cleanupY) { + dY.dispose(); + } + return ndarray_1.NDArray.make([outputDepth], { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.pool = function (program, x, fSize, stride, pad) { + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + var resultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + var poolResultTex = this.textureManager.acquireTexture(resultTexShape); + pool_gpu.poolCommon(this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + if (cleanupX) { + x.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: poolResultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.maxPoolInternal = function (x, fSize, stride, pad) { + var maxPoolProgKey = [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(maxPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.minPoolInternal = function (x, fSize, stride, pad) { + var minPoolProgKey = [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var minPoolProgram = this.getAndSaveProgram(minPoolProgKey, function () { + return min_pool_gpu.getFragmentShaderMinPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(minPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.avgPoolInternal = function (x, fSize, stride, pad) { + var avgPoolProgKey = [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + var avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, function () { + return avg_pool_gpu.getFragmentShaderAvgPoolSource(x.shape, fSize, stride, pad); + }); + return this.pool(avgPoolProgram, x, fSize, stride, pad); + }; + NDArrayMathGPU.prototype.maxPoolBackpropInternal = function (dy, x, fSize, origStride, origPad) { + var maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + var maxPoolPositionsProgram = this.getAndSaveProgram(maxPoolPositionsProgKey, function () { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource(x.shape, fSize, origStride, origPad); + }); + var maxPoolResultShape = conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], origStride, origPad); + var maxPoolResultTexShape = conv_util.computeTexShapeFrom3D(maxPoolResultShape); + var maxPoolPositionsResultTex = this.textureManager.acquireTexture(maxPoolResultTexShape); + var xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + var actualXTexShape = x.getTextureShapeRC(xTexShape); + var cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + max_pool_gpu.maxPoolCommon(this.gpgpu, maxPoolPositionsProgram, x.getTexture(), maxPoolPositionsResultTex, maxPoolResultTexShape); + var maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + var program = this.getAndSaveProgram(maxPoolBackpropProgKey, function () { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop(dy.shape, fSize, origStride, origPad); + }); + var dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + var actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + var cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + var dilatedDyRC = conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + var pad = fSize - 1 - origPad; + var resultShapeRCD = conv_util.computeOutputShape3D([dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, pad); + var resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + var resultTex = this.textureManager.acquireTexture(resultTexShape); + max_pool_backprop_gpu.maxPoolBackprop(this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, resultTex, resultTexShape); + if (cleanupDy) { + dy.dispose(); + } + if (cleanupX) { + x.dispose(); + } + this.textureManager.releaseTexture(maxPoolPositionsResultTex, maxPoolResultTexShape); + return ndarray_1.NDArray.make(resultShapeRCD, { texture: resultTex, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.resizeBilinear3DInternal = function (x, newShape2D, alignCorners) { + var programKey = [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + var newShapeRCD = [newShape2D[0], newShape2D[1], x.shape[2]]; + var resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + var program = this.getAndSaveProgram(programKey, function () { return resize_bilinear_gpu.getFragmentShaderSource(x.shape, newShape2D, alignCorners); }); + var resultTexture = this.textureManager.acquireTexture(resultTexShape); + resize_bilinear_gpu.resizeBilinear(this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + return ndarray_1.NDArray.make(newShapeRCD, { texture: resultTexture, textureShapeRC: resultTexShape }); + }; + NDArrayMathGPU.prototype.getAndSaveProgram = function (programKey, getShaderSource) { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + }; + NDArrayMathGPU.prototype.addSubMulDiv = function (a, b, resultShape, operandA, opType, operandB) { + var cleanupB = false; + var aOrientation = math_1.MatrixOrientation.REGULAR; + var bOrientation = math_1.MatrixOrientation.REGULAR; + var logicalBTexShape; + if (operandA === addsubmuldiv_gpu_1.OperandType.MATRIX && operandB === addsubmuldiv_gpu_1.OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + var aTexShape_1 = a.getTextureShapeRC(); + var bTexShape_1 = b.getTextureShapeRC(); + logicalBTexShape = bTexShape_1; + if (a.rank === 1) { + if (!util.arraysEqual(bTexShape_1, aTexShape_1)) { + bOrientation = math_1.MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape_1[1], bTexShape_1[0]]; + } + } + if (!util.arraysEqual(aTexShape_1, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape_1); + bOrientation = math_1.MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } + else { + logicalBTexShape = b.getTextureShapeRC(); + } + var aTexShape = a.getTextureShapeRC(); + var bTexShape = b.getTextureShapeRC(); + var programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + var program = this.getAndSaveProgram(programKey, function () { return addsubmuldiv_gpu.getFragmentShaderSource(operandA, aOrientation, opType, operandB, bOrientation); }); + var resultTextureShape = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + var resultTexture = this.textureManager.acquireTexture(resultTextureShape); + addsubmuldiv_gpu.addSubMulDiv(this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), bTexShape, resultTexture, resultTextureShape); + if (cleanupB) { + b.dispose(); + } + return ndarray_1.NDArray.make(resultShape, { texture: resultTexture, textureShapeRC: resultTextureShape }); + }; + NDArrayMathGPU.prototype.doGPUShapesMatch = function (a, b) { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + b.getTextureShapeRC(a.getTextureShapeRC()); + } + else if (b.inGPU()) { + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + }; + NDArrayMathGPU.prototype.getTextureManager = function () { + return this.textureManager; + }; + NDArrayMathGPU.prototype.dispose = function () { + for (var programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + }; + return NDArrayMathGPU; +}(math_1.NDArrayMath)); +exports.NDArrayMathGPU = NDArrayMathGPU; + +},{"../util":87,"./concat3d_util":16,"./conv_util":17,"./math":20,"./ndarray":23,"./webgl/addscaledmat_gpu":24,"./webgl/addsubmuldiv_gpu":25,"./webgl/argmaxequals_gpu":26,"./webgl/argminmax_gpu":27,"./webgl/avg_pool_gpu":28,"./webgl/batchnorm_gpu":29,"./webgl/concat3d_gpu":31,"./webgl/conv_backprop_gpu":32,"./webgl/conv_gpu":33,"./webgl/copy_gpu":34,"./webgl/exp_gpu":35,"./webgl/gpgpu_context":36,"./webgl/gpgpu_util":37,"./webgl/log_gpu":38,"./webgl/logsumexp_gpu":39,"./webgl/max_pool_backprop_gpu":40,"./webgl/max_pool_gpu":41,"./webgl/min_pool_gpu":42,"./webgl/minmax_gpu":43,"./webgl/mulmat_gpu":44,"./webgl/neg_gpu":45,"./webgl/pool_gpu":46,"./webgl/reducesum_gpu":47,"./webgl/relu_gpu":48,"./webgl/reshape_gpu":50,"./webgl/resize_bilinear_gpu":51,"./webgl/shader_compiler":52,"./webgl/sigmoid_gpu":53,"./webgl/step_gpu":54,"./webgl/texture_manager":56,"./webgl/trig_gpu":57,"./webgl/webgl_util":59}],23:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var webgl_util = require("./webgl/webgl_util"); +exports.GPGPU = null; +exports.TEXTURE_MANAGER = null; +function initializeGPU(gpgpu, textureManager) { + exports.GPGPU = gpgpu; + exports.TEXTURE_MANAGER = textureManager; +} +exports.initializeGPU = initializeGPU; +function throwIfGPUNotInitialized() { + if (exports.GPGPU == null || exports.TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} +var NDArray = (function () { + function NDArray(shape, data) { + util.assert(data.values != null || data.texture != null, 'Either `values` or `texture` must be defined'); + util.assert(data.texture == null || (data.textureShapeRC != null), '`textureShape` must be defined when `texture` is defined'); + this.size = util.sizeFromShape(shape); + if (data.values != null) { + util.assert(this.size === data.values.length, 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + this.shape = shape; + this.data = data; + var dim = this.shape.length; + if (dim < 2) { + this.strides = []; + } + else { + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (var i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + NDArray.zeros = function (shape) { + var values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, { values: values }); + }; + NDArray.zerosLike = function (another) { + return NDArray.zeros(another.shape); + }; + NDArray.like = function (another) { + var values = another.getValues(); + return NDArray.make(another.shape, { values: new Float32Array(values) }); + }; + NDArray.make = function (shape, data) { + switch (shape.length) { + case 0: + return new Scalar(data); + case 1: + return new Array1D(data); + case 2: + return new Array2D(shape, data); + case 3: + return new Array3D(shape, data); + case 4: + return new Array4D(shape, data); + default: + return new NDArray(shape, data); + } + }; + NDArray.prototype.reshape = function (newShape) { + if (util.arraysEqual(this.shape, newShape)) { + return this; + } + util.assert(this.size === util.sizeFromShape(newShape), 'new shape and old shape must have the same number of elements.'); + return NDArray.make(newShape, this.data); + }; + NDArray.prototype.asScalar = function () { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + }; + NDArray.prototype.as1D = function () { + return this.reshape([this.size]); + }; + NDArray.prototype.as2D = function (rows, columns) { + return this.reshape([rows, columns]); + }; + NDArray.prototype.as3D = function (rows, columns, depth) { + return this.reshape([rows, columns, depth]); + }; + NDArray.prototype.as4D = function (rows, columns, depth, depth2) { + return this.reshape([rows, columns, depth, depth2]); + }; + Object.defineProperty(NDArray.prototype, "rank", { + get: function () { + return this.shape.length; + }, + enumerable: true, + configurable: true + }); + NDArray.prototype.get = function () { + var locs = []; + for (var _i = 0; _i < arguments.length; _i++) { + locs[_i] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + }; + NDArray.prototype.add = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + this.set.apply(this, [this.get.apply(this, locs) + value].concat(locs)); + }; + NDArray.prototype.set = function (value) { + var locs = []; + for (var _i = 1; _i < arguments.length; _i++) { + locs[_i - 1] = arguments[_i]; + } + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + }; + NDArray.prototype.locToIndex = function (locs) { + var index = locs[locs.length - 1]; + for (var i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + }; + NDArray.prototype.indexToLoc = function (index) { + var locs = new Array(this.shape.length); + for (var i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + }; + NDArray.prototype.fill = function (value) { + this.getValues().fill(value); + }; + NDArray.prototype.getData = function () { + return this.data; + }; + NDArray.prototype.getValues = function () { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = exports.GPGPU.downloadMatrixFromTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1]); + this.disposeTexture(); + } + return this.data.values; + }; + NDArray.prototype.uploadToGPU = function (preferredTexShape) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape(exports.GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + exports.TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + exports.GPGPU.uploadMatrixToTexture(this.data.texture, this.data.textureShapeRC[0], this.data.textureShapeRC[1], this.data.values); + this.data.values = null; + }; + NDArray.prototype.getTexture = function (preferredShapeRC) { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture; + }; + NDArray.prototype.getTextureShapeRC = function (preferredShapeRC) { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC; + }; + NDArray.prototype.dispose = function () { + this.data.values = null; + this.shape = null; + if (this.data.texture != null) { + this.disposeTexture(); + } + }; + NDArray.prototype.disposeTexture = function () { + throwIfGPUNotInitialized(); + exports.TEXTURE_MANAGER.releaseTexture(this.data.texture, this.data.textureShapeRC); + this.data.texture = null; + this.data.textureShapeRC = null; + }; + NDArray.prototype.inGPU = function () { + return this.data.texture != null; + }; + NDArray.prototype.equals = function (t) { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + }; + NDArray.rand = function (shape, randFunction) { + var size = util.sizeFromShape(shape); + var values = new Float32Array(size); + for (var i = 0; i < size; i++) { + values[i] = randFunction(); + } + return NDArray.make(shape, { values: values }); + }; + NDArray.randNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev); }); + }; + NDArray.randTruncatedNormal = function (shape, mean, stdDev) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + return NDArray.rand(shape, function () { return util.randGauss(mean, stdDev, true); }); + }; + NDArray.randUniform = function (shape, a, b) { + return NDArray.rand(shape, function () { return util.randUniform(a, b); }); + }; + return NDArray; +}()); +exports.NDArray = NDArray; +var Scalar = (function (_super) { + __extends(Scalar, _super); + function Scalar(data) { + var _this = this; + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + _this = _super.call(this, [], data) || this; + return _this; + } + Scalar.new = function (value) { + return new Scalar({ values: new Float32Array([value]) }); + }; + Scalar.prototype.get = function () { + return this.getValues()[0]; + }; + Scalar.prototype.set = function (value) { + this.getValues()[0] = value; + }; + Scalar.prototype.add = function (value) { + this.getValues()[0] += value; + }; + return Scalar; +}(NDArray)); +Scalar.ZERO = Scalar.new(0); +Scalar.ONE = Scalar.new(1); +Scalar.TWO = Scalar.new(2); +Scalar.NEG_ONE = Scalar.new(-1); +exports.Scalar = Scalar; +var Array1D = (function (_super) { + __extends(Array1D, _super); + function Array1D(data) { + var _this = this; + var shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC)]; + _this = _super.call(this, shape, data) || this; + return _this; + } + Array1D.new = function (values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + util.assert(inferredShape.length === 1, "Error constructing Array1D. Shape of values " + inferredShape + " is " + + "not 1 dimensional."); + } + return new Array1D({ values: toTypedArray(values) }); + }; + Array1D.prototype.get = function (i) { + return this.getValues()[i]; + }; + Array1D.prototype.set = function (value, i) { + this.getValues()[i] = value; + }; + Array1D.prototype.add = function (value, i) { + this.getValues()[i] += value; + }; + Array1D.prototype.locToIndex = function (loc) { + return loc[0]; + }; + Array1D.prototype.indexToLoc = function (index) { + return [index]; + }; + Array1D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array1D; +}(NDArray)); +exports.Array1D = Array1D; +var Array2D = (function (_super) { + __extends(Array2D, _super); + function Array2D(shape, data) { + var _this = this; + util.assert(shape.length === 2, 'Shape should be of length 2'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + return _this; + } + Array2D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array2D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array2D(shape, { values: toTypedArray(values) }); + }; + Array2D.prototype.get = function (i, j) { + return this.getValues()[this.stride0 * i + j]; + }; + Array2D.prototype.set = function (value, i, j) { + this.getValues()[this.stride0 * i + j] = value; + }; + Array2D.prototype.add = function (value, i, j) { + this.getValues()[this.stride0 * i + j] += value; + }; + Array2D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + locs[1]; + }; + Array2D.prototype.indexToLoc = function (index) { + return [Math.floor(index / this.stride0), index % this.stride0]; + }; + Array2D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array2D; +}(NDArray)); +exports.Array2D = Array2D; +var Array3D = (function (_super) { + __extends(Array3D, _super); + function Array3D(shape, data) { + var _this = this; + util.assert(shape.length === 3, 'Shape should be of length 3'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + return _this; + } + Array3D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array3D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array3D(shape, { values: toTypedArray(values) }); + }; + Array3D.prototype.get = function (i, j, k) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + }; + Array3D.prototype.set = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + }; + Array3D.prototype.add = function (value, i, j, k) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + }; + Array3D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + }; + Array3D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + }; + Array3D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array3D; +}(NDArray)); +exports.Array3D = Array3D; +var Array4D = (function (_super) { + __extends(Array4D, _super); + function Array4D(shape, data) { + var _this = this; + util.assert(shape.length === 4, 'Shape should be of length 4'); + _this = _super.call(this, shape, data) || this; + _this.stride0 = _this.strides[0]; + _this.stride1 = _this.strides[1]; + _this.stride2 = _this.strides[2]; + return _this; + } + Array4D.new = function (shape, values) { + if (!(values instanceof Float32Array)) { + var inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch(shape, inferredShape, "Error when constructing Array4D. Shape of values " + + (inferredShape + " does not match the provided shape ") + + (shape + ". ")); + } + } + return new Array4D(shape, { values: toTypedArray(values) }); + }; + Array4D.prototype.get = function (i, j, k, l) { + return this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + }; + Array4D.prototype.set = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + }; + Array4D.prototype.add = function (value, i, j, k, l) { + this.getValues()[this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + }; + Array4D.prototype.locToIndex = function (locs) { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + }; + Array4D.prototype.indexToLoc = function (index) { + var i = Math.floor(index / this.stride0); + index -= i * this.stride0; + var j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + }; + Array4D.zeros = function (shape) { + return NDArray.zeros(shape); + }; + return Array4D; +}(NDArray)); +exports.Array4D = Array4D; +function toTypedArray(a) { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} + +},{"../util":87,"./webgl/webgl_util":59}],24:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n uniform sampler2D matrixAScalar;\n uniform sampler2D matrixBScalar;\n varying vec2 resultUV;\n\n const vec2 halfTexel = vec2(0.5, 0.5);\n\n void main() {\n float a = texture2D(matrixA, resultUV).r;\n float b = texture2D(matrixB, resultUV).r;\n float aScalar = texture2D(matrixAScalar, halfTexel).r;\n float bScalar = texture2D(matrixBScalar, halfTexel).r;\n vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar);\n gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function addScaledMatrices(gpgpu, addScaledMatricesProgram, a, b, rows, columns, aScalar, bScalar, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} +exports.addScaledMatrices = addScaledMatrices; +function uploadAddScaledMatricesDownload(a, b, rows, columns, aScalar, bScalar) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource()); + var aTex = gpgpu.createMatrixTexture(rows, columns); + var bTex = gpgpu.createMatrixTexture(rows, columns); + var aScalarTex = gpgpu.createMatrixTexture(1, 1); + var bScalarTex = gpgpu.createMatrixTexture(1, 1); + var resultTex = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + addScaledMatrices(gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, resultTex); + var result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadAddScaledMatricesDownload = uploadAddScaledMatricesDownload; + +},{"./gpgpu_context":36}],25:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var binaryop_gpu = require("./binaryop_gpu"); +var OperandType; +(function (OperandType) { + OperandType[OperandType["MATRIX"] = 0] = "MATRIX"; + OperandType[OperandType["SCALAR"] = 1] = "SCALAR"; +})(OperandType = exports.OperandType || (exports.OperandType = {})); +function getFragmentShaderSource(aType, aOrientation, op, bType, bOrientation) { + var aUV = operandToShaderSnippet(aType, aOrientation); + var bUV = operandToShaderSnippet(bType, bOrientation); + var resultOp = "gl_FragColor = vec4(a " + op + " b, 0, 0, 0);"; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function operandToShaderSnippet(operand, orientation) { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === math_1.MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} +function addSubMulDiv(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + return binaryop_gpu.binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol); +} +exports.addSubMulDiv = addSubMulDiv; +function uploadScalarPlusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarPlusMatrixDownload = uploadScalarPlusMatrixDownload; +function uploadMatrixMinusScalarDownload(a, aShape, b, aOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, math_1.MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload(a, aShape, new Float32Array([b]), [1, 1], src); +} +exports.uploadMatrixMinusScalarDownload = uploadMatrixMinusScalarDownload; +function uploadScalarMinusMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '-', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarMinusMatrixDownload = uploadScalarMinusMatrixDownload; +function uploadScalarTimesMatrixDownload(a, b, bShape, bOrientation) { + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.SCALAR, math_1.MatrixOrientation.REGULAR, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(new Float32Array([a]), [1, 1], b, bShape, src); +} +exports.uploadScalarTimesMatrixDownload = uploadScalarTimesMatrixDownload; +function uploadMatrixTimesMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixTimesMatrixDownload = uploadMatrixTimesMatrixDownload; +function uploadMatrixPlusMatrixDownload(a, b, shape, aOrientation, bOrientation) { + if (aOrientation === void 0) { aOrientation = math_1.MatrixOrientation.REGULAR; } + if (bOrientation === void 0) { bOrientation = math_1.MatrixOrientation.REGULAR; } + var src = getFragmentShaderSource(OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} +exports.uploadMatrixPlusMatrixDownload = uploadMatrixPlusMatrixDownload; + +},{"../math":20,"./binaryop_gpu":30}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var argminmax_gpu = require("./argminmax_gpu"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;"; +} +function getFragmentShaderMainSource() { + return "\n void main() {\n float argMaxA = getArgMinMax(matrixA);\n float argMaxB = getArgMinMax(matrixB);\n float value;\n if (isNaN(argMaxA)) {\n value = argMaxA;\n } else if (isNaN(argMaxB)) {\n value = argMaxB;\n } else {\n value = float(argMaxA == argMaxB);\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getArgMaxEqualsFragmentShaderSource(rows, columns) { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +exports.getArgMaxEqualsFragmentShaderSource = getArgMaxEqualsFragmentShaderSource; +function argMaxEquals(gpgpu, maxEqualsProgram, a, b, numRows, numCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.argMaxEquals = argMaxEquals; + +},{"./argminmax_gpu":27}],27:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderMainSource() { + return "\n void main() {\n gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0);\n }"; +} +function getArgMinMaxFragmentShaderSource(rows, columns, compOp) { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} +function getArgMinFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} +exports.getArgMinFragmentShaderSource = getArgMinFragmentShaderSource; +function getArgMaxFragmentShaderSource(rows, columns) { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} +exports.getArgMaxFragmentShaderSource = getArgMaxFragmentShaderSource; +function getFragmentShaderGetArgMinMaxSource(compOp, rows, columns) { + return "\n const vec2 dimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n float getArgMinMax(in sampler2D matrix) {\n vec2 bestCR = vec2(0, 0);\n float bestValue = texture2D(matrix, bestCR).r;\n\n for (float c = 0.0; c < dimCR.x; c += 1.0) {\n for (float r = 0.0; r < dimCR.y; r += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / dimCR;\n float value = texture2D(matrix, uv).r;\n if (isNaN(value)) {\n return value;\n }\n if (value " + compOp + " bestValue) {\n bestValue = value;\n bestCR = cr;\n }\n }\n }\n return bestCR.x + (bestCR.y * dimCR.x);\n }\n "; +} +exports.getFragmentShaderGetArgMinMaxSource = getFragmentShaderGetArgMinMaxSource; +function argMinMax(gpgpu, minMaxProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.argMinMax = argMinMax; + +},{"./webgl_util":59}],28:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderAvgPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'avg', false); +} +exports.getFragmentShaderAvgPoolSource = getFragmentShaderAvgPoolSource; +function avgPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.avgPool = avgPool; + +},{"./pool_gpu":46}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(xTexShapeRC, meanTexShapeRC, varianceTexShapeRC, offsetTexShapeRC, scaleTexShapeRC, varianceEpsilon) { + if (varianceEpsilon === void 0) { varianceEpsilon = 0.001; } + var offsetSamplerSnippet = ''; + var offsetShapeInitializationSnippet = ''; + var offsetCoordsSnippet = ''; + var offsetUVSnippet = ''; + var offsetValueSnippet = ''; + var offsetOperationSnippet = '0.0'; + var scaleSamplerSnippet = ''; + var scaleShapeInitializationSnippet = ''; + var scaleCoordsSnippet = ''; + var scaleUVSnippet = ''; + var scaleValueSnippet = ''; + var scaleOperationSnippet = ''; + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = "const vec2 offsetShapeCR = vec2(\n " + offsetTexShapeRC[1] + ", " + offsetTexShapeRC[0] + ");"; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = "const vec2 scaleShapeCR = vec2(\n " + scaleTexShapeRC[1] + ", " + scaleTexShapeRC[0] + ");"; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D mean;\n uniform sampler2D variance;\n " + offsetSamplerSnippet + "\n " + scaleSamplerSnippet + "\n\n varying vec2 resultUV;\n\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 meanShapeCR = vec2(" + meanTexShapeRC[1] + ", " + meanTexShapeRC[0] + ");\n const vec2 varianceShapeCR = vec2(\n " + varianceTexShapeRC[1] + ", " + varianceTexShapeRC[0] + ");\n\n " + offsetShapeInitializationSnippet + "\n " + scaleShapeInitializationSnippet + "\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const float varianceEpsilon = " + varianceEpsilon + ";\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n vec2 meanCoordsCR = mod(yTexCR, meanShapeCR);\n vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR);\n " + offsetCoordsSnippet + "\n " + scaleCoordsSnippet + "\n\n vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR;\n vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR;\n " + offsetUVSnippet + "\n " + scaleUVSnippet + "\n\n float xValue = texture2D(x, resultUV).r;\n float meanValue = texture2D(mean, meanUV).r;\n float varianceValue = texture2D(variance, varianceUV).r;\n " + offsetValueSnippet + "\n " + scaleValueSnippet + "\n\n float inv = 1.0 / sqrt(varianceValue + varianceEpsilon);\n " + scaleOperationSnippet + "\n float xTimesInv = xValue * inv;\n float meanTimesInvWithOffset = " + offsetOperationSnippet + "\n - meanValue * inv;\n\n gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function batchNormalization(gpgpu, program, x, xShapeRowCol, mean, meanShapeRowCol, variance, varianceShapeRowCol, offset, offsetShapeRowCol, scale, scaleShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + var nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} +exports.batchNormalization = batchNormalization; + +},{}],30:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(aResultUV, bResultUV, op) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform sampler2D matrixB;\n varying vec2 resultUV;\n\n void main() {\n float a = texture2D(matrixA, " + aResultUV + ").r;\n float b = texture2D(matrixB, " + bResultUV + ").r;\n " + op + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function binaryOp(gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.binaryOp = binaryOp; +function uploadBinaryOpDownload(a, aShape, b, bShape, fragmentShaderSource) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(fragmentShaderSource); + var aTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + var bTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + var resultShape = [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + var resultTexture = gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + binaryOp(gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, resultShape); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, resultShape[0], resultShape[1]); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadBinaryOpDownload = uploadBinaryOpDownload; + +},{"./gpgpu_context":36}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis) { + var x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + var x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + var yAxes = ['yR', 'yC', 'yD']; + var concatAxis = yAxes[axis]; + return "\n precision highp float;\n uniform sampler2D x1;\n uniform sampler2D x2;\n\n const vec2 x1ShapeCR = vec2(" + x1TexShapeRC[1] + ", " + x1TexShapeRC[0] + ");\n const vec2 x2ShapeCR = vec2(" + x2TexShapeRC[1] + ".0, " + x2TexShapeRC[0] + ".0);\n\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + resultShapeRCD[2] + ".0);\n float yD = mod(yTexCR.x, " + resultShapeRCD[2] + ".0);\n\n float value = 0.0;\n\n if (" + concatAxis + " < " + x1ShapeRCD[axis] + ".0) {\n // Map yR, yC, yD back to x1 coordinates.\n vec2 x1CR = vec2(yC * " + x1ShapeRCD[2] + ".0 + yD, yR);\n vec2 x1UV = (x1CR + halfCR) / x1ShapeCR;\n value = texture2D(x1, x1UV).r;\n } else {\n " + concatAxis + " = " + concatAxis + " - " + x1ShapeRCD[axis] + ".0;\n\n // Map yR, yC, yD back to x2 coordinates.\n vec2 x2CR = vec2(yC * " + x2ShapeRCD[2] + ".0 + yD, yR);\n vec2 x2UV = (x2CR + halfCR) / x2ShapeCR;\n value = texture2D(x2, x2UV).r;\n }\n\n gl_FragColor = vec4(value, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function concat3D(gpgpu, program, x1, x2, result, resultShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} +exports.concat3D = concat3D; + +},{"../conv_util":17}],32:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var conv_gpu = require("./conv_gpu"); +function getFragmentShaderDerWeightsSource(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad) { + var getMatrixValueOrZeroPad = conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + var inputDepth = xShapeRowColDepth[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + var yShape = conv_util.computeOutputShape3D(xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + var yNumRows = yShape[0]; + var yNumCols = yShape[1]; + var yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + var fSizeTimesInputDepth = fSize * inputDepth; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D dy;\n "; + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 dyShapeCR = vec2(" + yTexShapeRC[1] + ", " + yTexShapeRC[0] + ");\n\n void main() {\n vec2 wTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2).\n float wR = floor(wTexCR.y / " + fSizeTimesInputDepth + ".0);\n float wTexRLeftover = wTexCR.y - wR * " + fSizeTimesInputDepth + ".0;\n float wC = floor(wTexRLeftover / " + inputDepth + ".0);\n float d1 = mod(wTexRLeftover, " + inputDepth + ".0);\n float d2 = wTexCR.x;\n\n // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float xR = wR + yR * " + stride + ".0 - " + zeroPad + ".0;\n float xTexR = xR;\n float yTexR = yR;\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n float xC = wC + yC * " + stride + ".0 - " + zeroPad + ".0;\n\n // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC).\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n vec2 xyTexC = vec2(xC, yC) * vec2(" + inputDepth + ".0, " + outputDepth + ".0) +\n vec2(d1, d2);\n float xTexC = xyTexC.x;\n float yTexC = xyTexC.y;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read x(xR, xC, d1) (potentially zero-padded).\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n dotProd += (xValue * dyValue);\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderDerWeightsSource = getFragmentShaderDerWeightsSource; +function getFragmentShaderConvTransposeSource(xShapeRCD, fSize, origInputDepth, origStride, origPad, hasBias) { + var pad = fSize - 1 - origPad; + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], origOutputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + var getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + var biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + var biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + var prologue = "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n " + biasPrologue + "\n "; + return prologue + '\n' + getBiasValue + '\n' + + ("\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + origInputDepth + ".0);\n float d2 = mod(yTexCR.x, " + origInputDepth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) - vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float xR = (xRCorner + wR) / " + origStride + ".0;\n // TODO(smilkov): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (xR < 0.0 || xR >= " + xRows + ".0 || fract(xR) > 0.0) {\n continue;\n }\n\n float wRPerm = " + fSize + ".0 - 1.0 - wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float xC = (xCCorner + wC) / " + origStride + ".0;\n if (xC < 0.0 || xC >= " + xCols + ".0 || fract(xC) > 0.0) {\n continue;\n }\n\n float wCPerm = " + fSize + ".0 - 1.0 - wC;\n float wTexR = wRPerm * " + fSize + ".0 * " + origInputDepth + ".0 +\n wCPerm * " + origInputDepth + ".0 + d2;\n\n for (float d1 = 0.0; d1 < " + origOutputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + origOutputDepth + ".0 + d1;\n float wTexC = d1;\n\n // Read x(xR, xC, d1).\n vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR;\n float xValue = texture2D(x, xUV).r;\n\n // Read w(wRPerm, wCPerm, d2, d1).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n " + biasOperation + "\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"); +} +exports.getFragmentShaderConvTransposeSource = getFragmentShaderConvTransposeSource; +function getFragmentShaderDerBiasSource(dyShapeRCD) { + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + var yNumRows = dyShapeRCD[0], yNumCols = dyShapeRCD[1], outputDepth = dyShapeRCD[2]; + return "\n precision highp float;\n uniform sampler2D dy;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 biasTexCR = floor(gl_FragCoord.xy);\n\n // The bias texture RC shape is [1, d2].\n float d2 = biasTexCR.x;\n\n float derBias = 0.0;\n for (float yR = 0.0; yR < " + yNumRows + ".0; yR += 1.0) {\n float yTexR = yR;\n\n for (float yC = 0.0; yC < " + yNumCols + ".0; yC += 1.0) {\n // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC).\n float yTexC = yC * " + outputDepth + ".0 + d2;\n\n // Read dy(yR, yC, d2).\n vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n derBias += dyValue;\n }\n }\n gl_FragColor = vec4(derBias, 0, 0, 0);\n }"; +} +exports.getFragmentShaderDerBiasSource = getFragmentShaderDerBiasSource; +function derBias(gpgpu, program, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} +exports.derBias = derBias; +function derWeights(gpgpu, program, xTex, dyTex, result, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} +exports.derWeights = derWeights; +function convTranspose(gpgpu, program, xTex, weightsTex, biasesTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convTranspose = convTranspose; + +},{"../conv_util":17,"./conv_gpu":33}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderPrologueSource() { + return "\n precision highp float;\n uniform sampler2D x;\n uniform sampler2D weights;\n uniform sampler2D biases;\n varying vec2 resultUV;"; +} +exports.getFragmentShaderPrologueSource = getFragmentShaderPrologueSource; +function getFragmentShaderGetMatrixValueOrZeroPadSource() { + return "\n float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR,\n vec2 requestedCR) {\n vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR;\n float value = texture2D(matrix, uv).r;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n return mix(value, 0.0, float(outside));\n }"; +} +exports.getFragmentShaderGetMatrixValueOrZeroPadSource = getFragmentShaderGetMatrixValueOrZeroPadSource; +function getFragmentShaderConvolveSource(xShapeRCD, fSize, outputDepth, stride, pad, hasBias) { + var xRows = xShapeRCD[0], xCols = xShapeRCD[1], inputDepth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var wTexShapeRC = conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + return "\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n const vec2 wShapeCR = vec2(" + wTexShapeRC[1] + ", " + wTexShapeRC[0] + ");\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + outputDepth + ".0);\n float d2 = mod(yTexCR.x, " + outputDepth + ".0);\n float wTexC = d2;\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n\n for (float d1 = 0.0; d1 < " + inputDepth + ".0; d1 += 1.0) {\n float xTexC = xC * " + inputDepth + ".0 + d1;\n float wTexR = wR * " + fSize * inputDepth + ".0 +\n wC * " + inputDepth + ".0 + d1;\n\n float xValue =\n getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR));\n\n // Read w(wR, wC, d1, d2).\n vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR;\n float wValue = texture2D(weights, wUV).r;\n\n dotProd += xValue * wValue;\n }\n }\n }\n if (" + hasBias + ") {\n dotProd += getBiasValue(biases, d2);\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderConvolveSource = getFragmentShaderConvolveSource; +function getFragmentShaderGetBiasValueSource(outputDepth) { + return "\n float getBiasValue(in sampler2D bias, float biasC) {\n const vec2 biasShapeCR = vec2(" + outputDepth + ", 1);\n vec2 biasCR = vec2(mod(biasC, " + outputDepth + ".0), 0);\n vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR;\n return texture2D(bias, biasUV).r;\n }"; +} +exports.getFragmentShaderGetBiasValueSource = getFragmentShaderGetBiasValueSource; +function getFragmentShaderSource(aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, hasBias) { + var aShapeRC = conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + var weightShapeRC = conv_util.computeWeightsTexShape(aShapeRowColDepth[2], resultDepth, fieldSize); + var prologue = getFragmentShaderPrologueSource(); + var getMatrixValueOrZeroPad = getFragmentShaderGetMatrixValueOrZeroPadSource(); + var convolve = getFragmentShaderConvolveSource(aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + var getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function convolve(gpgpu, program, a, weights, biases, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} +exports.convolve = convolve; + +},{"../conv_util":17}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getFragmentShaderSource(sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol) { + return "\n precision highp float;\n uniform sampler2D source;\n uniform vec2 sourceStartCR;\n uniform vec2 destStartCR;\n\n const vec2 sourceShapeCR =\n vec2(" + sourceShapeRowCol[1] + ", " + sourceShapeRowCol[0] + ");\n const vec2 sourceSizeCR =\n vec2(" + sourceSizeRowCol[1] + ", " + sourceSizeRowCol[0] + ");\n const vec2 destSizeCR =\n vec2(" + destSizeRowCol[1] + ", " + destSizeRowCol[0] + ");\n\n void main() {\n vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR;\n float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x;\n vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x),\n floor(destOffsetFlat / sourceSizeCR.x));\n vec2 sourceCR = sourceStartCR + sourceOffsetCR;\n vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR;\n gl_FragColor = texture2D(source, sourceUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function copy(gpgpu, program, source, sourceShapeRowCol, sourceStartRowCol, sourceSizeRowCol, dest, destShapeRowCol, destStartRowCol, destSizeRowCol) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion(destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + var sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f(sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + var destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} +exports.copy = copy; + +},{}],35:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getExpUnaryOp() { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function exp(gpgpu, expProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} +exports.exp = exp; +function uploadExpDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} +exports.uploadExpDownload = uploadExpDownload; + +},{"./unaryop_gpu":58}],36:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_util = require("./gpgpu_util"); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +var GPGPUContext = (function () { + function GPGPUContext(gl) { + this.outputTexture = null; + this.program = null; + this.disposed = false; + this.autoDebugValidate = false; + if (gl != null) { + this.gl = gl; + } + else { + this.gl = gpgpu_util.createWebGLContext(); + } + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } + else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context'); + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + GPGPUContext.prototype.dispose = function () { + var _this = this; + this.throwIfDisposed(); + if (this.program != null) { + console.warn('Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn('Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + var gl = this.gl; + webgl_util.callAndCheck(gl, function () { return gl.finish(); }); + webgl_util.callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteFramebuffer(_this.framebuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.vertexBuffer); }); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteBuffer(_this.indexBuffer); }); + this.loseContextExtension.loseContext(); + this.disposed = true; + }; + GPGPUContext.prototype.enableAutomaticDebugValidation = function (enabled) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + }; + GPGPUContext.prototype.createMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.uploadPixelDataToTexture = function (texture, pixels) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + }; + GPGPUContext.prototype.createPackedMatrixTexture = function (rows, columns) { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + }; + GPGPUContext.prototype.deleteMatrixTexture = function (texture) { + var _this = this; + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteTexture(texture); }); + }; + GPGPUContext.prototype.uploadMatrixToTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + var numChannels = 1; + return gpgpu_util.uploadMatrixToTexture(this.gl, texture, rows, columns, matrix, numChannels); + }; + GPGPUContext.prototype.uploadMatrixToPackedTexture = function (texture, rows, columns, matrix) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture(this.gl, texture, rows, columns, matrix); + }; + GPGPUContext.prototype.downloadMatrixFromTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { + return gpgpu_util.downloadMatrixFromOutputTexture(_this.gl, rows, columns); + }); + }; + GPGPUContext.prototype.downloadMatrixFromPackedTexture = function (texture, rows, columns) { + var _this = this; + return this.downloadMatrixDriver(texture, function () { return gpgpu_util.downloadMatrixFromPackedOutputTexture(_this.gl, rows, columns); }); + }; + GPGPUContext.prototype.createProgram = function (fragmentShaderSource) { + this.throwIfDisposed(); + var gl = this.gl; + var fragmentShader = webgl_util.createFragmentShader(gl, fragmentShaderSource); + var vertexShader = gpgpu_util.createVertexShader(gl); + var program = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.attachShader(program, fragmentShader); }); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(vertexShader); }); + webgl_util.callAndCheck(gl, function () { return gl.detachShader(program, fragmentShader); }); + webgl_util.callAndCheck(gl, function () { return gl.deleteShader(fragmentShader); }); + return program; + }; + GPGPUContext.prototype.deleteProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, function () { return _this.gl.deleteProgram(program); }); + } + }; + GPGPUContext.prototype.setProgram = function (program) { + var _this = this; + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, function () { return _this.gl.useProgram(program); }); + }; + GPGPUContext.prototype.getUniformLocation = function (uniformName) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow(this.gl, this.program, uniformName); + }; + GPGPUContext.prototype.setInputMatrixTexture = function (inputMatrixTexture, uniformName, textureUnit) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler(this.gl, this.program, inputMatrixTexture, uniformName, textureUnit); + }; + GPGPUContext.prototype.setOutputMatrixTexture = function (outputMatrixTexture, rows, columns) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + }; + GPGPUContext.prototype.setOutputPackedMatrixTexture = function (outputPackedMatrixTexture, rows, columns) { + this.throwIfDisposed(); + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + this.setOutputMatrixWriteRegionDriver(startColumn, startRow, numColumns, numRows); + }; + GPGPUContext.prototype.setOutputPackedMatrixWriteRegion = function (startRow, numRows, startColumn, numColumns) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + }; + GPGPUContext.prototype.debugValidate = function () { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + }; + GPGPUContext.prototype.executeProgram = function () { + this.throwIfDisposed(); + this.throwIfNoProgram(); + var gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams(gl, this.program, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck(gl, function () { return gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); }); + }; + GPGPUContext.prototype.blockUntilAllProgramsCompleted = function () { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.finish(); }); + }; + GPGPUContext.prototype.downloadMatrixDriver = function (texture, downloadAndDecode) { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer(this.gl, texture, this.framebuffer); + var result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer(this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } + else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + }; + GPGPUContext.prototype.setOutputMatrixTextureDriver = function (outputMatrixTextureMaybePacked, width, height) { + this.throwIfDisposed(); + var gl = this.gl; + webgl_util.bindColorTextureToFramebuffer(gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, function () { return gl.viewport(0, 0, width, height); }); + webgl_util.callAndCheck(gl, function () { return gl.scissor(0, 0, width, height); }); + }; + GPGPUContext.prototype.setOutputMatrixWriteRegionDriver = function (x, y, width, height) { + var _this = this; + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, function () { return _this.gl.scissor(x, y, width, height); }); + }; + GPGPUContext.prototype.throwIfDisposed = function () { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + }; + GPGPUContext.prototype.throwIfNoProgram = function () { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + }; + return GPGPUContext; +}()); +exports.GPGPUContext = GPGPUContext; + +},{"./gpgpu_util":37,"./tex_util":55,"./webgl_util":59}],37:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tex_util = require("./tex_util"); +var webgl_util = require("./webgl_util"); +function getWebGLContextAttributes() { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} +exports.getWebGLContextAttributes = getWebGLContextAttributes; +function createWebGLContext(canvas) { + var attributes = getWebGLContextAttributes(); + var gl; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } + else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DEPTH_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.STENCIL_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.BLEND); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.DITHER); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.POLYGON_OFFSET_FILL); }); + webgl_util.callAndCheck(gl, function () { return gl.disable(gl.SAMPLE_COVERAGE); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.SCISSOR_TEST); }); + webgl_util.callAndCheck(gl, function () { return gl.enable(gl.CULL_FACE); }); + webgl_util.callAndCheck(gl, function () { return gl.cullFace(gl.BACK); }); + return gl; +} +exports.createWebGLContext = createWebGLContext; +function createVertexShader(gl) { + var vertexShaderSource = "\n precision highp float;\n attribute vec3 clipSpacePos;\n attribute vec2 uv;\n varying vec2 resultUV;\n\n void main() {\n gl_Position = vec4(clipSpacePos, 1);\n resultUV = uv;\n }"; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} +exports.createVertexShader = createVertexShader; +function createVertexBuffer(gl) { + var vertexArray = new Float32Array([-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} +exports.createVertexBuffer = createVertexBuffer; +function createIndexBuffer(gl) { + var triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} +exports.createIndexBuffer = createIndexBuffer; +function getTextureInternalFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + return gl.RGBA32F; + } + return gl.R32F; + } + return gl.RGBA; +} +function getTextureFormat(gl, numChannels) { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + return gl.RED; + } + return gl.RGBA; +} +function createAndConfigureTexture(gl, width, height, numChannels) { + webgl_util.validateTextureSize(gl, width, height); + var texture = webgl_util.createTexture(gl); + var tex2d = gl.TEXTURE_2D; + var internalFormat = getTextureInternalFormat(gl, numChannels); + var format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(tex2d, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); + return texture; +} +function createMatrixTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createMatrixTexture = createMatrixTexture; +function createColorMatrixTexture(gl, rows, columns) { + var _a = tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createColorMatrixTexture = createColorMatrixTexture; +function createPackedMatrixTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), width = _a[0], height = _a[1]; + var numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} +exports.createPackedMatrixTexture = createPackedMatrixTexture; +function bindVertexProgramAttributeStreams(gl, program, vertexBuffer) { + var posOffset = 0; + var uvOffset = 3 * 4; + var stride = (3 * 4) + (2 * 4); + webgl_util.callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); }); + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute(gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } + catch (e) { + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} +exports.bindVertexProgramAttributeStreams = bindVertexProgramAttributeStreams; +function uploadPixelDataToTexture(gl, texture, pixels) { + var numChannels = 4; + var internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.uploadPixelDataToTexture = uploadPixelDataToTexture; +function uploadDataToTexture(gl, texture, width, height, data, numChannels) { + var textureFormat = getTextureFormat(gl, numChannels); + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); + webgl_util.callAndCheck(gl, function () { return gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, data); }); + webgl_util.callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +function uploadMatrixToTexture(gl, texture, rows, columns, matrix, numChannels) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture); + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} +exports.uploadMatrixToTexture = uploadMatrixToTexture; +function uploadMatrixToPackedTexture(gl, texture, rows, columns, matrix) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + var numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} +exports.uploadMatrixToPackedTexture = uploadMatrixToPackedTexture; +function downloadMatrixFromOutputTexture(gl, rows, columns) { + var _a = tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var channelsPerTexture = 4; + var unpackedArray = new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize(rows * columns, channelsPerTexture)); + var textureFormat = getTextureFormat(gl, channelsPerTexture); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray); }); + var matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture); + return matrix; +} +exports.downloadMatrixFromOutputTexture = downloadMatrixFromOutputTexture; +function downloadMatrixFromPackedOutputTexture(gl, rows, columns) { + var _a = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + var packedRGBA = new Float32Array(tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck(gl, function () { return gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA); }); + var matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} +exports.downloadMatrixFromPackedOutputTexture = downloadMatrixFromPackedOutputTexture; + +},{"./tex_util":55,"./webgl_util":59}],38:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getLogUnaryOp() { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function log(gpgpu, logProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} +exports.log = log; +function uploadLogDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} +exports.uploadLogDownload = uploadLogDownload; + +},{"./unaryop_gpu":58}],39:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float aMax = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n aMax = max(aMax, aCur);\n }\n }\n\n float expSum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n float aCur = texture2D(matrixA, uv).r;\n expSum += exp(aCur - aMax);\n }\n }\n\n gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function logSumExp(gpgpu, logSumExpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.logSumExp = logSumExp; +function uploadLogSumExpDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} +exports.uploadLogSumExpDownload = uploadLogSumExpDownload; + +},{"./gpgpu_context":36}],40:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderMaxPoolBackprop(dyShapeRCD, fSize, origStride, origPad) { + var origInputDepth = dyShapeRCD[2]; + var pad = fSize - 1 - origPad; + var dyRows = dyShapeRCD[0], dyCols = dyShapeRCD[1], depth = dyShapeRCD[2]; + var dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + return "\n precision highp float;\n uniform sampler2D dy;\n uniform sampler2D maxPos;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 dyShapeCR = vec2(" + dyTexShapeRC[1] + ", " + dyTexShapeRC[0] + ");\n\n void main() {\n vec2 dxTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d).\n float dxR = dxTexCR.y;\n float dxC = floor(dxTexCR.x / " + origInputDepth + ".0);\n float d = mod(dxTexCR.x, " + origInputDepth + ".0);\n\n vec2 dyRCCorner = vec2(dxR, dxC) - vec2(" + pad + ".0, " + pad + ".0);\n float dyRCorner = dyRCCorner.x;\n float dyCCorner = dyRCCorner.y;\n\n // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d).\n // ? = to be determined. : = across all values in that axis.\n float dotProd = 0.0;\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n\n float dyR = (dyRCorner + wR) / " + origStride + ".0;\n // TODO(nsthorat): Splice this with another version where you call\n // getMatrixValueOrZeroPad(). Here and below.\n if (dyR < 0.0 || dyR >= " + dyRows + ".0 || fract(dyR) > 0.0) {\n continue;\n }\n\n float dyTexR = dyR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n\n float dyC = (dyCCorner + wC) / " + origStride + ".0;\n if (dyC < 0.0 || dyC >= " + dyCols + ".0 || fract(dyC) > 0.0) {\n continue;\n }\n\n float dyTexC = dyC * " + depth + ".0 + d;\n\n // Read dy(dyR, dyC, d).\n vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR;\n float dyValue = texture2D(dy, dyUV).r;\n\n // Read maxPos(dyR, dyC, d).\n float maxPosValue =\n " + (fSize * fSize - 1) + ".0 - texture2D(maxPos, dyUV).r;\n\n // Get the current value, check it against the value from the\n // position matrix.\n float curPosValue = wR * " + fSize + ".0 + wC;\n float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0);\n\n dotProd += dyValue * mask;\n }\n }\n gl_FragColor = vec4(dotProd, 0, 0, 0);\n }"; +} +exports.getFragmentShaderMaxPoolBackprop = getFragmentShaderMaxPoolBackprop; +function maxPoolBackprop(gpgpu, program, dyTex, maxPositionsTex, resultTex, resultTexShapeRC) { + gpgpu.setOutputMatrixTexture(resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} +exports.maxPoolBackprop = maxPoolBackprop; + +},{"../conv_util":17}],41:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMaxPoolPositionsSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, true); +} +exports.getFragmentShaderMaxPoolPositionsSource = getFragmentShaderMaxPoolPositionsSource; +function getFragmentShaderMaxPoolSource(xShapeRCD, fSize, stride, pad) { + return getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, false); +} +exports.getFragmentShaderMaxPoolSource = getFragmentShaderMaxPoolSource; +function getFragmentShaderMaxPoolCommonSource(xShapeRCD, fSize, stride, pad, computeMaxPositions) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} +function maxPoolCommon(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.maxPoolCommon = maxPoolCommon; + +},{"./pool_gpu":46}],42:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var pool_gpu = require("./pool_gpu"); +function getFragmentShaderMinPoolSource(xShapeRCD, fSize, stride, pad) { + return pool_gpu.getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, 'min', false); +} +exports.getFragmentShaderMinPoolSource = getFragmentShaderMinPoolSource; +function minPool(gpgpu, program, x, result, resultShapeRowCol) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} +exports.minPool = minPool; + +},{"./pool_gpu":46}],43:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderSource(rows, columns, compOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 outputColumnRow;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n float value = texture2D(matrixA, halfCR / aDimCR).r;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 cr = vec2(c, r);\n vec2 uv = (cr + halfCR) / aDimCR;\n float candidate = texture2D(matrixA, uv).r;\n if (isNaN(candidate)) {\n gl_FragColor = vec4(candidate, 0, 0, 0);\n return;\n }\n value = " + compOp + "(value, candidate);\n }\n }\n gl_FragColor = vec4(value, 0, 0, 0);\n }"; +} +function getMinFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'min'); +} +exports.getMinFragmentShaderSource = getMinFragmentShaderSource; +function getMaxFragmentShaderSource(rows, columns) { + return getFragmentShaderSource(rows, columns, 'max'); +} +exports.getMaxFragmentShaderSource = getMaxFragmentShaderSource; +function minMax(gpgpu, minMaxProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.minMax = minMax; + +},{"./webgl_util":59}],44:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var math_1 = require("../math"); +var shader_compiler = require("./shader_compiler"); +function getFragmentShader(a, b, out, aOrientation, bOrientation) { + var sharedDim = (aOrientation === math_1.MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + var aSnippet = (aOrientation === math_1.MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + var bSnippet = (bOrientation === math_1.MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + var inputs = [{ name: 'matrixA', array: a }, { name: 'matrixB', array: b }]; + var userCode = "\n const float sharedDim = " + sharedDim + ".0;\n\n float dotARowBCol(float aRow, float bCol) {\n float result = 0.0;\n for (float i = 0.0; i < sharedDim; i += 1.0) {\n float a = getMatrixA(" + aSnippet + ");\n float b = getMatrixB(" + bSnippet + ");\n result += (a * b);\n }\n return result;\n }\n\n void main() {\n vec2 resRC = getOutputCoords();\n setOutput(dotARowBCol(resRC.x, resRC.y));\n }\n "; + return shader_compiler.makeShader(inputs, out, userCode); +} +exports.getFragmentShader = getFragmentShader; +function multiplyMatrix(gpgpu, multiplyProgram, a, b, result, outTexShape) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} +exports.multiplyMatrix = multiplyMatrix; + +},{"../math":20,"./shader_compiler":52}],45:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getNegUnaryOp() { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function neg(gpgpu, program, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} +exports.neg = neg; +function uploadNegDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} +exports.uploadNegDownload = uploadNegDownload; + +},{"./unaryop_gpu":58}],46:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +var webgl_util_1 = require("./webgl_util"); +function getFragmentShaderPoolCommonSource(xShapeRCD, fSize, stride, pad, poolType, computePositions) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + var depth = xShapeRCD[2]; + var xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + var returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } + else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + return "\n precision highp float;\n uniform sampler2D x;\n varying vec2 resultUV;\n\n const vec2 halfCR = vec2(0.5, 0.5);\n const vec2 xShapeCR = vec2(" + xTexShapeRC[1] + ", " + xTexShapeRC[0] + ");\n\n " + webgl_util_1.IS_NAN_SHADER_FUNC + "\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2).\n float yR = yTexCR.y;\n float yC = floor(yTexCR.x / " + depth + ".0);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n vec2 xRCCorner = vec2(yR, yC) * vec2(" + stride + ", " + stride + ") -\n vec2(" + pad + ".0, " + pad + ".0);\n float xRCorner = xRCCorner.x;\n float xCCorner = xRCCorner.y;\n\n // max/min x(?, ?, d) to get y(yR, yC, d).\n // ? = to be determined\n float minMaxValue = 0.0;\n float minMaxValueFound = 0.0;\n float minMaxPosition = 0.0;\n float avgValue = 0.0;\n\n for (float wR = 0.0; wR < " + fSize + ".0; wR += 1.0) {\n float xR = xRCorner + wR;\n float xTexR = xR;\n\n for (float wC = 0.0; wC < " + fSize + ".0; wC += 1.0) {\n float xC = xCCorner + wC;\n float xTexC = xC * " + depth + ".0 + d;\n\n vec2 texCR = vec2(xTexC, xTexR);\n\n // Check if the requested UV is invalid.\n vec2 uv = (texCR + halfCR) / xShapeCR;\n bool lessThanZero = any(lessThan(uv, vec2(0, 0)));\n bool greaterThanOne = any(greaterThan(uv, vec2(1, 1)));\n bool outside = lessThanZero || greaterThanOne;\n if (outside) {\n continue;\n }\n\n float value = texture2D(x, uv).r;\n if (isNaN(value)) {\n gl_FragColor = vec4(value, 0, 0, 0);\n return;\n }\n if (" + (poolType === 'avg') + ") {\n avgValue += value / " + fSize * fSize + ".0;\n } else {\n // If a min / max value has already been found, use it. If not, use\n // the current value.\n float currentMinMaxValue = mix(\n value, minMaxValue, minMaxValueFound);\n if (value " + (poolType === 'min' ? '<=' : '>=') + " currentMinMaxValue) {\n minMaxValue = value;\n minMaxValueFound = 1.0;\n if (" + computePositions + ") {\n minMaxPosition = wR * " + fSize + ".0 + wC;\n }\n }\n }\n }\n }\n gl_FragColor = vec4(" + returnValue + ", 0, 0, 0);\n }"; +} +exports.getFragmentShaderPoolCommonSource = getFragmentShaderPoolCommonSource; +function poolCommon(gpgpu, program, x, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} +exports.poolCommon = poolCommon; + +},{"../conv_util":17,"./webgl_util":59}],47:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(rows, columns) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n const vec2 aDimCR = vec2(" + columns + ".0, " + rows + ".0);\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n float sum = 0.0;\n for (float r = 0.0; r < aDimCR.y; r += 1.0) {\n for (float c = 0.0; c < aDimCR.x; c += 1.0) {\n vec2 uv = (vec2(c, r) + halfCR) / aDimCR;\n sum += texture2D(matrixA, uv).r;\n }\n }\n gl_FragColor = vec4(sum, 0, 0, 0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reduceSum(gpgpu, reduceSumProgram, a, aNumRows, aNumCols, result) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.reduceSum = reduceSum; +function uploadReduceSumDownload(a, rows, columns) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadReduceSumDownload = uploadReduceSumDownload; + +},{"./gpgpu_context":36}],48:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getReluUnaryOp() { + return "\n float result = (value < 0.0 ? 0.0 : value);\n gl_FragColor = vec4(result, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function relu(gpgpu, reluProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} +exports.relu = relu; +function uploadReluDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} +exports.uploadReluDownload = uploadReluDownload; + +},{"./unaryop_gpu":58}],49:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var webgl_util = require("./webgl_util"); +function getRenderRGBShader(gpgpu, destinationWidth) { + var fragmentShaderSource = "\n precision highp float;\n uniform sampler2D source;\n varying vec2 resultUV;\n\n const float destinationWidth = " + destinationWidth + ".0;\n const float a = 1.0;\n\n void main() {\n float xr = floor(resultUV.s * destinationWidth) * 3.0;\n vec3 x = xr + vec3(0, 1, 2);\n\n float sourceWidth = destinationWidth * 3.0;\n vec3 u = (x + 0.5) / sourceWidth;\n float v = 1.0 - resultUV.t;\n\n float r = texture2D(source, vec2(u[0], v)).r;\n float g = texture2D(source, vec2(u[1], v)).r;\n float b = texture2D(source, vec2(u[2], v)).r;\n\n gl_FragColor = vec4(r, g, b, a);\n }"; + return gpgpu.createProgram(fragmentShaderSource); +} +exports.getRenderRGBShader = getRenderRGBShader; +function renderToCanvas(gpgpu, renderShader, sourceTex) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} +exports.renderToCanvas = renderToCanvas; +function renderToFramebuffer(gpgpu, renderShader, sourceTex) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} +exports.renderToFramebuffer = renderToFramebuffer; + +},{"./webgl_util":59}],50:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function getFragmentShaderSource() { + return "\n precision highp float;\n uniform sampler2D matrixA;\n uniform vec2 inputDimCR;\n uniform vec2 resultDimCR;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void main() {\n vec2 resultCR = floor(resultUV * resultDimCR);\n // indexInFlat = row * stride + column, where stride == numOutputColumns\n float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x;\n\n vec2 inputCR = vec2(\n mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns\n floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns\n ) + halfCR;\n\n vec2 inputUV = inputCR / inputDimCR;\n gl_FragColor = texture2D(matrixA, inputUV);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function reshape(gpgpu, reshapeProgram, a, aNumRows, aNumCols, result, resultNumRows, resultNumCols) { + var inputSize = aNumRows * aNumCols; + var outputSize = resultNumCols * resultNumRows; + util.assert(inputSize === outputSize, "The input size (" + inputSize + ") and output size (" + outputSize + ") " + + "must match"); + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + var inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + var resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + gpgpu.executeProgram(); +} +exports.reshape = reshape; + +},{"../../util":87}],51:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../conv_util"); +function getFragmentShaderSource(inputShapeRCD, outputDimensionsRowCol, alignCorners) { + var depth = inputShapeRCD[2]; + var inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + var effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + var effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n const vec2 inputShapeCR = vec2(" + inputShapeRCD[1] + ", " + inputShapeRCD[0] + ");\n const vec2 inputShapeTexCR = vec2(\n " + inputTexShapeRC[1] + ", " + inputTexShapeRC[0] + ");\n\n const vec2 effectiveInputOverOutputRatioCR = vec2(\n " + effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1] + ",\n " + effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0] + ");\n\n float sampleInput(float col, float row, float d) {\n vec2 uv = (vec2(col * " + depth + ".0 + d, row) + halfCR) / inputShapeTexCR;\n return texture2D(matrixA, uv).r;\n }\n\n void main() {\n vec2 yTexCR = floor(gl_FragCoord.xy);\n\n // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d).\n vec2 yCR = vec2(floor(yTexCR.x / " + depth + ".0), yTexCR.y);\n float d = mod(yTexCR.x, " + depth + ".0);\n\n // Fractional source index.\n vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR;\n\n // Compute the four integer indices.\n vec2 sourceFloorCR = floor(sourceFracIndexCR);\n vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR));\n\n float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d);\n float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d);\n float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d);\n float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d);\n\n vec2 fracCR = sourceFracIndexCR - sourceFloorCR;\n\n float top = topLeft + (topRight - topLeft) * fracCR[0];\n float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0];\n float newValue = top + (bottom - top) * fracCR[1];\n\n gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0);\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function resizeBilinear(gpgpu, resizeBilinearProgram, a, result, resultShapeRowCol) { + gpgpu.setOutputMatrixTexture(result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.resizeBilinear = resizeBilinear; + +},{"../conv_util":17}],52:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../../util"); +function makeShaderKey(inputs, output) { + var ins = inputs.map(function (x) { return x.shape + '_' + x.getTextureShapeRC(); }); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} +exports.makeShaderKey = makeShaderKey; +function makeShader(inputs, output, userCode) { + var inputPrefixSnippet = inputs.map(function (x) { return "uniform sampler2D " + x.name + ";"; }).join('\n'); + var inputSamplingSnippet = inputs.map(function (x) { return getInputSamplingSnippet(x); }).join('\n'); + var outTexShape = output.getTextureShapeRC(); + var outputSamplingSnippet = getOutputSamplingSnippet(output.shape, outTexShape); + var source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} +exports.makeShader = makeShader; +function getInputSamplingSnippet(input) { + var arr = input.array; + var shape = arr.shape; + var texShape = arr.getTextureShapeRC(shape); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape, texShape); + default: + throw new Error(arr.rank + "-D input sampling is not yet supported"); + } +} +function getOutputSamplingSnippet(outShape, outTexShape) { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape, outTexShape); + default: + throw new Error(outShape.length + "-D output sampling is not yet supported"); + } +} +var SHADER_PREFIX = "\n precision highp float;\n varying vec2 resultUV;\n const vec2 halfCR = vec2(0.5, 0.5);\n\n void setOutput(float val) {\n gl_FragColor = vec4(val, 0, 0, 0);\n }\n"; +var SAMPLE_2D_SNIPPET = "\n float sample2D(sampler2D texture, float texNumR, float texNumC, float numC,\n float row, float col) {\n float index = dot(vec2(row, col), vec2(numC, 1.0));\n float texR = floor(index / texNumC);\n float texC = mod(index, texNumC);\n vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR);\n return texture2D(texture, uv).r;\n }\n"; +function getOutput2DCoords(shape, texShape) { + if (util.arraysEqual(shape, texShape)) { + return "\n vec2 getOutputCoords() {\n return floor(gl_FragCoord.yx);\n }\n "; + } + return "\n vec2 getOutputCoords() {\n vec2 resTexRC = floor(gl_FragCoord.yx);\n float index = dot(resTexRC, vec2(" + texShape[1] + ".0, 1.0));\n float r = floor(index / " + shape[1] + ".0);\n float c = mod(index, " + shape[1] + ".0);\n return vec2(r, c);\n }\n "; +} +function getSampler2D(texName, shape, texShape) { + var funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + var tR = texShape[0]; + var tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return "\n float " + funcName + "(float row, float col) {\n vec2 uv = (vec2(col, row) + halfCR) / vec2(" + tC + ".0, " + tR + ".0);\n return texture2D(" + texName + ", uv).r;\n }\n "; + } + return "\n float " + funcName + "(float row, float col) {\n return sample2D(" + texName + ", " + tR + ".0, " + tC + ".0, " + shape[1] + ".0, row, col);\n }\n "; +} + +},{"../../util":87}],53:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSigmoidUnaryOp() { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} +function getSigmoidFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} +exports.getSigmoidFragmentShaderSource = getSigmoidFragmentShaderSource; +function sigmoid(gpgpu, sigmoidProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} +exports.sigmoid = sigmoid; +function uploadSigmoidDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSigmoidUnaryOp()); +} +exports.uploadSigmoidDownload = uploadSigmoidDownload; + +},{"./unaryop_gpu":58}],54:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getStepUnaryOp() { + return "\n float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value;\n gl_FragColor = vec4(res, 0, 0, 0);\n "; +} +function getFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function step(gpgpu, stepProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} +exports.step = step; +function uploadStepDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} +exports.uploadStepDownload = uploadStepDownload; + +},{"./unaryop_gpu":58}],55:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function getUnpackedMatrixTextureShapeWidthHeight(rows, columns) { + return [columns, rows]; +} +exports.getUnpackedMatrixTextureShapeWidthHeight = getUnpackedMatrixTextureShapeWidthHeight; +function getUnpackedArraySizeFromMatrixSize(matrixSize, channelsPerTexture) { + return matrixSize * channelsPerTexture; +} +exports.getUnpackedArraySizeFromMatrixSize = getUnpackedArraySizeFromMatrixSize; +function getColorMatrixTextureShapeWidthHeight(rows, columns) { + return [columns * 4, rows]; +} +exports.getColorMatrixTextureShapeWidthHeight = getColorMatrixTextureShapeWidthHeight; +function getMatrixSizeFromUnpackedArraySize(unpackedSize, channelsPerTexture) { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error('unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} +exports.getMatrixSizeFromUnpackedArraySize = getMatrixSizeFromUnpackedArraySize; +function encodeMatrixToUnpackedArray(matrix, unpackedArray, channelsPerTexture) { + var requiredSize = getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error('unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} +exports.encodeMatrixToUnpackedArray = encodeMatrixToUnpackedArray; +function decodeMatrixFromUnpackedArray(unpackedArray, matrix, channelsPerTexture) { + var requiredSize = getMatrixSizeFromUnpackedArraySize(unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var dst = 0; + for (var src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} +exports.decodeMatrixFromUnpackedArray = decodeMatrixFromUnpackedArray; +function getPackedMatrixTextureShapeWidthHeight(rows, columns) { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} +exports.getPackedMatrixTextureShapeWidthHeight = getPackedMatrixTextureShapeWidthHeight; +function getPackedRGBAArraySizeFromMatrixShape(rows, columns) { + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), w = _a[0], h = _a[1]; + return w * h * 4; +} +exports.getPackedRGBAArraySizeFromMatrixShape = getPackedRGBAArraySizeFromMatrixShape; +function encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA) { + var requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error('packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + { + var dstStride = (oddWidth ? 4 : 0); + var oneRow = columns; + var dst = 0; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + var matrixSrcRow = (blockY * 2 * columns); + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + var matrixSrcCol = blockX * 2; + var src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + if (oddWidth) { + var src = columns - 1; + var dst = (textureWidth - 1) * 4; + var srcStride = 2 * columns; + var dstStride = textureWidth * 4; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (rows - 1) * columns; + var dst = (textureHeight - 1) * textureWidth * 4; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + return packedRGBA; +} +exports.encodeMatrixToPackedRGBA = encodeMatrixToPackedRGBA; +function decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix) { + var requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error('matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + var oddWidth = (columns % 2) === 1; + var oddHeight = (rows % 2) === 1; + var widthInFullBlocks = Math.floor(columns / 2); + var heightInFullBlocks = Math.floor(rows / 2); + var _a = getPackedMatrixTextureShapeWidthHeight(rows, columns), textureWidth = _a[0], textureHeight = _a[1]; + { + var srcStride = oddWidth ? 4 : 0; + var dstStride = columns + (oddWidth ? 1 : 0); + var src = 0; + var dstRow1 = 0; + var dstRow2 = columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + if (oddWidth) { + var src = (textureWidth - 1) * 4; + var dst = columns - 1; + var srcStride = textureWidth * 4; + var dstStride = 2 * columns; + for (var blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + if (oddHeight) { + var src = (textureHeight - 1) * textureWidth * 4; + var dst = (rows - 1) * columns; + for (var blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + return matrix; +} +exports.decodeMatrixFromPackedRGBA = decodeMatrixFromPackedRGBA; + +},{}],56:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextureManager = (function () { + function TextureManager(gpgpu) { + this.gpgpu = gpgpu; + this.numUsedTextures = 0; + this.numFreeTextures = 0; + this.freeTextures = {}; + this.logEnabled = false; + this.usedTextureCount = {}; + } + TextureManager.prototype.acquireTexture = function (shapeRC) { + var shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift(); + } + this.numUsedTextures++; + this.log(); + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + }; + TextureManager.prototype.releaseTexture = function (texture, shape) { + var shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + }; + TextureManager.prototype.log = function () { + if (!this.logEnabled) { + return; + } + var total = this.numFreeTextures + this.numUsedTextures; + console.log('Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, "(" + total + ")"); + }; + TextureManager.prototype.getNumUsedTextures = function () { + return this.numUsedTextures; + }; + TextureManager.prototype.getNumFreeTextures = function () { + return this.numFreeTextures; + }; + TextureManager.prototype.dispose = function () { + for (var shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (var i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + }; + return TextureManager; +}()); +exports.TextureManager = TextureManager; +function getKeyFromTextureShape(shapeRowsCol) { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} + +},{}],57:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var unaryop_gpu = require("./unaryop_gpu"); +function getSinUnaryOp() { + return "\n gl_FragColor = vec4(sin(value), 0, 0, 0);\n "; +} +function getSinFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} +exports.getSinFragmentShaderSource = getSinFragmentShaderSource; +function sin(gpgpu, sinProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} +exports.sin = sin; +function uploadSinDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} +exports.uploadSinDownload = uploadSinDownload; +function getTanhUnaryOp() { + return "\n float e2x = exp(-2.0 * value);\n gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0);\n "; +} +function getTanhFragmentShaderSource() { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} +exports.getTanhFragmentShaderSource = getTanhFragmentShaderSource; +function tanh(gpgpu, tanhProgram, a, rows, columns, result) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} +exports.tanh = tanh; +function uploadTanhDownload(a, rows, columns) { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} +exports.uploadTanhDownload = uploadTanhDownload; + +},{"./unaryop_gpu":58}],58:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var gpgpu_context_1 = require("./gpgpu_context"); +function getFragmentShaderSource(resultOp) { + return "\n precision highp float;\n uniform sampler2D matrixA;\n varying vec2 resultUV;\n\n void main() {\n float value = texture2D(matrixA, resultUV).r;\n " + resultOp + "\n }"; +} +exports.getFragmentShaderSource = getFragmentShaderSource; +function unaryOp(gpgpu, unaryOpProgram, a, rows, columns, result) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} +exports.unaryOp = unaryOp; +function uploadUnaryOpDownload(a, rows, columns, resultOp) { + var gpgpu = new gpgpu_context_1.GPGPUContext(); + var fragmentShaderSrc = getFragmentShaderSource(resultOp); + var program = gpgpu.createProgram(fragmentShaderSrc); + var aTexture = gpgpu.createMatrixTexture(rows, columns); + var resultTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + var result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} +exports.uploadUnaryOpDownload = uploadUnaryOpDownload; + +},{"./gpgpu_context":36}],59:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var USE_WEBGL2_WHEN_AVAILABLE = false; +var WEBGL2_ENABLED = null; +var MAX_TEXTURE_SIZE = null; +var util = require("../../util"); +exports.IS_NAN_SHADER_FUNC = "\nbool isNaN(float val) {\n return val == val ? false : true;\n}\n"; +function createWebGLRenderingContext(attributes) { + var canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} +exports.createWebGLRenderingContext = createWebGLRenderingContext; +function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL1 = preferWebGL1; +function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} +exports.preferWebGL2 = preferWebGL2; +function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + if (WEBGL2_ENABLED === undefined) { + var tempCanvas = document.createElement('canvas'); + var gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + var loseContextExtension = getExtensionOrThrow(gl, 'WEBGL_lose_context'); + loseContextExtension.loseContext(); + } + else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} +exports.isWebGL2Enabled = isWebGL2Enabled; +function createWebGLRenderingContextFromCanvas(canvas, attributes) { + var gl; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes); + } + else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)); + } + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} +exports.createWebGLRenderingContextFromCanvas = createWebGLRenderingContextFromCanvas; +function callAndCheck(gl, func) { + var returnValue = func(); + checkWebGLError(gl); + return returnValue; +} +exports.callAndCheck = callAndCheck; +var webGLDebugErrorCheckingEnabled = false; +function enableDebugWebGLErrorChecking(enabled) { + webGLDebugErrorCheckingEnabled = enabled; +} +exports.enableDebugWebGLErrorChecking = enableDebugWebGLErrorChecking; +function checkWebGLError(gl) { + if (webGLDebugErrorCheckingEnabled) { + var error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} +exports.checkWebGLError = checkWebGLError; +function getWebGLErrorMessage(gl, status) { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} +exports.getWebGLErrorMessage = getWebGLErrorMessage; +function getExtensionOrThrow(gl, extensionName) { + return throwIfNull(gl, function () { return gl.getExtension(extensionName); }, 'Extension "' + extensionName + '" not supported on this browser.'); +} +exports.getExtensionOrThrow = getExtensionOrThrow; +function createVertexShader(gl, vertexShaderSource) { + var vertexShader = throwIfNull(gl, function () { return gl.createShader(gl.VERTEX_SHADER); }, 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(vertexShader, vertexShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(vertexShader); }); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} +exports.createVertexShader = createVertexShader; +function createFragmentShader(gl, fragmentShaderSource) { + var fragmentShader = throwIfNull(gl, function () { return gl.createShader(gl.FRAGMENT_SHADER); }, 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, function () { return gl.shaderSource(fragmentShader, fragmentShaderSource); }); + callAndCheck(gl, function () { return gl.compileShader(fragmentShader); }); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} +exports.createFragmentShader = createFragmentShader; +function createProgram(gl) { + return throwIfNull(gl, function () { return gl.createProgram(); }, 'Unable to create WebGLProgram.'); +} +exports.createProgram = createProgram; +function linkProgram(gl, program) { + callAndCheck(gl, function () { return gl.linkProgram(program); }); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} +exports.linkProgram = linkProgram; +function validateProgram(gl, program) { + callAndCheck(gl, function () { return gl.validateProgram(program); }); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} +exports.validateProgram = validateProgram; +function createStaticVertexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticVertexBuffer = createStaticVertexBuffer; +function createStaticIndexBuffer(gl, data) { + var buffer = throwIfNull(gl, function () { return gl.createBuffer(); }, 'Unable to create WebGLBuffer'); + callAndCheck(gl, function () { return gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW); }); + return buffer; +} +exports.createStaticIndexBuffer = createStaticIndexBuffer; +function queryMaxTextureSize(gl) { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, function () { return gl.getParameter(gl.MAX_TEXTURE_SIZE); }); + return MAX_TEXTURE_SIZE; +} +exports.queryMaxTextureSize = queryMaxTextureSize; +function getChannelsPerTexture() { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} +exports.getChannelsPerTexture = getChannelsPerTexture; +function createTexture(gl) { + return throwIfNull(gl, function () { return gl.createTexture(); }, 'Unable to create WebGLTexture.'); +} +exports.createTexture = createTexture; +function validateTextureSize(gl, width, height) { + var maxTextureSize = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + var requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + var requested = '[' + width + 'x' + height + ']'; + var max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error('Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} +exports.validateTextureSize = validateTextureSize; +function createFramebuffer(gl) { + return throwIfNull(gl, function () { return gl.createFramebuffer(); }, 'Unable to create WebGLFramebuffer.'); +} +exports.createFramebuffer = createFramebuffer; +function bindVertexBufferToProgramAttribute(gl, program, attribute, buffer, arrayEntriesPerItem, itemStrideInBytes, itemOffsetInBytes) { + var loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + var error = new Error('Unable to get attribute "' + attribute + '" on WebGLProgram.'); + error.namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, function () { return gl.bindBuffer(gl.ARRAY_BUFFER, buffer); }); + callAndCheck(gl, function () { return gl.vertexAttribPointer(loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, itemOffsetInBytes); }); + callAndCheck(gl, function () { return gl.enableVertexAttribArray(loc); }); +} +exports.bindVertexBufferToProgramAttribute = bindVertexBufferToProgramAttribute; +function bindTextureUnit(gl, texture, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, texture); }); +} +exports.bindTextureUnit = bindTextureUnit; +function unbindTextureUnit(gl, textureUnit) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, function () { return gl.activeTexture(gl.TEXTURE0 + textureUnit); }); + callAndCheck(gl, function () { return gl.bindTexture(gl.TEXTURE_2D, null); }); +} +exports.unbindTextureUnit = unbindTextureUnit; +function getProgramUniformLocationOrThrow(gl, program, uniformName) { + return throwIfNull(gl, function () { return gl.getUniformLocation(program, uniformName); }, 'uniform "' + uniformName + '" not present in program.'); +} +exports.getProgramUniformLocationOrThrow = getProgramUniformLocationOrThrow; +function bindTextureToProgramUniformSampler(gl, program, texture, uniformSamplerName, textureUnit) { + callAndCheck(gl, function () { return bindTextureUnit(gl, texture, textureUnit); }); + var samplerLocation = getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, function () { return gl.uniform1i(samplerLocation, textureUnit); }); +} +exports.bindTextureToProgramUniformSampler = bindTextureToProgramUniformSampler; +function bindCanvasToFramebuffer(gl) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, null); }); + callAndCheck(gl, function () { return gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); }); + callAndCheck(gl, function () { return gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); }); +} +exports.bindCanvasToFramebuffer = bindCanvasToFramebuffer; +function bindColorTextureToFramebuffer(gl, texture, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); }); +} +exports.bindColorTextureToFramebuffer = bindColorTextureToFramebuffer; +function unbindColorTextureFromFramebuffer(gl, framebuffer) { + callAndCheck(gl, function () { return gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); }); + callAndCheck(gl, function () { return gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); }); +} +exports.unbindColorTextureFromFramebuffer = unbindColorTextureFromFramebuffer; +function validateFramebuffer(gl) { + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error('Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} +exports.validateFramebuffer = validateFramebuffer; +function getFramebufferErrorMessage(gl, status) { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} +exports.getFramebufferErrorMessage = getFramebufferErrorMessage; +function throwIfNull(gl, returnTOrNull, failureMessage) { + var tOrNull = callAndCheck(gl, function () { return returnTOrNull(); }); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull; +} +function validateTextureUnit(gl, textureUnit) { + var maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + var glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + var textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} +function getTextureShapeFromLogicalShape(gl, logicalShape, preferredTexShape) { + var maxTexSize = queryMaxTextureSize(gl); + var size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + var sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert(size === sizePreferred, "Size of shape (" + size + ") must match size of " + + ("preferredShape (" + sizePreferred + ")")); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } + else if (logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape; + } + else if (logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } + else { + return util.sizeToSquarishShape(size); + } +} +exports.getTextureShapeFromLogicalShape = getTextureShapeFromLogicalShape; + +},{"../../util":87}],60:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var add_1 = require("./ops/add"); +var argmax_1 = require("./ops/argmax"); +var argmaxequals_1 = require("./ops/argmaxequals"); +var concat3d_1 = require("./ops/concat3d"); +var convolution_1 = require("./ops/convolution"); +var divide_1 = require("./ops/divide"); +var element_wise_activation_1 = require("./ops/element_wise_activation"); +var element_wise_cost_1 = require("./ops/element_wise_cost"); +var exp_1 = require("./ops/exp"); +var linear_combination_1 = require("./ops/linear_combination"); +var log_1 = require("./ops/log"); +var matmul_1 = require("./ops/matmul"); +var max_pool_1 = require("./ops/max_pool"); +var multiply_1 = require("./ops/multiply"); +var reduce_sum_1 = require("./ops/reduce_sum"); +var reshape_1 = require("./ops/reshape"); +var softmax_1 = require("./ops/softmax"); +var split_1 = require("./ops/split"); +var subtract_1 = require("./ops/subtract"); +function emitFromGraphNodes(nodes) { + var ops = []; + nodes.forEach(function (node) { return Array.prototype.push.apply(ops, emitOpFromNode(node)); }); + return ops; +} +exports.emitFromGraphNodes = emitFromGraphNodes; +function emitOpFromNode(node) { + if (node instanceof graph_1.ReshapeNode) { + return [new reshape_1.Reshape(node.inputs[graph_1.ReshapeNode.X], node.output)]; + } + else if (node instanceof graph_1.MatMulNode) { + var x1 = node.inputs[graph_1.MatMulNode.X1]; + var x2 = node.inputs[graph_1.MatMulNode.X2]; + return [new matmul_1.MatMul(x1, x2, node.output)]; + } + else if (node instanceof graph_1.Convolution2DNode) { + var w = node.inputs[graph_1.Convolution2DNode.W]; + var x = node.inputs[graph_1.Convolution2DNode.X]; + var b = node.inputs[graph_1.Convolution2DNode.B]; + return [new convolution_1.Convolution2D(w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.MaxPoolNode) { + var x = node.inputs[graph_1.MaxPoolNode.X]; + return [new max_pool_1.MaxPool(x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } + else if (node instanceof graph_1.ExpNode) { + return [new exp_1.Exp(node.inputs[graph_1.ExpNode.X], node.output)]; + } + else if (node instanceof graph_1.LogNode) { + return [new log_1.Log(node.inputs[graph_1.LogNode.X], node.output)]; + } + else if (node instanceof graph_1.ReLUNode) { + return [new element_wise_activation_1.ReLU(node.inputs[graph_1.ReLUNode.X], node.output)]; + } + else if (node instanceof graph_1.TanHNode) { + return [new element_wise_activation_1.TanH(node.inputs[graph_1.TanHNode.X], node.output)]; + } + else if (node instanceof graph_1.SigmoidNode) { + return [new element_wise_activation_1.Sigmoid(node.inputs[graph_1.SigmoidNode.X], node.output)]; + } + else if (node instanceof graph_1.SoftmaxCrossEntropyCostNode) { + var x = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.X]; + var target = node.inputs[graph_1.SoftmaxCrossEntropyCostNode.TARGET]; + return [new softmax_1.SoftmaxCrossEntropyCost(x, target, node.output)]; + } + else if (node instanceof graph_1.SoftmaxNode) { + return [new softmax_1.Softmax(node.inputs[graph_1.SoftmaxNode.X], node.output)]; + } + else if (node instanceof graph_1.MeanSquaredCostNode) { + var label = node.inputs[graph_1.MeanSquaredCostNode.LABEL]; + var prediction = node.inputs[graph_1.MeanSquaredCostNode.PREDICTION]; + return [new element_wise_cost_1.MeanSquaredCost(label, prediction, node.output)]; + } + else if (node instanceof graph_1.ArgMaxEqualsNode) { + return [new argmaxequals_1.ArgMaxEquals(node.inputs[graph_1.ArgMaxEqualsNode.X1], node.inputs[graph_1.ArgMaxEqualsNode.X2], node.output)]; + } + else if (node instanceof graph_1.ArgMaxNode) { + return [new argmax_1.ArgMax(node.x, node.output)]; + } + else if (node instanceof graph_1.FusedLinearCombinationNode) { + return [new linear_combination_1.LinearCombination(node.inputs[graph_1.FusedLinearCombinationNode.T1], node.inputs[graph_1.FusedLinearCombinationNode.T2], node.inputs[graph_1.FusedLinearCombinationNode.C1], node.inputs[graph_1.FusedLinearCombinationNode.C2], node.output)]; + } + else if (node instanceof graph_1.Concat3DNode) { + return [new concat3d_1.Concat3D(node.inputs[graph_1.Concat3DNode.X1], node.inputs[graph_1.Concat3DNode.X2], node.axis, node.output)]; + } + else if (node instanceof graph_1.SquareNode) { + return [new element_wise_activation_1.Square(node.inputs[graph_1.SquareNode.X], node.output)]; + } + else if (node instanceof graph_1.AddNode) { + return [new add_1.Add(node.inputs[graph_1.AddNode.T1], node.inputs[graph_1.AddNode.T2], node.output)]; + } + else if (node instanceof graph_1.SubtractNode) { + return [new subtract_1.Subtract(node.inputs[graph_1.SubtractNode.T1], node.inputs[graph_1.SubtractNode.T2], node.output)]; + } + else if (node instanceof graph_1.MultiplyNode) { + return [new multiply_1.Multiply(node.inputs[graph_1.MultiplyNode.T1], node.inputs[graph_1.MultiplyNode.T2], node.output)]; + } + else if (node instanceof graph_1.DivideNode) { + return [new divide_1.Divide(node.inputs[graph_1.DivideNode.T1], node.inputs[graph_1.DivideNode.T2], node.output)]; + } + else if (node instanceof graph_1.SplitNode) { + return [new split_1.Split(node.inputs[graph_1.SplitNode.X], node.outputs)]; + } + else if (node instanceof graph_1.ReduceSumNode) { + return [new reduce_sum_1.ReduceSum(node.inputs[graph_1.ReduceSumNode.X], node.output)]; + } + else if (graph_util.isInputNode(node)) { + return []; + } + else { + throw Error('Unsupported node type: ' + node.constructor.name); + } +} + +},{"./graph":8,"./graph_util":11,"./ops/add":61,"./ops/argmax":62,"./ops/argmaxequals":63,"./ops/concat3d":64,"./ops/convolution":65,"./ops/divide":66,"./ops/element_wise_activation":67,"./ops/element_wise_cost":68,"./ops/exp":69,"./ops/linear_combination":70,"./ops/log":71,"./ops/matmul":72,"./ops/max_pool":73,"./ops/multiply":74,"./ops/reduce_sum":76,"./ops/reshape":77,"./ops/softmax":78,"./ops/split":79,"./ops/subtract":80}],61:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Add = (function (_super) { + __extends(Add, _super); + function Add(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Add.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } + else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } + else { + result = math.add(x1, x2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Add.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x1Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x1Tensor, dy); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.x2Tensor, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.x2Tensor, dy); + } + } + }); + }; + Add.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Add; +}(op_1.Operation)); +exports.Add = Add; + +},{"../graph_util":11,"../math/ndarray":23,"../util":87,"./op":75}],62:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMax = (function (_super) { + __extends(ArgMax, _super); + function ArgMax(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + ArgMax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMax(x))); + }); + }; + ArgMax.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMax backprop unimplemented'); + }; + return ArgMax; +}(op_1.Operation)); +exports.ArgMax = ArgMax; + +},{"./op":75}],63:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var op_1 = require("./op"); +var ArgMaxEquals = (function (_super) { + __extends(ArgMaxEquals, _super); + function ArgMaxEquals(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + ArgMaxEquals.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + }; + ArgMaxEquals.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('ArgMaxEquals backprop unimplemented'); + }; + return ArgMaxEquals; +}(op_1.Operation)); +exports.ArgMaxEquals = ArgMaxEquals; + +},{"./op":75}],64:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var concat3d_util = require("../math/concat3d_util"); +var op_1 = require("./op"); +var Concat3D = (function (_super) { + __extends(Concat3D, _super); + function Concat3D(x1Tensor, x2Tensor, axis, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.axis = axis; + _this.yTensor = yTensor; + concat3d_util.assertConcat3DShapesMatch(x1Tensor.shape, x2Tensor.shape, axis); + return _this; + } + Concat3D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var concatResult = math.concat3D(x1, x2, _this.axis); + inferenceArrays.set(_this.yTensor, keep(concatResult)); + }); + }; + Concat3D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + throw new Error('Concat3D backprop not implemented.'); + }; + return Concat3D; +}(op_1.Operation)); +exports.Concat3D = Concat3D; + +},{"../math/concat3d_util":16,"./op":75}],65:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Convolution2D = (function (_super) { + __extends(Convolution2D, _super); + function Convolution2D(wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, zeroPad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.wTensor = wTensor; + _this.xTensor = xTensor; + _this.bTensor = bTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.outputDepth = outputDepth; + _this.stride = stride; + _this.assertWeightsShape(wTensor.shape); + _this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(_this.xTensor.shape, _this.fieldSize, _this.stride); + util.assert(util.isInt(_this.zeroPad), "The zero padding (" + _this.zeroPad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + Convolution2D.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var biases = inferenceArrays.get(this.bTensor); + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.conv2d(x, weights, biases, _this.stride, _this.zeroPad))); + }); + }; + Convolution2D.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var weights = inferenceArrays.get(this.wTensor); + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var _a = math.conv2dBackProp(x, dy, weights, _this.stride, _this.zeroPad), dw = _a.dw, db = _a.db, dx = _a.dx; + gradientArrays.set(_this.wTensor, keep(dw)); + gradientArrays.set(_this.bTensor, keep(db)); + gradientArrays.set(_this.xTensor, keep(dx)); + }); + }; + Convolution2D.prototype.assertWeightsShape = function (weightsShape) { + util.assert(weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, "weights must be of shape [" + this.fieldSize + "," + this.fieldSize + "," + + (this.xTensor.shape[2] + "," + this.outputDepth + "] but they are of") + + ("shape [" + weightsShape + "]")); + }; + return Convolution2D; +}(op_1.Operation)); +exports.Convolution2D = Convolution2D; + +},{"../math/conv_util":17,"../util":87,"./op":75}],66:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Divide = (function (_super) { + __extends(Divide, _super); + function Divide(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Divide.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } + else { + result = math.divide(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Divide.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + var x1IsScalar = util.isScalarShape(x1.shape); + var x2IsScalar = util.isScalarShape(x2.shape); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (x1IsScalar) { + var div = math.divide(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(div))); + div.dispose(); + } + else if (x2IsScalar) { + gradientArrays.set(_this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.divide(dy, x2))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var x2Squared = math.elementWiseMul(x2, x2); + var x1OverX2Squared = void 0; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } + else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } + else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + var dx2 = math.neg(x1OverX2Squared); + var dyTimesDerivative = math.elementWiseMul(dy, dx2); + if (x2IsScalar) { + gradientArrays.set(_this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + }; + return Divide; +}(op_1.Operation)); +exports.Divide = Divide; + +},{"../graph_util":11,"../util":87,"./op":75}],67:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var activation_functions_1 = require("../math/activation_functions"); +var op_1 = require("./op"); +var ElementWiseActivation = (function (_super) { + __extends(ElementWiseActivation, _super); + function ElementWiseActivation(xTensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.func = func; + return _this; + } + ElementWiseActivation.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(_this.func.output(math, x))); + }); + }; + ElementWiseActivation.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + var dydx = _this.func.der(math, x, y); + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + }; + return ElementWiseActivation; +}(op_1.Operation)); +exports.ElementWiseActivation = ElementWiseActivation; +var ReLU = (function (_super) { + __extends(ReLU, _super); + function ReLU(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.ReLUFunc()) || this; + } + return ReLU; +}(ElementWiseActivation)); +exports.ReLU = ReLU; +var TanH = (function (_super) { + __extends(TanH, _super); + function TanH(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.TanHFunc()) || this; + } + return TanH; +}(ElementWiseActivation)); +exports.TanH = TanH; +var Sigmoid = (function (_super) { + __extends(Sigmoid, _super); + function Sigmoid(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SigmoidFunc()) || this; + } + return Sigmoid; +}(ElementWiseActivation)); +exports.Sigmoid = Sigmoid; +var Square = (function (_super) { + __extends(Square, _super); + function Square(xTensor, yTensor) { + return _super.call(this, xTensor, yTensor, new activation_functions_1.SquareFunc()) || this; + } + return Square; +}(ElementWiseActivation)); +exports.Square = Square; + +},{"../math/activation_functions":15,"./op":75}],68:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var cost_functions_1 = require("../math/cost_functions"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ElementWiseCost = (function (_super) { + __extends(ElementWiseCost, _super); + function ElementWiseCost(x1Tensor, x2Tensor, yTensor, func) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + _this.func = func; + _this.oneOverNScalar = ndarray_1.Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + return _this; + } + ElementWiseCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var elementWiseCost = _this.func.cost(math, x1, x2); + var sum = math.sum(elementWiseCost); + var result = math.scalarTimesArray(_this.oneOverNScalar, sum); + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + ElementWiseCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(_this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(_this.func.der(math, x2, x1))); + } + }); + }; + ElementWiseCost.prototype.dispose = function () { + this.func.dispose(); + this.oneOverNScalar.dispose(); + }; + return ElementWiseCost; +}(op_1.Operation)); +exports.ElementWiseCost = ElementWiseCost; +var MeanSquaredCost = (function (_super) { + __extends(MeanSquaredCost, _super); + function MeanSquaredCost(x1Tensor, x2Tensor, yTensor) { + return _super.call(this, x1Tensor, x2Tensor, yTensor, new cost_functions_1.SquareCostFunc()) || this; + } + return MeanSquaredCost; +}(ElementWiseCost)); +exports.MeanSquaredCost = MeanSquaredCost; + +},{"../graph_util":11,"../math/cost_functions":19,"../math/ndarray":23,"../util":87,"./op":75}],69:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Exp = (function (_super) { + __extends(Exp, _super); + function Exp(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Exp.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.exp(x))); + }); + }; + Exp.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var y = inferenceArrays.get(this.yTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + }; + return Exp; +}(op_1.Operation)); +exports.Exp = Exp; + +},{"../graph_util":11,"./op":75}],70:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var LinearCombination = (function (_super) { + __extends(LinearCombination, _super); + function LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, outTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.c1Tensor = c1Tensor; + _this.c2Tensor = c2Tensor; + _this.outTensor = outTensor; + return _this; + } + LinearCombination.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + var c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + }; + LinearCombination.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var c1 = inferenceArrays.get(this.c1Tensor); + var c2 = inferenceArrays.get(this.c2Tensor); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + if (graph_util.shouldBackProp(_this.c1Tensor)) { + var dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(_this.c1Tensor, keep(math.sum(dotProduct1))); + } + if (graph_util.shouldBackProp(_this.c2Tensor)) { + var dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(_this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + }; + return LinearCombination; +}(op_1.Operation)); +exports.LinearCombination = LinearCombination; + +},{"../graph_util":11,"./op":75}],71:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var op_1 = require("./op"); +var Log = (function (_super) { + __extends(Log, _super); + function Log(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + return _this; + } + Log.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.log(x))); + }); + }; + Log.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.xTensor)) { + gradientArrays.set(_this.xTensor, keep(math.divide(dy, x))); + } + }); + }; + return Log; +}(op_1.Operation)); +exports.Log = Log; + +},{"../graph_util":11,"./op":75}],72:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var math_1 = require("../math/math"); +var op_1 = require("./op"); +var MatMul = (function (_super) { + __extends(MatMul, _super); + function MatMul(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + return _this; + } + MatMul.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.matMul(x1, x2))); + } + else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set(_this.yTensor, keep(math.matrixTimesVector(x1, x2))); + } + else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set(_this.yTensor, keep(math.vectorTimesMatrix(x1, x2))); + } + }); + }; + MatMul.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + var dx1 = math.matMul(dy, x2, math_1.MatrixOrientation.REGULAR, math_1.MatrixOrientation.TRANSPOSED); + gradientArrays.set(_this.x1Tensor, keep(_this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + var dx2 = math.matMul(x1, dy, math_1.MatrixOrientation.TRANSPOSED, math_1.MatrixOrientation.REGULAR); + gradientArrays.set(_this.x2Tensor, keep(_this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + }; + return MatMul; +}(op_1.Operation)); +exports.MatMul = MatMul; + +},{"../graph_util":11,"../math/math":20,"./op":75}],73:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var conv_util = require("../math/conv_util"); +var util = require("../util"); +var op_1 = require("./op"); +var MaxPool = (function (_super) { + __extends(MaxPool, _super); + function MaxPool(xTensor, yTensor, fieldSize, stride, pad) { + if (stride === void 0) { stride = 1; } + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + _this.fieldSize = fieldSize; + _this.stride = stride; + if (pad != null) { + _this.pad = pad; + } + else { + _this.pad = conv_util.computeDefaultPad(xTensor.shape, _this.fieldSize, _this.stride); + } + util.assert(util.isInt(_this.pad), "The zero padding (" + _this.pad + ") must be an integer. Change the " + + "stride and/or zero pad parameters"); + return _this; + } + MaxPool.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.maxPool(x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + MaxPool.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.maxPoolBackprop(dy, x, _this.fieldSize, _this.stride, _this.pad))); + }); + }; + return MaxPool; +}(op_1.Operation)); +exports.MaxPool = MaxPool; + +},{"../math/conv_util":17,"../util":87,"./op":75}],74:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Multiply = (function (_super) { + __extends(Multiply, _super); + function Multiply(x1Tensor, x2Tensor, yTensor) { + var _this = _super.call(this) || this; + _this.x1Tensor = x1Tensor; + _this.x2Tensor = x2Tensor; + _this.yTensor = yTensor; + util.assert(util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Multiply.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.x1Tensor); + var t2 = inferenceArrays.get(this.x2Tensor); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } + else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(_this.yTensor, keep(result)); + }); + }; + Multiply.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var x1 = inferenceArrays.get(this.x1Tensor); + var x2 = inferenceArrays.get(this.x2Tensor); + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.x1Tensor)) { + if (util.isScalarShape(_this.x1Tensor.shape)) { + var mul = math.elementWiseMul(dy, x2); + gradientArrays.set(_this.x1Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x2.shape)) { + gradientArrays.set(_this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } + else { + gradientArrays.set(_this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + if (graph_util.shouldBackProp(_this.x2Tensor)) { + if (util.isScalarShape(_this.x2Tensor.shape)) { + var mul = math.elementWiseMul(dy, x1); + gradientArrays.set(_this.x2Tensor, keep(math.sum(mul))); + } + else if (util.isScalarShape(x1.shape)) { + gradientArrays.set(_this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } + else { + gradientArrays.set(_this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + }; + return Multiply; +}(op_1.Operation)); +exports.Multiply = Multiply; + +},{"../graph_util":11,"../util":87,"./op":75}],75:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Operation = (function () { + function Operation() { + } + Operation.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { }; + Operation.prototype.dispose = function () { }; + return Operation; +}()); +exports.Operation = Operation; + +},{}],76:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var ReduceSum = (function (_super) { + __extends(ReduceSum, _super); + function ReduceSum(x, outTensor) { + var _this = _super.call(this) || this; + _this.x = x; + _this.outTensor = outTensor; + util.assertShapesMatch(outTensor.shape, []); + return _this; + } + ReduceSum.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.x); + math.scope(function (keep) { + inferenceArrays.set(_this.outTensor, keep(math.sum(x))); + }); + }; + ReduceSum.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.x)) { + return; + } + math.scope(function (keep) { + var dy = gradientArrays.get(_this.outTensor); + if (_this.ones == null) { + var xArray = inferenceArrays.get(_this.x); + _this.ones = ndarray_1.NDArray.zerosLike(xArray); + _this.ones.fill(1); + } + gradientArrays.set(_this.x, keep(math.scalarTimesArray(dy, _this.ones))); + }); + }; + return ReduceSum; +}(op_1.Operation)); +exports.ReduceSum = ReduceSum; + +},{"../graph_util":11,"../math/ndarray":23,"../util":87,"./op":75}],77:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var util = require("../util"); +var op_1 = require("./op"); +var Reshape = (function (_super) { + __extends(Reshape, _super); + function Reshape(xTensor, yTensor) { + var _this = _super.call(this) || this; + _this.xTensor = xTensor; + _this.yTensor = yTensor; + var xSize = util.sizeFromShape(xTensor.shape); + var ySize = util.sizeFromShape(yTensor.shape); + util.assert(xSize === ySize, "The input size (" + xSize + ") and output size (" + ySize + ") must match"); + return _this; + } + Reshape.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var x = inferenceArrays.get(this.xTensor); + math.scope(function (keep) { + inferenceArrays.set(_this.yTensor, keep(math.reshape(x, _this.yTensor.shape))); + }); + }; + Reshape.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var dy = gradientArrays.get(this.yTensor); + math.scope(function (keep) { + gradientArrays.set(_this.xTensor, keep(math.reshape(dy, _this.xTensor.shape))); + }); + }; + return Reshape; +}(op_1.Operation)); +exports.Reshape = Reshape; + +},{"../util":87,"./op":75}],78:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("../graph"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Softmax = (function (_super) { + __extends(Softmax, _super); + function Softmax(logitsTensor, output) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.output = output; + return _this; + } + Softmax.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + return math.scope(function (keep) { + inferenceArrays.set(_this.output, keep(math.softmax(logits))); + }); + }; + Softmax.prototype.backProp = function () { + throw Error('Softmax backprop is not yet implemented'); + }; + return Softmax; +}(op_1.Operation)); +exports.Softmax = Softmax; +var SoftmaxCrossEntropyCost = (function (_super) { + __extends(SoftmaxCrossEntropyCost, _super); + function SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor) { + var _this = _super.call(this) || this; + _this.logitsTensor = logitsTensor; + _this.labelTensor = labelTensor; + _this.yTensor = yTensor; + _this.epsilon = ndarray_1.Scalar.new(1e-5); + _this.softmaxTensor = new graph_1.Tensor(logitsTensor.shape); + return _this; + } + SoftmaxCrossEntropyCost.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var logits = inferenceArrays.get(this.logitsTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + var softmaxResult = math.softmax(logits); + inferenceArrays.set(_this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set(_this.yTensor, keep(crossEntropyCost(math, softmaxResult, label, _this.epsilon))); + }); + }; + SoftmaxCrossEntropyCost.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var softmax = inferenceArrays.get(this.softmaxTensor); + var label = inferenceArrays.get(this.labelTensor); + math.scope(function (keep) { + gradientArrays.set(_this.logitsTensor, keep(math.sub(softmax, label))); + }); + }; + SoftmaxCrossEntropyCost.prototype.disposeTransientArrays = function (inferenceArrays, gradientArrays) { + inferenceArrays.disposeArray(this.softmaxTensor); + }; + SoftmaxCrossEntropyCost.prototype.dispose = function () { + this.epsilon.dispose(); + }; + return SoftmaxCrossEntropyCost; +}(op_1.Operation)); +exports.SoftmaxCrossEntropyCost = SoftmaxCrossEntropyCost; +function crossEntropyCost(math, y, target, epsilon) { + util.assert(y.size === target.size, 'The output and target must be the same size'); + return math.scope(function () { + var yPlusEps = math.scalarPlusArray(epsilon, y); + var logOutput = math.log(yPlusEps); + var tarLogOutput = math.elementWiseMul(target, logOutput); + var costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} +exports.crossEntropyCost = crossEntropyCost; + +},{"../graph":8,"../math/ndarray":23,"../util":87,"./op":75}],79:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var util = require("../util"); +var op_1 = require("./op"); +var Split = (function (_super) { + __extends(Split, _super); + function Split(input, outputs) { + var _this = _super.call(this) || this; + _this.input = input; + _this.outputs = outputs; + outputs.forEach(function (output) { + util.assertShapesMatch(input.shape, output.shape); + }); + return _this; + } + Split.prototype.feedForward = function (math, inferenceArrays) { + var inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(function (output) { + inferenceArrays.set(output, inputArray); + }); + }; + Split.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + if (!graph_util.shouldBackProp(this.input)) { + return; + } + math.scope(function (keep) { + var dx = math.add(gradientArrays.get(_this.outputs[0]), gradientArrays.get(_this.outputs[1])); + _this.outputs.slice(2).forEach(function (output) { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(_this.input, keep(dx)); + }); + }; + return Split; +}(op_1.Operation)); +exports.Split = Split; + +},{"../graph_util":11,"../util":87,"./op":75}],80:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_util = require("../graph_util"); +var ndarray_1 = require("../math/ndarray"); +var util = require("../util"); +var op_1 = require("./op"); +var Subtract = (function (_super) { + __extends(Subtract, _super); + function Subtract(t1, t2, outTensor) { + var _this = _super.call(this) || this; + _this.t1 = t1; + _this.t2 = t2; + _this.outTensor = outTensor; + util.assert(util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + return _this; + } + Subtract.prototype.feedForward = function (math, inferenceArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + math.scope(function (keep) { + var result; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } + else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } + else { + result = math.sub(t1, t2); + } + inferenceArrays.set(_this.outTensor, keep(result)); + }); + }; + Subtract.prototype.backProp = function (math, inferenceArrays, gradientArrays) { + var _this = this; + var t1 = inferenceArrays.get(this.t1); + var t2 = inferenceArrays.get(this.t2); + var dy = gradientArrays.get(this.outTensor); + math.scope(function (keep) { + if (graph_util.shouldBackProp(_this.t1)) { + if (util.isScalarShape(_this.t1.shape)) { + var sum = math.sum(dy); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t1, keep(math.divide(sum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t1, keep(dy)); + } + } + if (graph_util.shouldBackProp(_this.t2)) { + if (util.isScalarShape(_this.t2.shape)) { + var sum = math.sum(dy); + var negSum = math.neg(sum); + if (_this.dySizeScalar == null) { + _this.dySizeScalar = ndarray_1.Scalar.new(dy.size); + } + gradientArrays.set(_this.t2, keep(math.divide(negSum, _this.dySizeScalar))); + } + else { + gradientArrays.set(_this.t2, keep(math.neg(dy))); + } + } + }); + }; + Subtract.prototype.dispose = function () { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + }; + return Subtract; +}(op_1.Operation)); +exports.Subtract = Subtract; + +},{"../graph_util":11,"../math/ndarray":23,"../util":87,"./op":75}],81:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var Optimizer = (function () { + function Optimizer(specifiedVariableList) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList; + } + } + return Optimizer; +}()); +exports.Optimizer = Optimizer; + +},{}],82:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function defaultCompare(a, b) { + if (a === b) { + return 0; + } + else if (a < b) { + return -1; + } + else { + return 1; + } +} +exports.defaultCompare = defaultCompare; +var PriorityQueue = (function () { + function PriorityQueue(comparator, indexObserver) { + this.comparator = comparator; + this.indexObserver = indexObserver; + this.heap = []; + } + PriorityQueue.prototype.enqueue = function (t) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + }; + PriorityQueue.prototype.dequeue = function () { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + var t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + }; + PriorityQueue.prototype.update = function (newT, index) { + var last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } + else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + }; + PriorityQueue.prototype.empty = function () { + return this.heap.length === 0; + }; + PriorityQueue.prototype.onIndexChanged = function (t, newIndex) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + }; + PriorityQueue.prototype.getParentIndex = function (index) { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + }; + PriorityQueue.prototype.getLeftChildIndex = function (index) { + var candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.getRightChildIndex = function (index) { + var candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + }; + PriorityQueue.prototype.siftUpIndex = function (index) { + var parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + }; + PriorityQueue.prototype.siftUp = function (index) { + var siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + }; + PriorityQueue.prototype.siftDownIndex = function (index) { + if (index >= this.heap.length) { + return -1; + } + var largestChildIndex = index; + var leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + var rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + }; + PriorityQueue.prototype.siftDown = function (index) { + var siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + }; + PriorityQueue.prototype.compare = function (aIndex, bIndex) { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + }; + PriorityQueue.prototype.swap = function (a, b) { + var temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + }; + return PriorityQueue; +}()); +exports.PriorityQueue = PriorityQueue; + +},{}],83:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var operation_emitter = require("./operation_emitter"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var util = require("./util"); +var FeedDictionary = (function () { + function FeedDictionary(feedEntries) { + var _this = this; + this.dict = {}; + if (feedEntries) { + feedEntries.forEach(function (entry) { return _this.dict[entry.tensor.id] = entry; }); + } + } + return FeedDictionary; +}()); +exports.FeedDictionary = FeedDictionary; +var CostReduction; +(function (CostReduction) { + CostReduction[CostReduction["NONE"] = 0] = "NONE"; + CostReduction[CostReduction["SUM"] = 1] = "SUM"; + CostReduction[CostReduction["MEAN"] = 2] = "MEAN"; +})(CostReduction = exports.CostReduction || (exports.CostReduction = {})); +var Session = (function () { + function Session(graph, math) { + this.graph = graph; + this.math = math; + this.activationArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.gradientArrayMap = new tensor_array_map_1.TensorArrayMap(); + this.runtimeCache = {}; + this.oneScalar = ndarray_1.Scalar.new(1); + } + Session.prototype.dispose = function () { + var _this = this; + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(function (key) { + var runtime = _this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(function (op) { return op.dispose(); }); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + }; + Session.prototype.evalAll = function (tensors, feedEntries) { + var _this = this; + return this.math.scope(function () { + var feed = new FeedDictionary(feedEntries); + var runtime = _this.getOrCreateRuntime(tensors, feed); + var activations = _this.activationArrayMap; + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeTransientOperationArrays(runtime.operations, _this.activationArrayMap, _this.gradientArrayMap); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + runtime.operations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + var results = tensors.map(function (x) { return activations.get(x); }); + tensors.forEach(function (x) { return activations.delete(x); }); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + return results; + }); + }; + Session.prototype.eval = function (tensor, feedEntries) { + return this.evalAll([tensor], feedEntries)[0]; + }; + Session.prototype.train = function (costTensor, feedEntries, batchSize, optimizer, costReduction) { + var _this = this; + if (costReduction === void 0) { costReduction = CostReduction.NONE; } + util.assert(util.isScalarShape(costTensor.shape), 'Cost tensor for training must be a scalar value.'); + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = ndarray_1.Scalar.new(batchSize); + } + var feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + var runtime = this.getOrCreateRuntime([costTensor], feed); + var inferenceOperations = runtime.operations; + var backPropOperations = runtime.operations.slice().reverse(); + var activations = this.activationArrayMap; + var gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + session_util.addPersistentArraysToTensorArrayMap(runtime.nodes, activations); + optimizer.beforeBatch(this.math, batchSize, runtime, activations, gradients); + return this.math.scope(function (keep, track) { + var cost = track(ndarray_1.Scalar.new(0)); + for (var i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs(runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients(runtime.nodes, gradients); + session_util.disposeTransientOperationArrays(runtime.operations, activations, gradients); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(feed, activations, _this.math); + inferenceOperations.forEach(function (op) { return op.feedForward(_this.math, activations); }); + backPropOperations.forEach(function (op) { return op.backProp(_this.math, activations, gradients); }); + optimizer.afterExample(_this.math, runtime, activations, gradients); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(feed, activations, _this.math); + cost = _this.updateCostForExample(cost, activations.get(costTensor), costReduction); + } + optimizer.afterBatch(_this.math, batchSize, runtime, activations, gradients); + return _this.updateCostForBatch(cost, costReduction); + }); + }; + Session.prototype.updateCostForExample = function (totalCost, currCost, costReduction) { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + }; + Session.prototype.updateCostForBatch = function (totalCost, costReduction) { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + }; + Session.prototype.getOrCreateRuntime = function (tensors, feed) { + var key = this.makeRuntimeCacheKey(tensors, feed); + var runtime = this.runtimeCache[key]; + if (runtime === undefined) { + var nodes = session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + var operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = { nodes: nodes, operations: operations }; + this.runtimeCache[key] = runtime; + } + return runtime; + }; + Session.prototype.makeRuntimeCacheKey = function (tensors, feed) { + return tensors.map(function (x) { return x.id; }).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + }; + return Session; +}()); +exports.Session = Session; + +},{"./math/ndarray":23,"./operation_emitter":60,"./session_util":84,"./tensor_array_map":86,"./util":87}],84:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var graph_1 = require("./graph"); +var graph_util = require("./graph_util"); +var ndarray_1 = require("./math/ndarray"); +var util = require("./util"); +function getTerminatingNodesFromFeedDictionary(feedDictionary) { + return Object.keys(feedDictionary.dict) + .map(function (tensorID) { return feedDictionary.dict[+tensorID].tensor.node; }); +} +exports.getTerminatingNodesFromFeedDictionary = getTerminatingNodesFromFeedDictionary; +function getOrderedEvaluationSetFromEvalTensor(evalTensors, feedDictionary) { + var terminatingNodes = getTerminatingNodesFromFeedDictionary(feedDictionary); + var evalNodes = evalTensors.map(function (x) { return x.node; }); + var unorderedEvaluationSet = graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + var orderedEvaluationSet = graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} +exports.getOrderedEvaluationSetFromEvalTensor = getOrderedEvaluationSetFromEvalTensor; +function addPersistentArraysToTensorArrayMap(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode || node instanceof graph_1.ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} +exports.addPersistentArraysToTensorArrayMap = addPersistentArraysToTensorArrayMap; +function getVariableNodesFromEvaluationSet(evaluationSet) { + var nodes = []; + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.VariableNode) { + nodes.push(node); + } + }); + return nodes; +} +exports.getVariableNodesFromEvaluationSet = getVariableNodesFromEvaluationSet; +function throwIfFeedDictionaryContainsNDArrays(feedDictionary) { + Object.keys(feedDictionary.dict).forEach(function (tensorID) { + if (feedDictionary.dict[+tensorID].data instanceof ndarray_1.NDArray) { + throw new Error('training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} +exports.throwIfFeedDictionaryContainsNDArrays = throwIfFeedDictionaryContainsNDArrays; +function loadInputsFromFeedDictionaryToTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + var data; + if (feedEntry.data instanceof ndarray_1.NDArray) { + data = feedEntry.data; + } + else { + var provider = feedEntry.data; + data = provider.getNextCopy(math); + } + util.assert(util.arraysEqual(feedEntry.tensor.shape, data.shape), "Error loading FeedEntry: feeding NDArray of shape " + data.shape + " " + + ("does not match Tensor (id: " + feedEntry.tensor.id + ") shape: ") + + (feedEntry.tensor.shape + ".")); + activations.set(feedEntry.tensor, data); + }); +} +exports.loadInputsFromFeedDictionaryToTensorArrayMap = loadInputsFromFeedDictionaryToTensorArrayMap; +function releaseFeedDictionaryInputsFromTensorArrayMap(batchFeed, activations, math) { + Object.keys(batchFeed.dict).forEach(function (tensorID) { + var feedEntry = batchFeed.dict[+tensorID]; + if (!(feedEntry.data instanceof ndarray_1.NDArray)) { + var provider = feedEntry.data; + var feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + activations.delete(feedEntry.tensor); + }); +} +exports.releaseFeedDictionaryInputsFromTensorArrayMap = releaseFeedDictionaryInputsFromTensorArrayMap; +function removeFeedDictionaryNodesFromEvaluationSet(feedDictionary, evaluationSet) { + var i = 0; + while (i < evaluationSet.length) { + var node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } + else { + ++i; + } + } +} +exports.removeFeedDictionaryNodesFromEvaluationSet = removeFeedDictionaryNodesFromEvaluationSet; +function disposeAndInitializeOperationOutputs(evaluationSet, tensorArrayMap) { + evaluationSet.forEach(function (node) { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} +exports.disposeAndInitializeOperationOutputs = disposeAndInitializeOperationOutputs; +function disposeAndInitializeOperationInputGradients(evaluationSet, gradients) { + evaluationSet.forEach(function (node) { + Object.keys(node.inputs).forEach(function (inputName) { + var input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} +exports.disposeAndInitializeOperationInputGradients = disposeAndInitializeOperationInputGradients; +function disposeTransientOperationArrays(operations, activations, gradients) { + operations.forEach(function (op) { return op.disposeTransientArrays(activations, gradients); }); +} +exports.disposeTransientOperationArrays = disposeTransientOperationArrays; +function throwErrorIfEvaluationSetContainsPlaceholderNodes(evaluationSet) { + evaluationSet.forEach(function (node) { + if (node instanceof graph_1.PlaceholderNode) { + var shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error('Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} +exports.throwErrorIfEvaluationSetContainsPlaceholderNodes = throwErrorIfEvaluationSetContainsPlaceholderNodes; +function addSplitNodes(nodes) { + var nodeIdToNumConsumers = []; + var nodeIdToSplitNode = {}; + nodes.forEach(function (node) { + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new graph_1.SplitNode(input.graph, inputTensor); + } + }); + }); + var newNodes = []; + nodes.forEach(function (node) { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + var splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + var keys = Object.keys(node.inputs); + keys.forEach(function (key) { + var inputTensor = node.inputs[key]; + var inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} +exports.addSplitNodes = addSplitNodes; + +},{"./graph":8,"./graph_util":11,"./math/ndarray":23,"./util":87}],85:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var ndarray_1 = require("./math/ndarray"); +var optimizer_1 = require("./optimizer"); +var session_util = require("./session_util"); +var tensor_array_map_1 = require("./tensor_array_map"); +var SGDOptimizer = (function (_super) { + __extends(SGDOptimizer, _super); + function SGDOptimizer(learningRate, specifiedVariableList) { + var _this = _super.call(this, specifiedVariableList) || this; + _this.learningRate = learningRate; + _this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + _this.one = ndarray_1.Scalar.new(1); + return _this; + } + SGDOptimizer.prototype.beforeBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = ndarray_1.Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach(function (node) { return _this.variableGradients.set(node.output, ndarray_1.NDArray.zeros(node.output.shape)); }); + }; + SGDOptimizer.prototype.afterExample = function (math, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var gradient = gradientArrayMap.get(node.output); + var accumulatedGradient = _this.variableGradients.get(node.output); + _this.variableGradients.set(node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + }; + SGDOptimizer.prototype.afterBatch = function (math, batchSize, runtime, activationArrayMap, gradientArrayMap) { + var _this = this; + math.scope(function (keep) { + _this.variableNodes.forEach(function (node) { + var oldVariable = activationArrayMap.get(node.output); + var gradient = _this.variableGradients.get(node.output); + var variable = math.scaledArrayAdd(_this.c, gradient, _this.one, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + oldVariable.dispose(); + }); + }); + this.variableGradients.dispose(); + this.variableGradients = new tensor_array_map_1.TensorArrayMap(); + }; + SGDOptimizer.prototype.dispose = function () { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + }; + SGDOptimizer.prototype.setLearningRate = function (learningRate) { + this.learningRate = learningRate; + }; + return SGDOptimizer; +}(optimizer_1.Optimizer)); +exports.SGDOptimizer = SGDOptimizer; + +},{"./math/ndarray":23,"./optimizer":81,"./session_util":84,"./tensor_array_map":86}],86:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TensorArrayMap = (function () { + function TensorArrayMap() { + this.dict = {}; + } + TensorArrayMap.prototype.set = function (tensor, array) { + this.dict[tensor.id] = array; + }; + TensorArrayMap.prototype.get = function (tensor, skipChecks) { + if (skipChecks === void 0) { skipChecks = false; } + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + var nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda; + }; + TensorArrayMap.prototype.delete = function (tensor) { + delete this.dict[tensor.id]; + }; + TensorArrayMap.prototype.disposeArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + var nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + }; + TensorArrayMap.prototype.size = function () { + return Object.keys(this.dict).length; + }; + TensorArrayMap.prototype.dispose = function () { + var _this = this; + Object.keys(this.dict).forEach(function (tensorID) { + var nda = _this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + }; + TensorArrayMap.prototype.hasNullArray = function (tensor) { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + }; + return TensorArrayMap; +}()); +exports.TensorArrayMap = TensorArrayMap; + +},{}],87:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function shuffle(array) { + var counter = array.length; + var temp = 0; + var index = 0; + while (counter > 0) { + index = (Math.random() * counter) | 0; + counter--; + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} +exports.shuffle = shuffle; +function clamp(min, x, max) { + return Math.max(min, Math.min(x, max)); +} +exports.clamp = clamp; +function randUniform(a, b) { + return Math.random() * (b - a) + a; +} +exports.randUniform = randUniform; +function randGauss(mean, stdDev, truncated) { + if (mean === void 0) { mean = 0; } + if (stdDev === void 0) { stdDev = 1; } + if (truncated === void 0) { truncated = false; } + var v1, v2, s; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + var result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} +exports.randGauss = randGauss; +function distSquared(a, b) { + var result = 0; + for (var i = 0; i < a.length; i++) { + var diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} +exports.distSquared = distSquared; +function assert(expr, msg) { + if (!expr) { + throw new Error(msg); + } +} +exports.assert = assert; +function assertShapesMatch(shapeA, shapeB, errorMessagePrefix) { + if (errorMessagePrefix === void 0) { errorMessagePrefix = ''; } + assert(arraysEqual(shapeA, shapeB), errorMessagePrefix + ("Shapes " + shapeA + " and " + shapeB + " must match")); +} +exports.assertShapesMatch = assertShapesMatch; +function flatten(arr, ret) { + ret = (ret === undefined ? [] : ret); + for (var i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } + else { + ret.push(arr[i]); + } + } + return ret; +} +exports.flatten = flatten; +function inferShape(arr) { + var shape = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} +exports.inferShape = inferShape; +function sizeFromShape(shape) { + if (shape.length === 0) { + return 1; + } + var size = shape[0]; + for (var i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} +exports.sizeFromShape = sizeFromShape; +function isScalarShape(shape) { + return shape.length === 0; +} +exports.isScalarShape = isScalarShape; +function arraysEqual(n1, n2) { + if (n1.length !== n2.length) { + return false; + } + for (var i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} +exports.arraysEqual = arraysEqual; +function isInt(a) { + return a % 1 === 0; +} +exports.isInt = isInt; +function tanh(x) { + if (Math.tanh != null) { + return Math.tanh(x); + } + if (x === Infinity) { + return 1; + } + else if (x === -Infinity) { + return -1; + } + else { + var e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} +exports.tanh = tanh; +function sizeToSquarishShape(size) { + for (var a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} +exports.sizeToSquarishShape = sizeToSquarishShape; +function createShuffledIndices(n) { + var shuffledIndices = new Uint32Array(n); + for (var i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} +exports.createShuffledIndices = createShuffledIndices; + +},{}]},{},[2]); diff --git a/demos/polymer-spec.ts b/demos/polymer-spec.ts new file mode 100644 index 0000000000..1664cb4385 --- /dev/null +++ b/demos/polymer-spec.ts @@ -0,0 +1,64 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +/** + * @fileoverview + * + * Defines an interface for creating Polymer elements in Typescript with the + * correct typings. A Polymer element should be defined like this: + * + * ``` + * let MyElementPolymer = PolymerElement({ + * is: 'my-polymer-element', + * properties: { + * foo: string, + * bar: Array + * } + * }); + * + * class MyElement extends MyElementPolymer { + * foo: string; + * bar: number[]; + * + * ready() { + * console.log('MyElement initialized!'); + * } + * } + * + * document.registerElement(MyElement.prototype.is, MyElement); + * ``` + */ + +export type Spec = { + is: string; properties: { + [key: string]: (Function|{ + // tslint:disable-next-line:no-any + type: Function, value?: any; + reflectToAttribute?: boolean; + readonly?: boolean; + notify?: boolean; + computed?: string; + observer?: string; + }) + }; + observers?: string[]; +}; + +export function PolymerElement(spec: Spec) { + // tslint:disable-next-line:no-any + return Polymer.Class(spec as any) as {new (): PolymerHTMLElement}; +} + +export interface PolymerHTMLElement extends HTMLElement, polymer.Base {} diff --git a/demos/xhr-dataset.ts b/demos/xhr-dataset.ts new file mode 100644 index 0000000000..6e7833d311 --- /dev/null +++ b/demos/xhr-dataset.ts @@ -0,0 +1,195 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {InMemoryDataset} from '../src/dataset'; +import {Array1D, NDArray} from '../src/math/ndarray'; +import * as util from '../src/util'; + +const PARSING_IMAGE_CANVAS_HEIGHT_PX = 1000; + +export interface NDArrayInfo { + path: string; + name: string; + dataType: 'uint8'|'float32'|'png'; + shape: number[]; +} + +export interface XhrDatasetConfig { + data: NDArrayInfo[]; + + labelClassNames?: string[]; + // Paths to pre-built models. + modelConfigs: {[modelName: string]: XhrModelConfig}; +} + +export interface XhrModelConfig { + path: string; +} + +export function getXhrDatasetConfig(jsonConfigPath: string): + Promise<{[datasetName: string]: XhrDatasetConfig}> { + return new Promise<{[datasetName: string]: XhrDatasetConfig}>( + (resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', jsonConfigPath); + + xhr.onload = () => { + resolve(JSON.parse( + xhr.responseText) as {[datasetName: string]: XhrDatasetConfig}); + }; + xhr.onerror = (error) => { + reject(error); + }; + xhr.send(); + }); +} + +export class XhrDataset extends InMemoryDataset { + protected xhrDatasetConfig: XhrDatasetConfig; + + constructor(xhrDatasetConfig: XhrDatasetConfig) { + super(xhrDatasetConfig.data.map(x => x.shape)); + this.xhrDatasetConfig = xhrDatasetConfig; + } + + protected getNDArray(info: NDArrayInfo): Promise { + const dataPromise = info.dataType === 'png' ? + parseTypedArrayFromPng(info, info.shape as [number, number, number]) : + parseTypedArrayFromBinary(info); + + return dataPromise.then(data => { + const inputSize = util.sizeFromShape(info.shape); + const ndarrays: T[] = []; + for (let i = 0; i < data.length / inputSize; i++) { + const values = data.subarray(i * inputSize, (i + 1) * inputSize); + const ndarray = + NDArray.make(info.shape, {values: new Float32Array(values)}); + ndarrays.push(ndarray); + } + return ndarrays; + }); + } + + fetchData(): Promise { + return new Promise((resolve, reject) => { + const promises = this.xhrDatasetConfig.data.map(x => this.getNDArray(x)); + Promise.all(promises).then((data: NDArray[][]) => { + this.dataset = data; + resolve(); + }); + }); + } +} + +function parseTypedArrayFromBinary(info: NDArrayInfo): + Promise { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', info.path); + xhr.responseType = 'arraybuffer'; + xhr.onload = event => { + const data = (info.dataType === 'float32') ? + new Float32Array(xhr.response) : + new Uint8Array(xhr.response); + resolve(data); + }; + xhr.onerror = err => reject(err); + xhr.send(); + }); +} + +function parseGrayscaleImageData( + data: Uint8Array|Uint8ClampedArray, result: Uint8Array, + resultOffset: number): void { + let idx = resultOffset; + for (let i = 0; i < data.length; i += 4) { + result[idx++] = data[i]; + } +} + +function parseRGBImageData( + data: Uint8Array|Uint8ClampedArray, result: Uint8Array, + resultOffset: number): void { + let idx = resultOffset; + for (let i = 0; i < data.length; i += 4) { + result[idx] = data[i]; + result[idx + 1] = data[i + 1]; + result[idx + 2] = data[i + 2]; + idx += 3; + } +} + +function parseImage( + img: HTMLImageElement, shape: [number, number, number]): Uint8Array { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d')!; + const N = img.height; + const inputSize = util.sizeFromShape(shape); + const result = new Uint8Array(N * inputSize); + if (img.width !== shape[0] * shape[1]) { + throw new Error( + `Image width (${img.width}) must be multiple of ` + + `rows*columns (${shape[0]}*${shape[1]}) of the ndarray`); + } + // TODO(smilkov): Canvas has max width of 32,767px. This approach + // (canvas.width = shape[0] * shape[1]) works with examples up to 181x181px. + // Consider having the canvas in un-flat format, i.e. + // canvas.width = shape[1]; canvas.height = DRAW_BATCH * shape[0]; + canvas.width = img.width; + + // Ideally we want canvas.height=img.height (which is N), but canvas size is + // limited by the browser, so we do multiple passes with a smaller canvas. + canvas.height = PARSING_IMAGE_CANVAS_HEIGHT_PX; + const sx = 0; + const sWidth = canvas.width; + let sHeight = canvas.height; + const dx = 0; + const dy = 0; + const dWidth = sWidth; + let dHeight = sHeight; + const depth = shape[2]; + let offset = 0; + const numPasses = Math.ceil(N / canvas.height); + for (let pass = 0; pass < numPasses; ++pass) { + const sy = pass * canvas.height; + if ((pass === numPasses - 1) && (N % canvas.height > 0)) { + // Last pass is a special case. + canvas.height = N % canvas.height; + sHeight = canvas.height; + dHeight = sHeight; + } + ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); + const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data; + (depth === 1) ? parseGrayscaleImageData(data, result, offset) : + parseRGBImageData(data, result, offset); + offset += canvas.height * inputSize; + } + return result; +} + +function parseTypedArrayFromPng( + info: NDArrayInfo, shape: [number, number, number]): Promise { + return new Promise((resolve, reject) => { + let img = new Image(); + img.setAttribute('crossOrigin', ''); + img.onload = () => { + const result = parseImage(img, shape); + img.src = ''; + img = null!; + resolve(result); + }; + img.src = info.path; + }); +} diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000000..06af868851 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,72 @@ +--- +layout: page +order: 1000 +--- +# Roadmap + +This page outlines some of the projects we wish to happen in the near future. +These are projects that we would love to see the open source community +contribute to. + +## Optimizers + +Currently, **deeplearn.js** only has an SGD optimizer, however the optimizer +interface is generic enough to support new optimizers. We would love to see: + + * RMSProp + * Adagrad + * Adadelta + * Adam + * Adamax + +## Logical sampling + +When writing custom shader programs in **deeplearn.js**, the author must sample +textures in 2D physical texture space. This means that if an shader program +operates on an `Array3D`, it must manually convert between logical 3D space and +physical 2D texture space. Since shader programs are a little tricky to debug, +this makes shader programs error-prone. + +We have started on "logical sampling", that is, introducing functions and a +shader compiler that allows shaders to sample in logical space through a utility +function. This means we can store higher dimensional NDArrays in 2D textures in +whatever shape we want to ensure minimal reshapes when chaining operations. + +Currently, matmul is the only GPU shader program that uses the new shader compiler +and logical sampling, but it should serve as a guide for the way shader programs +should be migrated, and how new shader programs should be written. + +## Batch as the outer dimension + +**deeplearn.js** at the shader level only supports operations with a batch size +of 1, whereas most other machine learning libraries use the batch size as an +outer dimension. This is usually okay for many applications, though it can be +restrictive when models are ported. + +As part of the new shader compiler and helper functions to do logical sampling, +we now can introduce batching as an outer dimension of operations. + +## Automatic TensorFlow <=> deeplearn.js + +Currently we support dumping weights from a TensorFlow checkpoint into a format +that can be imported into **deeplearn.js**, however the developer must then +recreate the model in **deeplearn.js** and use the weights from that checkpoint. + +We plan on building a way to port models directly from TensorFlow => +**deeplearn.js** automatically from a `GraphDef`. + +## Dynamic batching + +Dynamic batching, which allows training with explicitly defining a graph, but +instead simply analyzing the forward mathematical operations and differentiating +that dynamic computation graph, is a popular method for training models. + +We can implement dynamic batching by doing it at the NDArrayMath layer. When +mathematical methods are called, we can record operations that were called and +automatically differentiate when requested. + +## Recurrence in training + +**deeplearn.js** doesn't currently support recurrence as top level +functionality during training, however we do support arbitrary data flow graphs, +so recurrence should be straight forward to implement. diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md new file mode 100644 index 0000000000..e3713a7c1c --- /dev/null +++ b/docs/tutorials/index.md @@ -0,0 +1,24 @@ +--- +layout: page +title: '' +--- +# Tutorials + +This is a living, breathing document. Feel free to add your work below. + +{% assign default_paths = site.pages | map: "path" %} +{% assign page_paths = site.header_pages | default: default_paths %} + +
diff --git a/docs/tutorials/intro.md b/docs/tutorials/intro.md new file mode 100644 index 0000000000..f95731d6c5 --- /dev/null +++ b/docs/tutorials/intro.md @@ -0,0 +1,310 @@ +--- +layout: page +order: 2 +--- +# Introduction + +**deeplearn.js** is an open source WebGL-accelerated JavaScript library for machine +intelligence. **deeplearn.js** brings highly performant machine learning +building blocks to your fingertips, allowing you to train neural networks +in a browser or run pre-trained models in inference mode. It provides an API for +constructing differentiable data flow graphs, as well as a set of mathematical +functions that can be used directly. + +* TOC +{:toc} + +For the purposes of the documentation, we will use TypeScript code examples. +For vanilla JavaScript, you may need to remove TypeScript syntax like +`const`, `let`, or other type definitions. + +## Core concepts + +### NDArrays + +The central unit of data in **deeplearn.js** is the `NDArray`. An `NDArray` +consists of a set of floating point values shaped into an array of an arbitrary +number of dimensions. `NDArray`s have a `shape` attribute to define +their shape. The library provides sugar subclasses for low-rank `NDArray`s: +`Scalar`, `Array1D`, `Array2D`, `Array3D` and `Array4D`. + +Example usage with a 2x3 matrix: + +```js +const shape = [2, 3]; // 2 rows, 3 columns +const a = Array2D.new(shape, [1.0, 2.0, 3.0, 10.0, 20.0, 30.0]); +``` + +`NDArray`s can store data either on the GPU as a `WebGLTexture`, where each +pixel stores a floating point value, or on the CPU as a vanilla JavaScript +`TypedArray`. Most of the time, the user should not think about the storage, +as it is an implementation detail. + +If `NDArray` data is +stored on the CPU, the first time a GPU mathematical operation is called the +data will be uploaded to a texture automatically. If you call +`NDArray.getValues()` on a GPU-resident `NDArray`, the +library will download the texture to the CPU and delete the texture. + +### NDArrayMath + +The library provides a `NDArrayMath` base class which defines a set of +mathematical functions that operate on `NDArray`s. + +#### NDArrayMathGPU + +When using the `NDArrayMathGPU` implementation, these mathematical +operations enqueue shader programs to be executed on the GPU. Unlike in +`NDArrayMathCPU`, **these operations are not blocking**, but the user can +synchronize the cpu with the gpu by calling `get()` or `getValues()` on +the `NDArray`, as we describe in detail below. + +These shaders read and write from `WebGLTexture`s which are owned by +`NDArray`s. When chaining mathematical operations, textures can stay in GPU +memory (not downloaded to the CPU between operations), which is critical for +performance. + +Example of taking the mean squared difference between two matrices (more +details about `math.scope`, `keep`, and `track` below): + +```js +const math = new NDArrayMathGPU(); + +math.scope((keep, track) => { + const a = track(Array2D.new([2, 2], [1.0, 2.0, 3.0, 4.0])); + const b = track(Array2D.new([2, 2], [0.0, 2.0, 4.0, 6.0])); + + // Non-blocking math calls. + const diff = math.sub(a, b); + const squaredDiff = math.elementWiseMul(diff, diff); + const sum = math.sum(squaredDiff); + const size = Scalar.new(a.size); + const average = math.divide(sum, size); + + // Blocking call to actually read the values from average. Waits until the + // GPU has finished executing the operations before returning values. + console.log(average.get()); // average is a Scalar so we use .get() +}); +``` + +> NOTE: `NDArray.get()` and `NDArray.getValues()` are blocking calls. +There is no need to register callbacks after performing chained math functions, +just call `getValues()` to synchronize the CPU & GPU. + +> TIP: Avoid calling `get()` or `getValues()` between mathematical GPU +operations unless you are debugging. This forces a texture download, and +subsequent `NDArrayMathGPU` calls will have to re-upload the data to a new +texture. + +##### math.scope() + +When math operations are used, you must wrap them in a math.scope() function +closure as shown in the example above. The results of math operations in this +scope will get disposed at the end of the scope, unless they are the value +returned in the scope. + +Two functions are passed to the function closure, `keep()` and `track()`. + +`keep()` ensures that the NDArray passed to keep will not be cleaned up +automatically when the scope ends. + +`track()` tracks any NDArrays that you may construct directly inside of a +scope. When the scope ends, any manually tracked `NDArray`s will get +cleaned up. Results of all `math.method()` functions, as well as results of +many other core library functions are automatically cleaned up, so you don't +have to manually track them. + +```ts +const math = new NDArrayMathGPU(); + +let output; + +// You must have an outer scope, but don't worry, the library will throw an +// error if you don't have one. +math.scope((keep, track) => { + // CORRECT: By default, math wont track NDArrays that are constructed + // directly. You can call track() on the NDArray for it to get tracked and + // cleaned up at the end of the scope. + const a = track(Scalar.new(2)); + + // INCORRECT: This is a texture leak!! + // math doesn't know about b, so it can't track it. When the scope ends, the + // GPU-resident NDArray will not get cleaned up, even though b goes out of + // scope. Make sure you call track() on NDArrays you create. + const b = Scalar.new(2); + + // CORRECT: By default, math tracks all outputs of math functions. + const c = math.neg(math.exp(a)); + + // CORRECT: d is tracked by the parent scope. + const d = math.scope(() => { + // CORRECT: e will get cleaned up when this inner scope ends. + const e = track(Scalar.new(3)); + + // CORRECT: The result of this math function is tracked. Since it is the + // return value of this scope, it will not get cleaned up with this inner + // scope. However, the result will be tracked automatically in the parent + // scope. + return math.elementWiseMul(e, e); + }); + + // CORRECT, BUT BE CAREFUL: The output of math.tanh will be tracked + // automatically, however we can call keep() on it so that it will be kept + // when the scope ends. That means if you are not careful about calling + // output.dispose() some time later, you might introduce a texture memory + // leak. A better way to do this would be to return this value as a return + // value of a scope so that it gets tracked in a parent scope. + output = keep(math.tanh(d)); +}); +``` + +> More technical details: When WebGL textures go out of scope in JavaScript, +they don't get cleaned up automatically by the browser's garbage collection +mechanism. This means when you are done with an NDArray that is GPU-resident, +it must manually be disposed some time later. If you forget to manually call +`ndarray.dispose()` when you are done with an NDArray, you will introduce +a texture memory leak, which will cause serious performance issues. +If you use `math.scope()`, any NDArrays created by `math.method()` and +any other method that returns the result through a scope will automatically +get cleaned up. + + +> If you want to do manual memory management and not use math.scope(), you can +construct a `NDArrayMath` object with safeMode = false. This is not +recommended, but is useful for `NDArrayMathCPU` since CPU-resident memory +will get cleaned up automatically by the JavaScript garbage collector. + + +#### NDArrayMathCPU + +When using CPU implementations, these mathematical +operations are blocking and get executed immediately on the underlying +`TypedArray`s with vanilla JavaScript. + +### Training + +Differentiable data flow graphs in **deeplearn.js** use a delayed execution model, +just like in TensorFlow. Users construct a graph and then train or +infer on them by providing input `NDArray`s through `FeedEntry`s. + +> NOTE: NDArrayMath and NDArrays are sufficient for inference mode. You only need a +graph if you want to train. + +#### Graphs and Tensors +The `Graph` object is the core class for constructing data flow graphs. +`Graph` objects don't actually hold `NDArray` data, only connectivity +between operations. + +The `Graph` class has differentiable operations as top level member +functions. When you call a graph method to add an operation, you get back a +`Tensor` object which only holds connectivity and shape information. + +An example graph that multiplies an input by a variable: + +```js +const g = new Graph(); + +// Placeholders are input containers. This is the container for where we will +// feed an input NDArray when we execute the graph. +const inputShape = [3]; +const inputTensor = g.placeholder('input', inputShape); + +const labelShape = [1]; +const inputTensor = g.placeholder('label', labelShape); + +// Variables are containers that hold a value that can be updated from training. +// Here we initialize the multiplier variable randomly. +const multiplier = g.variable('multiplier', Array2D.randNormal([1, 3])); + +// Top level graph methods take Tensors and return Tensors. +const outputTensor = g.matmul(multiplier, inputTensor); +const costTensor = g.meanSquaredCost(outputTensor, labelTensor); + +// Tensors, like NDArrays, have a shape attribute. +console.log(outputTensor.shape); +``` + +#### Session and FeedEntry + +Session objects are what drive the execution of `Graph`s. `FeedEntry` +(similar to TensorFlow `feed_dict`) are what provide data for the run, +feeding a value to a `Tensor` from a given NDArray. + +> A quick note on batching: **deeplearn.js** hasn't yet implemented batching as an outer +dimension for operations. This means every top level graph op, as well as math +function, operate on single examples. However, batching is important so that +weight updates operate on the average of gradients over a batch. **deeplearn.js** +simulates batching by using an `InputProvider` in train `FeedEntry`s to +provide inputs, rather than `NDArray`s directly. The `InputProvider` +will get called for each item in a batch. We provide a +`InMemoryShuffledInputProviderBuilder` for shuffling a set of inputs and +keeping them in sync. + +Training with the `Graph` object from above: + +```js +const learningRate = .001; +const batchSize = 2; + +const math = new NDArrayMathGPU(); +const session = new Session(g, math); +const optimizer = new SGDOptimizer(learningRate); + +const inputs: Array1D[] = [ + Array1D.new([1.0, 2.0, 3.0]), + Array1D.new([10.0, 20.0, 30.0]), + Array1D.new([100.0, 200.0, 300.0]) +]; + +const labels: Array1D[] = [ + Array1D.new([2.0, 6.0, 12.0]), + Array1D.new([20.0, 60.0, 120.0]), + Array1D.new([200.0, 600.0, 1200.0]) +]; + +// Shuffles inputs and labels and keeps them mutually in sync. +const shuffledInputProviderBuilder = + new InCPUMemoryShuffledInputProviderBuilder([inputs, labels]); +const [inputProvider, labelProvider] = + shuffledInputProviderBuilder.getInputProviders(); + +// Maps tensors to InputProviders. +const feedEntries: FeedEntry[] = [ + {tensor: inputTensor, data: inputProvider}, + {tensor: labelTensor, data: labelProvider} +]; + +// Wrap session.train in a scope so the cost gets cleaned up automatically. +math.scope(() => { + // Train takes a cost tensor to minimize. Trains one batch. Returns the + // average cost as a Scalar. + const cost = session.train( + costTensor, feedEntries, batchSize, optimizer, CostReduction.MEAN); + + console.log('last average cost: ' + cost.get()); +}); +``` + +After training, we can infer through the graph: + +```js + +// Wrap session.eval in a scope so the intermediate values get cleaned up +// automatically. +math.scope((keep, track) => { + const testInput = track(Array1D.new([1.0, 2.0, 3.0])); + + // session.eval can take NDArrays as input data. + const testFeedEntries: FeedEntry[] = [ + {tensor: inputTensor, data: testInput} + ]; + + const testOutput = session.eval(outputTensor, testFeedEntries); + + console.log('inference output:'); + console.log(testOutput.shape); + console.log(testOutput.getValues()); +}); +``` + +Want to learn more? Read [these tutorials](index.md). diff --git a/docs/tutorials/ml_beginners.md b/docs/tutorials/ml_beginners.md new file mode 100644 index 0000000000..134f558013 --- /dev/null +++ b/docs/tutorials/ml_beginners.md @@ -0,0 +1,303 @@ +--- +layout: page +order: 3 +--- +# Guide for non-ML experts + +* TOC +{:toc} + +### NDArrays, Tensors, and numbers + +#### Mathematical tensors + +Mathematically, a "tensor" is the most basic object of linear algebra, a +generalization of numbers, vectors, and matrices. A vector can be thought of as +a 1-dimensional list of numbers; a matrix as a 2-dimensional list of numbers. A +tensor simply generalizes that concept to n-dimensional lists of numbers. It is +any arrangement of component numbers (or even strings or other data types) +arranged in any multi-dimensional rectangular array. + +A tensor has several properties: + +* It has a type, which describes the types of each of its components, + for instance `integer`, `float`, etc. **deeplearn.js** only supports + `float32` for now. +* It has a shape, a list of integers which describes the shape of the + rectangular array of components. When you say a matrix is "4 by 4", you are + describing the shape of the matrix. +* It has a rank, which is the length of its shape; the dimension of the + array of components. A vector has rank 1; a matrix has rank 2. + +Example tensor | Type | Shape | Rank +------------------ | ----- | -------- | ---- +Scalar: 3.0 | float | \[\] | 0 +Vector: (1, 5, -2) | int | \[3\] | 1 +2x2 Matrix | int | \[2, 2\] | 2 + +#### number[], NDArray, Tensor: Three data types for mathematical tensors + +This same object a mathematician would call a "tensor" is represented in three +different ways in **deeplearn.js**. The above discussion (ranks, shapes, and +types), applies to all of them, but they are different and it is important to +keep them straight: + +* `number[]` is the underlying javascript type corresponding to an + array of numbers. Really it is `number` for a 0-rank tensor, `number[]` for + a 1-rank tensor, `number[][]` for 2-rank, etc. You won't use it much within + **deeplearn.js**, but it is the way you will get things in and out of + **deeplearn.js**. +* `NDArray` is **deeplearn.js**'s more powerful implementation. + Calculations involving NDArrays can be performed on the client's GPU, which + is the fundamental advantage of **deeplearn.js**. This is the format most + actual tensor data will be in when the calculations ultimately happen. When + you call `Session.eval`, for instance, this is what you get back (and what + the `FeedEntry` inputs should be). You can convert between `NDArray` and + `number[]` using `NDArray.new(number[])` and `NDArray.get([indices])` +* `Tensor` is an empty bag; it has no actual data inside. It is a + placeholder used when a Graph is constructed, which records the shape and + type of the data that will ultimately fit inside it. It contains no actual + values in its components. Just by knowing the shape and type, however, it + can do important error-catching at Graph-construction time; if you want to + multiply a 2x3 matrix by a 10x10 matrix, the graph can yell at you when you + create the node, before you ever give it input data. It does not make sense + to convert directly between a `Tensor` and an `NDArray` or `number[]`; if + you find you are trying to do that, one of the following is probably true: + * You have a static `NDArray` that you want to use in a graph; use + graph.constant() to create a constant ```Tensor``` node. + * You have an `NDArray` that you want to feed in as an input to the Graph. + Create a Placeholder with ```graph.placeholder``` in the graph, and then + send your input to the graph in the `FeedEntry`. + * You have an output tensor and you want the session to evaluate and + return its value. Call `Session.eval(tensor)`. + + +In general, you should only use a `Graph` when you want automatic +differentiation (training). If you just want to use the library for forward +mode inference, or just general numeric computation, using `NDArray`s with +`NDArrayMath` will suffice. + +If you are interested in training, you must use a `Graph`. When you +construct a graph you will be working with `Tensor`s, and when you execute it +with `Session.eval`, the result will be `NDArray`s. + +### Forward mode inference / numeric computation + +If you just want to perform mathematical operations on NDArrays, you can simply +construct `NDArray`s with your data, and perform operations on them with +a `NDArrayMath` object. + +For example, if you want to compute a matrix times a vector on the GPU: +```ts +const math = new NDArrayMathGPU(); + +math.scope((keep, track) => { + const matrixShape = [2, 3]; // 2 rows, 3 columns. + const matrix = track(Array2D.new(matrixShape, [10, 20, 30, 40, 50, 60])); + const vector = track(Array1D.new([0, 1, 2])); + const result = math.matrixTimesVector(matrix, vector); + + console.log("result shape:", result.shape); + console.log("result", result.getValues()); +}); +``` + +For more information on `NDArrayMath`, `keep`, and `track`, see +[Introduction and core concepts](intro.md). + +The `NDArray`/`NDArrayMath` layer can be thought of as analogous to +[NumPy](http://www.numpy.org/). + +### Training: delayed execution, Graphs, and Sessions + +The most important thing to understand about training +(automatic differentiation) in **deeplearn.js** is that it uses a delayed +execution model. Your code will contain two separate stages: first you will +build a `Graph`, the object representing the calculation you want to do, then +later you will execute the graph and get the results. + +Most of the time, your `Graph` will transform some input(s) to some output(s). +In general, the architecture of your `Graph` will stay fixed, but it will +contain parameters that will be automatically updated. + +When you execute a `Graph`, there are two modes: training, and inference. + +Inference is the act of providing a `Graph` an input to produce an output. + +Training a graph involves providing the `Graph` many examples of labeled +input / output pairs, and automatically updating parameters of the `Graph` +so that the output of the `Graph` when evaluating (inferring) an input is +closer to the labelled output. The function that gives a `Scalar` representing +how close labelled output to a generated output is called the "cost function" +(also known as a "loss function"). The loss function should output close to +zero when the model is performing well. You must provide a cost function when +training. + +**deeplearn.js** is structured very similarly to +[Tensorflow](https://www.tensorflow.org), Google's python-based machine +learning language. If you know TensorFlow, the concepts of `Tensor`s, `Graph`s, +and `Session`s are all almost the same, however we assume no knowledge of +TensorFlow here. + +#### Graphs as Functions + +The difference can be understood by analogy to regular JavaScript code. For the +rest of this tutorial, we will be working with this quadratic equation: + +```ts +// y = a * x^2 + b * x + c +const x = 4; +const a = Math.random(); +const b = Math.random(); +const c = Math.random(); + +const order2 = a * Math.pow(x, 2); +const order1 = b * x; +const y = order2 + order1 + c; +``` + +In this original code, the mathematical calculations are evaluated in each line +as it's processed immediately. + +Contrast that with the following code, analogous to how **deeplearn.js** +`Graph` inference works. + +```ts +function graph(x, a, b, c) { + const order2 = a * Math.pow(x, 2); + const order1 = b * x; + return order2 + order1 + c; +} + +const a = Math.random(); +const b = Math.random(); +const c = Math.random(); +const y = graph(4, a, b, c); +``` + +This code is in two blocks: first setting up the graph function, and then +calling it. The code to set up the graph function in the first few lines does +not do any actual mathematical operations until the function is called in the +final line. During the setup, basic compiler type-safety errors can be caught +even though the calculations are not yet performed. + +This is exactly analogous to how `Graph`s work in **deeplearn.js**. The first +part of your code will set up the graph, describing: + + * Inputs, in our case "x". Inputs are represented as placeholders + (e.g. `graph.placeholder()`). + * Outputs, in our case "order1", "order2", and the final output "y". + * Operations to produce outputs, in our case the decomponsed functions of the + quadratic (x^2, multiplication, addition). + * Updatable parameters, in our case "a", "b", "c". Updatable parameters are + represented as variables (e.g. `graph.variable()`) + + +Then in a later part of your code you will "call" (`Session.eval`) the graph's +function on certain inputs, and you will learn the values for "a", "b", and +"c", for some data with `Session.train`. + +One minor difference between the above function analogy and **deeplearn.js** +`Graph`s is that the `Graph` does not specify its output. Instead, the caller +of the `Graph` function specifies which of the tensors they want to be +returned. This allows different calls to the same `Graph` to execute different +parts of it. Only the parts necessary to obtain the results demanded by the +caller will be evaluated. + +Inference and training of a `Graph` is driven by a `Session` object. This +object contains runtime state, weights, activations, and gradients +(derivatives), whereas the `Graph` object only holds connectivity information. + +So the function above would be implemented in **deeplearn.js** as follows: + +```ts +const graph = new Graph(); +// Make a new input in the graph, called 'x', with shape [] (a Scalar). +const x: Tensor = graph.placeholder('x', []); +// Make new variables in the graph, 'a', 'b', 'c' with shape [] and random +// initial values. +const a: Tensor = graph.variable('a', Scalar.new(Math.random())); +const b: Tensor = graph.variable('b', Scalar.new(Math.random())); +const c: Tensor = graph.variable('c', Scalar.new(Math.random())); +// Make new tensors representing the output of the operations of the quadratic. +const order2: Tensor = graph.multiply(a, graph.square(x)); +const order1: Tensor = graph.multiply(b, x); +const y: Tensor = graph.add(graph.add(order2, order1), c); + +// When training, we need to provide a label and a cost function. +const yLabel: Tensor = graph.placeholder('y label', []); +// Provide a mean squared cost function for training. cost = (y - yLabel)^2 +const cost: Tensor = graph.meanSquaredCost(y, yLabel); + +// At this point the graph is set up, but has not yet been evaluated. +// **deeplearn.js** needs a Session object to evaluate a graph. +const math = new NDArrayMathGPU(); +const session = new Session(graph, math); + +math.scope((keep, track) => { + /** + * Inference + */ + // Now we ask the graph to evaluate (infer) and give us the result when + // providing a value 4 for "x". + // NOTE: "a", "b", and "c" are randomly intialized, so this will give us + // something random. + const result: NDArray = + session.eval(y, [{tensor: x, data: track(Scalar.new(4))}]); + + /** + * Training + */ + // Now let's learn the coeffiecients of this quadratic given some data. + // To do this, we need to provide examples of x and y. + // The values given here are for values a = 3, b = 2, c = 1, with random + // noise added to the output so it's not a perfect fit. + const xs: Scalar[] = [ + track(Scalar.new(0)), + track(Scalar.new(1)), + track(Scalar.new(2)), + track(Scalar.new(3)) + ]; + const ys: Scalar[] = [ + track(Scalar.new(1.1)), + track(Scalar.new(5.9)), + track(Scalar.new(16.8)), + track(Scalar.new(33.9)) + ]; + // When training, it's important to shuffle your data! + const shuffledInputProviderBuilder = + new InCPUMemoryShuffledInputProviderBuilder([xs, ys]); + const [xProvider, yProvider] = + shuffledInputProviderBuilder.getInputProviders(); + + // Training is broken up into batches. + const NUM_BATCHES = 5; + const BATCH_SIZE = xs.length; + // Before we start training, we need to provide an optimizer. This is the + // object that is responsible for updating weights. The learning rate param + // is a value that represents how large of a step to make when updating + // weights. If this is too big, you may overstep and oscillate. If it is too + // small, the model may take a long time to train. + const LEARNING_RATE = .001; + const optimizer = new SGDOptimizer(LEARNING_RATE); + for (let i = 0; i < NUM_BATCHES; i++) { + // Train takes a cost tensor to minimize; this call trains one batch and + // returns the average cost of the batch as a Scalar. + const costValue = session.train( + cost, + // Map input providers to Tensors on the graph. + [{tensor: x, data: xProvider}, {tensor: y, data: yProvider}], + BATCH_SIZE, optimizer, CostReduction.MEAN); + + console.log('average cost: ' + costValue.get()); + } +}); +``` + +After the training the model, you can infer through the graph again to get a +value for "y" given an "x". + +Of course, in practice, you will not want to just use `Scalar` values. +**deeplearn.js** provides powerful hardware-accelerated linear algebra which +you can use for everything for image recognition, to text generation. See other +[tutorials](index.md) for more! diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000000..e1c29056b2 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,31 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +module.exports = function(config) { + config.set({ + frameworks: ['jasmine', 'karma-typescript'], + files: [ + {pattern: 'src/**/*.ts'} + ], + preprocessors: { + '**/*.ts': ['karma-typescript'], // *.tsx for React Jsx + }, + karmaTypescriptConfig: { + tsconfig: "tsconfig.json" + }, + reporters: ['progress', 'karma-typescript'], + browsers: ['Chrome'] + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000000..c8410b6be7 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "deeplearn", + "version": "0.1.0", + "description": "Hardware-accelerated JavaScript library for machine intelligence", + "private": false, + "main": "dist/src/index.js", + "types": "dist/src/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/PAIR-code/deeplearnjs.git" + }, + "devDependencies": { + "@types/jasmine": "~2.5.53", + "@types/polymer": "~1.1.31", + "bower": "~1.8.0", + "browserify": "~14.4.0", + "http-server": "~0.10.0", + "jasmine-core": "~2.6.4", + "karma": "~1.7.0", + "karma-chrome-launcher": "~2.2.0", + "karma-jasmine": "~1.1.0", + "karma-typescript": "~3.0.4", + "polymer-bundler": "~3.0.1", + "tsify": "~3.0.1", + "tslint": "~5.5.0", + "typedoc": "~0.7.2", + "typescript": "2.3.4", + "watchify": "~3.9.0" + }, + "scripts": { + "prep": "npm install && bower install && mkdirp dist", + "test": "karma start", + "lint": "tslint -p . --type-check -t verbose" + } +} diff --git a/scripts/build-demo b/scripts/build-demo new file mode 100755 index 0000000000..6a8354eede --- /dev/null +++ b/scripts/build-demo @@ -0,0 +1,28 @@ +#!/usr/bin/env node +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +const path = require('path'); +const spawn = require('child_process').spawn; + +const startTsFilePath = process.argv[2]; +const outputPath = path.join(path.dirname(startTsFilePath), 'bundle.js') + +const cmd = `node_modules/.bin/browserify`; +const child = spawn(cmd, [startTsFilePath, '-p', '[tsify]', '-o' , outputPath], + {detached: false}); +child.stdout.pipe(process.stdout); +child.stderr.pipe(process.stderr); +child.on('close', () => console.log(`Stored bundle in ${outputPath}`)); diff --git a/scripts/build-npm.sh b/scripts/build-npm.sh new file mode 100755 index 0000000000..e38b6e856d --- /dev/null +++ b/scripts/build-npm.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================= +node_modules/.bin/tsc +cd dist/ +npm pack ../ +cd .. +echo 'Stored npm package at dist/deeplearn-version.tgz' diff --git a/scripts/build-standalone.sh b/scripts/build-standalone.sh new file mode 100755 index 0000000000..a8b483603a --- /dev/null +++ b/scripts/build-standalone.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================= +mkdir -p dist/ +node_modules/.bin/browserify --standalone deeplearn src/index.ts -p [tsify] > dist/deeplearn.js +echo 'Stored standalone library at dist/deeplearn.js' diff --git a/scripts/convert_uint8_tensor_to_png.py b/scripts/convert_uint8_tensor_to_png.py new file mode 100644 index 0000000000..8fcdcb9f62 --- /dev/null +++ b/scripts/convert_uint8_tensor_to_png.py @@ -0,0 +1,63 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Converts an array of 3D tensors to pngs.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import os +import numpy as np +from PIL import Image + +FLAGS = None + + +def main(): + fpath = os.path.expanduser(FLAGS.uint8_tensor_file) + with open(fpath, 'rb') as f: + # a has shape N x Width x Height x Channels + a = np.frombuffer(f.read(), np.uint8).reshape( + [-1, FLAGS.size, FLAGS.size, FLAGS.num_channels]) + + print('Read', a.shape[0], 'images') + print('min/max pixel values: ', a.min(), '/', a.max()) + + # Make each image take a single row in the big batch image by flattening the + # width (2nd) and height (3rd) dimension. + # a has shape N x (Width*Height) x Channels. + a = a.reshape([a.shape[0], -1, FLAGS.num_channels]).squeeze() + im = Image.fromarray(a) + im.save(fpath + '.png') + print('Saved image with width/height', im.size, 'at', fpath + '.png') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--uint8_tensor_file', + type=str, + required=True, + help='File path to the binary uint8 tensor to convert to png') + parser.add_argument( + '--size', type=int, required=True, help='Width/Height of each image') + parser.add_argument( + '--num_channels', type=int, required=True, help='Number of channelse') + FLAGS, unparsed = parser.parse_known_args() + if unparsed: + print('Error, unrecognized flags:', unparsed) + exit(-1) + main() diff --git a/scripts/deploy-demo b/scripts/deploy-demo new file mode 100755 index 0000000000..7975a2cbbe --- /dev/null +++ b/scripts/deploy-demo @@ -0,0 +1,36 @@ +#!/usr/bin/env node +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +const path = require('path'); +const spawn = require('child_process').spawn; + +const startTsFilePath = process.argv[2]; +const startHTMLFilePath = process.argv[3]; +const outDir = process.argv[4]; + +const cmd = `scripts/build-demo`; +const child = spawn(cmd, [startTsFilePath], {detached: false}); +child.stdout.pipe(process.stdout); +child.stderr.pipe(process.stderr); +child.on('close', () => { + const bundlePath = path.join(outDir, path.basename(startHTMLFilePath)); + const cmd = `node_modules/.bin/polymer-bundler`; + const child = spawn(cmd, ['--inline-scripts', '--inline-css', + '--out-html', bundlePath, startHTMLFilePath], {detached: false}); + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); + child.on('close', () => console.log(`Saved bundled demo at ${bundlePath}`)); +}); diff --git a/scripts/dump_checkpoint_vars.py b/scripts/dump_checkpoint_vars.py new file mode 100644 index 0000000000..9255892b5c --- /dev/null +++ b/scripts/dump_checkpoint_vars.py @@ -0,0 +1,100 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""A script to dump tensorflow checkpoint variables to deeplearnjs. + +This script takes a checkpoint file and writes all of the variables in the +checkpoint to a directory. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import json +import os +import re +import string +import tensorflow as tf + +FLAGS = None +FILENAME_CHARS = string.ascii_letters + string.digits + '_' + + +def _var_name_to_filename(var_name): + chars = [] + for c in var_name: + if c in FILENAME_CHARS: + chars.append(c) + elif c == '/': + chars.append('_') + return ''.join(chars) + + +def main(): + chk_fpath = os.path.expanduser(FLAGS.checkpoint_file) + reader = tf.train.NewCheckpointReader(chk_fpath) + var_to_shape_map = reader.get_variable_to_shape_map() + output_dir = os.path.expanduser(FLAGS.output_dir) + if not os.path.exists(output_dir): + os.mkdirs(output_dir) + manifest = {} + remove_vars_compiled_re = re.compile(FLAGS.remove_variables_regex) + + var_filenames_strs = [] + for name in var_to_shape_map: + if (FLAGS.remove_variables_regex and + re.match(remove_vars_compiled_re, name)) or name == 'global_step': + print('Ignoring ' + name) + continue + var_filename = _var_name_to_filename(name) + manifest[name] = {'filename': var_filename, 'shape': var_to_shape_map[name]} + + print('Writing variable ' + name + '...') + tensor = reader.get_tensor(name) + with open(os.path.join(output_dir, var_filename), 'w') as f: + f.write(tensor.tobytes()) + + var_filenames_strs.append("\"" + var_filename + "\"") + + manifest_fpath = os.path.join(output_dir, 'manifest.json') + print('Writing manifest to ' + manifest_fpath) + with open(manifest_fpath, 'w') as f: + f.write(json.dumps(manifest, indent=2, sort_keys=True)) + print('Done!') + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--checkpoint_file', + type=str, + required=True, + help='Path to the model checkpoint') + parser.add_argument( + '--output_dir', + type=str, + required=True, + help='The output directory where to store the converted weights') + parser.add_argument( + '--remove_variables_regex', + type=str, + default='', + help='A regular expression to match against variable names that should ' + 'not be included') + FLAGS, unparsed = parser.parse_known_args() + if unparsed: + print('Error, unrecognized flags:', unparsed) + exit(-1) + main() diff --git a/scripts/make-website.sh b/scripts/make-website.sh new file mode 100755 index 0000000000..f00bd24617 --- /dev/null +++ b/scripts/make-website.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================= +TMP_DIR="/tmp/deeplearn-website" + +npm run prep +rm -rf "$TMP_DIR" +mkdir "$TMP_DIR" + +cp -r "docs" "$TMP_DIR/" +cp "README.md" "$TMP_DIR/" + +# Make the documentation. +./node_modules/.bin/typedoc --out "$TMP_DIR/docs/api/" --excludeExternals \ + --excludeNotExported --excludePrivate --mode file --tsconfig tsconfig-doc.json + +# Build the demos (deploy-demo vulcanizes polymer apps). +cp -r "demos" "$TMP_DIR/" +./scripts/deploy-demo demos/model-builder/model-builder.ts \ + demos/model-builder/model-builder-demo.html $TMP_DIR/demos/model-builder/ +./scripts/deploy-demo demos/imagenet/imagenet-demo.ts \ + demos/imagenet/imagenet-demo.html $TMP_DIR/demos/imagenet +./scripts/deploy-demo demos/nn-art/nn-art.ts \ + demos/nn-art/nn-art-demo.html $TMP_DIR/demos/nn-art +./scripts/deploy-demo demos/benchmarks/math-benchmark.ts \ + demos/benchmarks/benchmark-demo.html $TMP_DIR/demos/benchmarks + +# Build the homepage (no deploy since homepage is not polymer). +./scripts/build-demo demos/homepage/index.ts +cp -r demos/homepage/* "$TMP_DIR" + +git stash +git checkout gh-pages + +cp -rf "$TMP_DIR"/* . + +git add . +git commit -m "github pages" + +git checkout master +git stash pop diff --git a/scripts/watch-demo b/scripts/watch-demo new file mode 100755 index 0000000000..c805e46cc2 --- /dev/null +++ b/scripts/watch-demo @@ -0,0 +1,33 @@ +#!/usr/bin/env node +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= + +const path = require('path'); +const spawn = require('child_process').spawn; + +const startTsFilePath = process.argv[2]; +const outputPath = path.join(path.dirname(startTsFilePath), 'bundle.js') + +const cmd = `node_modules/.bin/watchify`; +const watchify = spawn(cmd, [startTsFilePath, '-p', '[tsify]', '-v', '--debug', + '-o' , outputPath], {detached: false}); +watchify.stdout.pipe(process.stdout); +watchify.stderr.pipe(process.stderr); + +const httpServer = spawn('node_modules/.bin/http-server', ['-c-1'], { + detached: false +}); +httpServer.stdout.pipe(process.stdout); +httpServer.stderr.pipe(process.stderr); diff --git a/src/checkpoint_loader.ts b/src/checkpoint_loader.ts new file mode 100644 index 0000000000..77e215255a --- /dev/null +++ b/src/checkpoint_loader.ts @@ -0,0 +1,137 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArray} from './math/ndarray'; + +/** + * @hidden + */ +export interface CheckpointVariable { + filename: string; + shape: number[]; +} + +/** + * @hidden + */ +export type CheckpointManifest = { + [varName: string]: CheckpointVariable +}; + +const MANIFEST_FILE = 'manifest.json'; + +export class CheckpointLoader { + private checkpointManifest: CheckpointManifest; + private variables: {[varName: string]: NDArray}; + + constructor(private urlPath: string) { + if (this.urlPath.charAt(this.urlPath.length - 1) !== '/') { + this.urlPath += '/'; + } + } + + private loadManifest(): Promise { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', this.urlPath + MANIFEST_FILE); + + xhr.onload = () => { + this.checkpointManifest = JSON.parse(xhr.responseText); + resolve(); + }; + xhr.onerror = (error) => { + throw new Error( + `${MANIFEST_FILE} not found at ${this.urlPath}. ` + error); + }; + xhr.send(); + }); + } + + getCheckpointManifest(): Promise { + if (this.checkpointManifest == null) { + return new Promise((resolve, reject) => { + this.loadManifest().then(() => { + resolve(this.checkpointManifest); + }); + }); + } + return new Promise((resolve, reject) => { + resolve(this.checkpointManifest); + }); + } + + getAllVariables(): Promise<{[varName: string]: NDArray}> { + if (this.variables != null) { + return new Promise<{[varName: string]: NDArray}>((resolve, reject) => { + resolve(this.variables); + }); + } + + return new Promise<{[varName: string]: NDArray}>((resolve, reject) => { + this.getCheckpointManifest().then( + (checkpointDefinition: CheckpointManifest) => { + const variableNames = Object.keys(this.checkpointManifest); + + const variablePromises: Array> = []; + for (let i = 0; i < variableNames.length; i++) { + variablePromises.push(this.getVariable(variableNames[i])); + } + + Promise.all(variablePromises).then(variables => { + this.variables = {}; + for (let i = 0; i < variables.length; i++) { + this.variables[variableNames[i]] = variables[i]; + } + resolve(this.variables); + }); + }); + }); + } + + getVariable(varName: string): Promise { + if (!(varName in this.checkpointManifest)) { + throw new Error('Cannot load non-existant variable ' + varName); + } + + const variableRequestPromiseMethod = + (resolve: (ndarray: NDArray) => void, reject: () => void) => { + const xhr = new XMLHttpRequest(); + xhr.responseType = 'arraybuffer'; + const fname = this.checkpointManifest[varName].filename; + xhr.open('GET', this.urlPath + fname); + + xhr.onload = () => { + const values = new Float32Array(xhr.response); + const ndarray = + NDArray.make(this.checkpointManifest[varName].shape, {values}); + resolve(ndarray); + }; + xhr.onerror = (error) => { + throw new Error( + 'Could not fetch variable ' + varName + ': ' + error); + }; + xhr.send(); + }; + + if (this.checkpointManifest == null) { + return new Promise((resolve, reject) => { + this.loadManifest().then(() => { + new Promise(variableRequestPromiseMethod).then(resolve); + }); + }); + } + return new Promise(variableRequestPromiseMethod); + } +} diff --git a/src/checkpoint_loader_test.ts b/src/checkpoint_loader_test.ts new file mode 100644 index 0000000000..852f3e8c9b --- /dev/null +++ b/src/checkpoint_loader_test.ts @@ -0,0 +1,99 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {CheckpointLoader, CheckpointManifest} from './checkpoint_loader'; + +describe('Checkpoint var loader', () => { + let xhrObj: XMLHttpRequest; + + beforeEach(() => { + xhrObj = jasmine.createSpyObj( + 'xhrObj', ['addEventListener', 'open', 'send', 'onload', 'onerror']); + // tslint:disable-next-line:no-any + spyOn(window as any, 'XMLHttpRequest').and.returnValue(xhrObj); + }); + + it('Load manifest and a variable', (doneFn) => { + const fakeCheckpointManifest: CheckpointManifest = { + 'fakeVar1': {filename: 'fakeFile1', shape: [10]}, + 'fakeVar2': {filename: 'fakeFile2', shape: [5, 5]} + }; + + const varLoader = new CheckpointLoader('fakeModel'); + varLoader.getCheckpointManifest().then(checkpoint => { + expect(checkpoint).toEqual(fakeCheckpointManifest); + + const buffer = + new ArrayBuffer(4 * fakeCheckpointManifest['fakeVar1'].shape[0]); + const view = new Float32Array(buffer); + for (let i = 0; i < 10; i++) { + view[i] = i; + } + + varLoader.getVariable('fakeVar1').then(ndarray => { + expect(ndarray.shape).toEqual(fakeCheckpointManifest['fakeVar1'].shape); + expect(ndarray.getValues()).toEqual(view); + doneFn(); + }); + // tslint:disable-next-line:no-any + (xhrObj as any).response = buffer; + // tslint:disable-next-line:no-any + (xhrObj as any).onload(); + }); + // tslint:disable-next-line:no-any + (xhrObj as any).responseText = JSON.stringify(fakeCheckpointManifest); + // tslint:disable-next-line:no-any + (xhrObj as any).onload(); + }); + + it('Load manifest error', () => { + const varLoader = new CheckpointLoader('fakeModel'); + varLoader.getCheckpointManifest(); + // tslint:disable-next-line:no-any + expect(() => (xhrObj as any).onerror()).toThrowError(); + }); + + it('Load non-existent variable throws error', (doneFn) => { + const fakeCheckpointManifest: + CheckpointManifest = {'fakeVar1': {filename: 'fakeFile1', shape: [10]}}; + + const varLoader = new CheckpointLoader('fakeModel'); + varLoader.getCheckpointManifest().then(checkpoint => { + expect(() => varLoader.getVariable('varDoesntExist')).toThrowError(); + doneFn(); + }); + // tslint:disable-next-line:no-any + (xhrObj as any).responseText = JSON.stringify(fakeCheckpointManifest); + // tslint:disable-next-line:no-any + (xhrObj as any).onload(); + }); + + it('Load variable throws error', (doneFn) => { + const fakeCheckpointManifest: + CheckpointManifest = {'fakeVar1': {filename: 'fakeFile1', shape: [10]}}; + + const varLoader = new CheckpointLoader('fakeModel'); + varLoader.getCheckpointManifest().then(checkpoint => { + varLoader.getVariable('fakeVar1'); + // tslint:disable-next-line:no-any + expect(() => (xhrObj as any).onerror()).toThrowError(); + doneFn(); + }); + // tslint:disable-next-line:no-any + (xhrObj as any).responseText = JSON.stringify(fakeCheckpointManifest); + // tslint:disable-next-line:no-any + (xhrObj as any).onload(); + }); +}); diff --git a/src/dataset.ts b/src/dataset.ts new file mode 100644 index 0000000000..c1d4762907 --- /dev/null +++ b/src/dataset.ts @@ -0,0 +1,266 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMath} from './math/math'; +import {NDArray} from './math/ndarray'; +import * as util from './util'; + +const STATS_SAMPLE_PERCENTAGE = 0.1; + +export interface DataStats { + exampleCount: number; + inputMin: number; + inputMax: number; + shape: number[]; +} + +interface NormalizationInfo { + isNormalized: boolean; + // Bounds of the normalization if normalized. + lowerBound?: number; + upperBound?: number; + // Minimum and maximum values for each dimension of the original data. These + // are the same size as an input example. These are computed lazily, only if + // normalization is requested. If the data is un-normalized, these are kept + // around so they don't have to be recomputed. + minValues: Float32Array; + maxValues: Float32Array; +} + +export abstract class InMemoryDataset { + protected dataset: NDArray[][]|null; + + // Contains information necessary for reconstruction of the original data + // after normalization. + private normalizationInfo: {[dataIndex: number]: NormalizationInfo}; + + constructor(protected dataShapes: number[][]) { + this.normalizationInfo = {}; + } + + getDataShape(dataIndex: number): number[] { + return this.dataShapes[dataIndex]; + } + + abstract fetchData(): Promise; + + getData(): NDArray[][]|null { + return this.dataset; + } + + getStats(): DataStats[] { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + + return this.dataset.map(d => this.getStatsForData(d)); + } + + // Computes stats across a sampled portion of the data. + private getStatsForData(data: NDArray[]): DataStats { + let inputMin = Number.POSITIVE_INFINITY; + let inputMax = Number.NEGATIVE_INFINITY; + + let exampleIndices = data.map((example, i) => i); + util.shuffle(exampleIndices); + exampleIndices = + exampleIndices.slice(exampleIndices.length * STATS_SAMPLE_PERCENTAGE); + + for (let i = 0; i < exampleIndices.length; i++) { + const inputValues = data[exampleIndices[i]].getValues(); + for (let j = 0; j < inputValues.length; j++) { + inputMin = Math.min(inputMin, inputValues[j]); + inputMax = Math.max(inputMax, inputValues[j]); + } + } + + return { + inputMin, + inputMax, + exampleCount: data.length, + shape: data[0].shape, + }; + } + + /** + * @param examples NDArrays to be normalized. + * @param curLowerBounds An array containing the minimum value for each + * dimension or a fixed minimum value. + * @param curUpperBounds An array containing the maximum value for each + * dimension or a fixed maximum value. + * @param newLowerBounds An array containing new minimum values for each + * dimension, or a fixed minumum value to normalize the data to. + * @param newUpperBounds An array containing new maximum values for each + * dimension, or a fixed maximum value to normalize the data to. + */ + private normalizeExamplesToRange( + examples: NDArray[], curLowerBounds: Float32Array|number, + curUpperBounds: Float32Array|number, newLowerBounds: Float32Array|number, + newUpperBounds: Float32Array|number): NDArray[] { + const curBoundsIsPerDimension = + (curUpperBounds instanceof Float32Array && + curLowerBounds instanceof Float32Array); + const newBoundsIsPerDimension = + (newLowerBounds instanceof Float32Array && + newUpperBounds instanceof Float32Array); + + const inputSize = util.sizeFromShape(examples[0].shape); + const newExamples: NDArray[] = []; + + examples.forEach(example => { + const inputValues = example.getValues(); + const normalizedValues = new Float32Array(inputSize); + for (let j = 0; j < inputSize; j++) { + const curLowerBound = curBoundsIsPerDimension ? + (curLowerBounds as Float32Array)[j] : + curLowerBounds as number; + const curUpperBound = curBoundsIsPerDimension ? + (curUpperBounds as Float32Array)[j] : + curUpperBounds as number; + const curRange = curUpperBound - curLowerBound; + + const newLowerBound = newBoundsIsPerDimension ? + (newLowerBounds as Float32Array)[j] : + newLowerBounds as number; + const newUpperBound = newBoundsIsPerDimension ? + (newUpperBounds as Float32Array)[j] : + newUpperBounds as number; + const newRange = newUpperBound - newLowerBound; + + if (curRange === 0) { + normalizedValues[j] = newLowerBound; + } else { + normalizedValues[j] = newLowerBound + + newRange * (inputValues[j] - curLowerBound) / curRange; + } + } + newExamples.push(NDArray.make(example.shape, {values: normalizedValues})); + }); + return newExamples; + } + + private computeBounds(dataIndex: number) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + + const size = util.sizeFromShape(this.dataset[dataIndex][0].shape); + + // Compute min and max values for every dimension. + this.normalizationInfo[dataIndex] = { + isNormalized: false, + minValues: new Float32Array(size), + maxValues: new Float32Array(size) + }; + + for (let i = 0; i < size; i++) { + this.normalizationInfo[dataIndex].minValues[i] = Number.POSITIVE_INFINITY; + this.normalizationInfo[dataIndex].maxValues[i] = Number.NEGATIVE_INFINITY; + } + + this.dataset[dataIndex].forEach(example => { + const inputValues = example.getValues(); + for (let k = 0; k < size; k++) { + this.normalizationInfo[dataIndex].minValues[k] = Math.min( + this.normalizationInfo[dataIndex].minValues[k], inputValues[k]); + this.normalizationInfo[dataIndex].maxValues[k] = Math.max( + this.normalizationInfo[dataIndex].maxValues[k], inputValues[k]); + } + }); + } + + normalizeWithinBounds( + dataIndex: number, lowerBound: number, upperBound: number) { + if (this.dataset == null) { + throw new Error('Data is null.'); + } + if (dataIndex >= this.dataset.length) { + throw new Error('dataIndex out of bounds.'); + } + + if (this.normalizationInfo[dataIndex] == null) { + this.computeBounds(dataIndex); + } + + // curLower/UpperBounds of the current data set can either be fixed numbers + // if the data has already been normalized, or curLower/Upper for each + // dimension if it hasn't been normalized yet. + let curLowerBounds: Float32Array|number; + let curUpperBounds: Float32Array|number; + + if (this.normalizationInfo[dataIndex].isNormalized) { + curLowerBounds = this.normalizationInfo[dataIndex].lowerBound!; + curUpperBounds = this.normalizationInfo[dataIndex].upperBound!; + } else { + curLowerBounds = this.normalizationInfo[dataIndex].minValues; + curUpperBounds = this.normalizationInfo[dataIndex].maxValues; + } + + this.dataset[dataIndex] = this.normalizeExamplesToRange( + this.dataset[dataIndex], curLowerBounds, curUpperBounds, lowerBound, + upperBound); + this.normalizationInfo[dataIndex].isNormalized = true; + this.normalizationInfo[dataIndex].lowerBound = lowerBound; + this.normalizationInfo[dataIndex].upperBound = upperBound; + } + + private isNormalized(dataIndex: number): boolean { + return this.normalizationInfo != null && + this.normalizationInfo[dataIndex].isNormalized; + } + + removeNormalization(dataIndex: number) { + if (this.dataset == null) { + throw new Error('Training or test data is null.'); + } + + if (!this.isNormalized(dataIndex)) { + return; + } + + this.dataset[dataIndex] = this.normalizeExamplesToRange( + this.dataset[dataIndex], this.normalizationInfo[dataIndex].lowerBound!, + this.normalizationInfo[dataIndex].upperBound!, + this.normalizationInfo[dataIndex].minValues, + this.normalizationInfo[dataIndex].maxValues); + this.normalizationInfo[dataIndex].isNormalized = false; + } + + unnormalizeExamples(examples: NDArray[], dataIndex: number): NDArray[] { + if (!this.isNormalized(dataIndex)) { + return examples; + } + + return this.normalizeExamplesToRange( + examples, this.normalizationInfo[dataIndex].lowerBound!, + this.normalizationInfo[dataIndex].upperBound!, + this.normalizationInfo[dataIndex].minValues, + this.normalizationInfo[dataIndex].maxValues); + } + + dispose() { + if (this.dataset == null) { + return; + } + + for (let i = 0; i < this.dataset.length; i++) { + for (let j = 0; j < this.dataset[i].length; j++) { + this.dataset[i][j].dispose(); + } + } + this.dataset = []; + } +} + diff --git a/src/dataset_test.ts b/src/dataset_test.ts new file mode 100644 index 0000000000..669db46fd2 --- /dev/null +++ b/src/dataset_test.ts @@ -0,0 +1,100 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {InMemoryDataset} from './dataset'; +import {Array1D, Array2D, NDArray} from './math/ndarray'; +import * as test_util from './test_util'; + +class StubDataset extends InMemoryDataset { + constructor(data: NDArray[][]) { + super(data.map(value => value[0].shape)); + this.dataset = data; + } + + fetchData(): Promise { + return new Promise((resolve, reject) => {}); + } +} + +describe('Dataset', () => { + it('normalize', () => { + const data = [ + [ + Array2D.new([2, 3], new Float32Array([1, 2, 10, -1, -2, .75])), + Array2D.new([2, 3], new Float32Array([2, 3, 20, -2, 2, .5])), + Array2D.new([2, 3], new Float32Array([3, 4, 30, -3, -4, 0])), + Array2D.new([2, 3], new Float32Array([4, 5, 40, -4, 4, 1])) + ], + [ + Array1D.randNormal([1]), Array1D.randNormal([1]), + Array1D.randNormal([1]), Array1D.randNormal([1]) + ] + ]; + const dataset = new StubDataset(data); + + // Normalize only the first data index. + const dataIndex = 0; + dataset.normalizeWithinBounds(dataIndex, 0, 1); + + let normalizedInputs = dataset.getData()![0]; + + test_util.expectArraysClose( + new Float32Array([0, 0, 0, 1, .25, .75]), + normalizedInputs[0].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([1 / 3, 1 / 3, 1 / 3, 2 / 3, .75, .5]), + normalizedInputs[1].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([2 / 3, 2 / 3, 2 / 3, 1 / 3, 0, 0]), + normalizedInputs[2].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([1, 1, 1, 0, 1, 1]), normalizedInputs[3].getValues(), + 1e-5); + + dataset.normalizeWithinBounds(dataIndex, -1, 1); + + normalizedInputs = dataset.getData()![0]; + + test_util.expectArraysClose( + new Float32Array([-1, -1, -1, 1, -.5, .5]), + normalizedInputs[0].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([-1 / 3, -1 / 3, -1 / 3, 1 / 3, .5, .0]), + normalizedInputs[1].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([1 / 3, 1 / 3, 1 / 3, -1 / 3, -1, -1]), + normalizedInputs[2].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([1, 1, 1, -1, 1, 1]), normalizedInputs[3].getValues(), + 1e-5); + + dataset.removeNormalization(dataIndex); + + normalizedInputs = dataset.getData()![0]; + + test_util.expectArraysClose( + new Float32Array([1, 2, 10, -1, -2, .75]), + normalizedInputs[0].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([2, 3, 20, -2, 2, .5]), + normalizedInputs[1].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([3, 4, 30, -3, -4, 0]), + normalizedInputs[2].getValues(), 1e-5); + test_util.expectArraysClose( + new Float32Array([4, 5, 40, -4, 4, 1]), normalizedInputs[3].getValues(), + 1e-5); + }); +}); diff --git a/src/graph.ts b/src/graph.ts new file mode 100644 index 0000000000..900f170298 --- /dev/null +++ b/src/graph.ts @@ -0,0 +1,905 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GraphLayers} from './graph_layers'; +import * as concat3d_util from './math/concat3d_util'; +import * as conv_util from './math/conv_util'; +import {NDArray, Scalar} from './math/ndarray'; +import * as util from './util'; + +/** + * Graph is the primary container structure for deeplearn.js operations. Graph + * holds the topology of operation nodes and the connectivity between them. + */ +export class Graph { + layers: GraphLayers; + + constructor() { + this.layers = new GraphLayers(this); + } + + /** + * Creates a named variable. Variables are tensors that maintain state across + * session calls and whose values are adjusted during backpropagation + * training. + * @param name The name of this variable. + * @param data The NDArray to associate with this variable tensor. + * @return The tensor representing the variable. + */ + variable(name: string, data: NDArray): Tensor { + return this.addNodeAndReturnOutput(new VariableNode(this, name, data)); + } + + /** + * Inserts a placeholder for a tensor that will be always fed. Placeholders + * are input tensors whose values are provided by the client via feed + * dictionaries. Placeholders are not updated as part of training; they are + * only used as immutable input. + * @param name The name of this placeholder. + * @param shape The shape of the placeholder tensor. + * @return The tensor representing the placeholder. + */ + placeholder(name: string, shape: number[]): Tensor { + return this.addNodeAndReturnOutput(new PlaceholderNode(this, name, shape)); + } + + /** + * Constant value that persists across session calls. + * @param value The value to return. + * @return A node outputing the constant value. + */ + constant(value: ArrayData): Tensor { + let finalValue: NDArray; + if (typeof value === 'number') { + finalValue = Scalar.new(value); + } else if (value instanceof NDArray) { + finalValue = value; + } else if (value instanceof Array) { + const vals = new Float32Array(util.flatten(value)); + finalValue = NDArray.make(util.inferShape(value), {values: vals}); + } else { + throw new Error('unimplemented constant type.'); + } + return this.addNodeAndReturnOutput(new ConstantNode(this, finalValue)); + } + + /** + * Reshape the input tensor. + * @param x The input tensor to be reshaped. + * @param shape The shape of the output tensor. + * @return The tensor representing the reshape operation. + */ + reshape(x: Tensor, shape: number[]): Tensor { + return this.addNodeAndReturnOutput( + new ReshapeNode(this, 'Reshape', x, shape)); + } + + /** + * Computes a fused linear combination of two tensors. + * @param x1 The first input tensor. + * @param x2 The second input tensor. Same shape as t1. + * @param c1 Coefficient of t1. Must be size 1. + * @param c2 Coefficient of t2. Must be size 1. + * @return The tensor representing c1*t1+c2*t2. + */ + fusedLinearCombination(x1: Tensor, x2: Tensor, c1: Tensor, c2: Tensor): + Tensor { + return this.addNodeAndReturnOutput( + new FusedLinearCombinationNode(this, x1, x2, c1, c2)); + } + + + /** + * Adds two tensors (elementwise). Broadcasts if one of the tensors is scalar. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing t1+t2. + */ + add(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new AddNode(this, x1, x2)); + } + + /** + * Subtracts two tensors (elementwise). Broadcasts if one of the tensors is + * scalar. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing t1-t2. + */ + subtract(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new SubtractNode(this, x1, x2)); + } + + /** + * Multiply two tensors (elementwise). Broadcasts if one of the tensors is + * scalar. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing t1*t2. + */ + multiply(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new MultiplyNode(this, x1, x2)); + } + + /** + * Divide two tensors (elementwise). Broadcasts if one of the tensors is + * scalar. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing t1 / t2. + */ + divide(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new DivideNode(this, x1, x2)); + } + + /** + * Computes the sum of elements in the tensor. + * @param x The input tensor. + */ + reduceSum(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new ReduceSumNode(this, x)); + } + + /** + * Concats two 3D tensors along a given axis. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing concat of two tensors along axis. + */ + concat3d(x1: Tensor, x2: Tensor, axis: number): Tensor { + return this.addNodeAndReturnOutput(new Concat3DNode(this, x1, x2, axis)); + } + + /** + * Computes the dot product between two matrices. + * @param x1 The first input tensor. + * @param x2 The second input tensor. + * @return The tensor representing the dot product of x1 and x2. + */ + matmul(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new MatMulNode(this, x1, x2)); + } + + /** + * Computes a 2D convolution. + * @param x The input tensor to the convolution operation. + * @param w The weight tensor used by the convolution operation. + * @param b The bias tensor used by the convolution operation. + * @param fieldSize The size of the convolutional kernel. + * @param outputDepth The output depth of the convolution operation. + * @param stride The stride of the convolution operation. + * @param zeroPad The amount of zero padding on all sides of the input tensor. + * @return The tensor representing the convolution operation. + */ + conv2d( + x: Tensor, w: Tensor, b: Tensor, fieldSize: number, outputDepth: number, + stride = 1, zeroPad?: number): Tensor { + return this.addNodeAndReturnOutput(new Convolution2DNode( + this, x, w, b, fieldSize, outputDepth, stride, zeroPad)); + } + + /** + * Computes a 2D max pool of x. + * @param x The input tensor to the max pool operation. + * @param fieldSize The size of the convolutional kernel. + * @param stride The stride of the convolution operation. + * @param zeroPad The amount of zero padding on all sides of the input tensor. + * @return The tensor representing the max pool operation. + */ + maxPool(x: Tensor, fieldSize: number, stride = 1, zeroPad?: number): Tensor { + return this.addNodeAndReturnOutput( + new MaxPoolNode(this, x, fieldSize, stride, zeroPad)); + } + + /** + * Computes exponential of x element-wise. + * @param x The input tensor to the exp. + * @return The tensor representing the e ^ x operation. + */ + exp(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new ExpNode(this, x)); + } + + /** + * Computes log of x element-wise. + * @param x The input tensor to the log. + * @return The tensor representing the ln(x) operation. + */ + log(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new LogNode(this, x)); + } + + /** + * Computes ReLU of x element-wise. + * @param x The input tensor to the ReLU. + * @return The tensor representing the ReLU operation. + */ + relu(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new ReLUNode(this, x)); + } + + /** + * Computes TanH of x element-wise. + * @param x The input tensor to the TanH. + * @return The tensor representing the TanH operation. + */ + tanh(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new TanHNode(this, x)); + } + + /** + * Computes Sigmoid of x element-wise. + * @param x The input tensor to the sigmoid. + * @return The tensor representing the sigmoid operation. + */ + sigmoid(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new SigmoidNode(this, x)); + } + + /** + * Computes square of x element-wise. + * @param x The input tensor to the square. + */ + square(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new SquareNode(this, x)); + } + + /** + * Computes softmax probabilities from logits. + * + * @param x The input logits. + * @return The softmax probabilities. + */ + softmax(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new SoftmaxNode(this, x)); + } + + /** + * Creates a softmax cross-entropy cost operation in the graph. + * @param x The input tensor to classify. + * @return The tensor representing the softmax cross-entropy cost operation. + */ + softmaxCrossEntropyCost(x: Tensor, target: Tensor): Tensor { + return this.addNodeAndReturnOutput( + new SoftmaxCrossEntropyCostNode(this, x, target)); + } + + /** + * Creates a mean-squared cost operation in the graph. + * @param label The label tensor. + * @param prediction The prediction tensor. + * @return The tensor representing the mean-squared cost operation. + */ + meanSquaredCost(label: Tensor, prediction: Tensor) { + return this.addNodeAndReturnOutput( + new MeanSquaredCostNode(this, label, prediction)); + } + + /** + * Returns the flattened index of the maximum entry in the tensor. + * @param x The tensor with the value. + * @return A Scalar tensor with the index of the maximum entry. + */ + argmax(x: Tensor): Tensor { + return this.addNodeAndReturnOutput(new ArgMaxNode(this, x)); + } + + /** + * Creates an argmax equals operation in the graph. + * @param x1 First input tensor to check against. + * @param x2 Second input tensor to check against. + * @return The tensor representing the argmax equals operation. + */ + argmaxEquals(x1: Tensor, x2: Tensor): Tensor { + return this.addNodeAndReturnOutput(new ArgMaxEqualsNode(this, x1, x2)); + } + + private addNodeAndReturnOutput(node: Node): Tensor { + this.nodes.push(node); + node.validate(); + return node.output; + } + + getNodes(): Node[] { + return this.nodes; + } + + private nodes: Node[] = []; +} + +/** + * Tensor represents the output of an operation node in the graph. + * Tensors have no data associated with them, but maintain a shape array + * to determine operation compatibility. All graph methods that create graph + * operations return Tensor objects, which can be thought of as 'handles' to + * operations. + */ +export class Tensor { + node: Node; + id: number; + /** + * @param shape The shape of this tensor, in dimension sizes. + */ + constructor(public shape: number[]) { + this.id = Tensor.nextID++; + } + private static nextID = 0; +} + +/** + * Node is the concrete base class for all operations in the graph. + * Users generally don't need to interact directly with Node instances, but they + * are provided for informational and introspection purposes. + * + * @hidden + */ +export abstract class Node { + /** + * @param graph The graph containing this node + * @param name The name of this node + * @param inputs A dictionary of named Tensors that comprise this node's + * inputs. + * @param output This node's output Tensor + */ + constructor( + public graph: Graph, public name: string, + public inputs: {[name: string]: Tensor}, public output: Tensor) { + this.id = Node.nextID++; + output.node = this; + } + abstract validate(): void; + id: number; + private static nextID = 0; +} + +/** + * VariableNode represents a variable, a user-provided NDArray that's + * adjusted during backpropagation training. + * + * @hidden + */ +export class VariableNode extends Node { + constructor(graph: Graph, name: string, public data: NDArray) { + super(graph, name, {}, new Tensor(data.shape)); + } + validate() { + util.assert( + this.data != null, + 'Error adding variable op: Data for variable \'' + this.name + + '\' is null or undefined'); + } +} + +/** + * PlaceholderNode represents a placeholder, a user-provided NDArray + * that's used as immutable input during inference and training. + * + * @hidden + */ +export class PlaceholderNode extends Node { + constructor(graph: Graph, name: string, shape: number[]) { + super(graph, name, {}, new Tensor(shape)); + } + validate() {} +} + +/** + * ConstantNode represents a constant value in the graph. + * + * @hidden + */ +export class ConstantNode extends Node { + constructor(graph: Graph, public data: NDArray) { + super(graph, 'Constant', {}, new Tensor(data.shape)); + } + validate() { + util.assert( + this.data != null, + 'Error adding constant: data for placeholder \'' + this.name + + '\' is null or undefined'); + } +} + +/** + * ReshapeNode represents a reshape operation in the graph. + * + * @hidden + */ +export class ReshapeNode extends Node { + static readonly X = 'x'; + constructor( + graph: Graph, public name: string, private x: Tensor, + private shape: number[]) { + super(graph, name, {x}, new Tensor(shape)); + } + validate() { + const xSize = util.sizeFromShape(this.x.shape); + const shapeSize = util.sizeFromShape(this.shape); + util.assert( + xSize === shapeSize, + 'Error making reshape operation: input Tensor to reshape \'' + + this.name + '\' of shape (' + this.x.shape + + ') does not match size of requested shape ' + this.shape + '.'); + } +} + +/** + * LinearCombinationNode represents a linear combination of two tensors. + * @hidden + */ +export class FusedLinearCombinationNode extends Node { + static readonly T1 = 't1'; + static readonly T2 = 't2'; + static readonly C1 = 'c1'; + static readonly C2 = 'c2'; + constructor( + graph: Graph, private t1: Tensor, private t2: Tensor, private c1: Tensor, + private c2: Tensor) { + super(graph, 'Linear Combination', {t1, t2, c1, c2}, new Tensor(t1.shape)); + } + + validate() { + util.assertShapesMatch(this.t1.shape, this.t2.shape); + if (!util.isScalarShape(this.c1.shape)) { + throw new Error( + 'Error adding fusedLinearCombination: c1 is not a scalar, got ' + + 'shape: ' + this.c1.shape); + } + if (!util.isScalarShape(this.c2.shape)) { + throw new Error( + 'Error adding fusedLinearCombination: c2 is not a scalar, got ' + + 'shape: ' + this.c2.shape); + } + } +} + +/** + * @hidden + */ +export class AddNode extends Node { + static readonly T1 = 't1'; + static readonly T2 = 't2'; + + constructor(graph: Graph, private t1: Tensor, private t2: Tensor) { + super( + graph, 'Add', {t1, t2}, + new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)); + } + + validate() { + util.assert( + util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), + 'Error adding add operation op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + } +} + +/** + * @hidden + */ +export class SubtractNode extends Node { + static readonly T1 = 't1'; + static readonly T2 = 't2'; + + constructor(graph: Graph, private t1: Tensor, private t2: Tensor) { + super( + graph, 'Subtract', {t1, t2}, + new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)); + } + + validate() { + util.assert( + util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), + 'Error adding subtract op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + } +} + +/** + * @hidden + */ +export class MultiplyNode extends Node { + static readonly T1 = 't1'; + static readonly T2 = 't2'; + + constructor(graph: Graph, private t1: Tensor, private t2: Tensor) { + super( + graph, 'Multiply', {t1, t2}, + new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)); + } + + validate() { + util.assert( + util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), + 'Error adding multiply op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + } +} + +/** + * @hidden + */ +export class DivideNode extends Node { + static readonly T1 = 't1'; + static readonly T2 = 't2'; + + constructor(graph: Graph, private t1: Tensor, private t2: Tensor) { + super( + graph, 'Divide', {t1, t2}, + new Tensor(util.sizeFromShape(t1.shape) === 1 ? t2.shape : t1.shape)); + } + + validate() { + util.assert( + util.sizeFromShape(this.t1.shape) === 1 || + util.sizeFromShape(this.t2.shape) === 1 || + util.arraysEqual(this.t1.shape, this.t2.shape), + 'Error adding divide op: one of inputs must be scalar or the ' + + 'shapes ' + this.t1.shape + ' and ' + this.t2.shape + + ' must match.'); + } +} + +/** + * @hidden + */ +export class ReduceSumNode extends Node { + static readonly X = 'x'; + + constructor(graph: Graph, x: Tensor) { + super(graph, 'ReduceSum', {x}, new Tensor([])); + } + + validate() {} +} + +/** + * Concat3DNode represents a 3D concatenation of two tensors along an axis. + * @hidden + */ +export class Concat3DNode extends Node { + static readonly X1 = 'x1'; + static readonly X2 = 'x2'; + static readonly AXIS = 'axis'; + constructor( + graph: Graph, private x1: Tensor, private x2: Tensor, + public axis: number) { + super( + graph, 'Concat3D', {x1, x2}, + new Tensor(concat3d_util.computeConcat3DOutputShape( + x1.shape, x2.shape, axis))); + } + validate() { + concat3d_util.assertConcat3DShapesMatch( + this.x1.shape, this.x2.shape, this.axis); + } +} + +function getMatMulOutputShape(x1Shape: number[], x2Shape: number[]): number[] { + if (x1Shape.length === 1 && x2Shape.length === 1) { + return [1]; + } else if (x1Shape.length === 1 && x2Shape.length === 2) { + return [x2Shape[1]]; + } else if (x1Shape.length === 2 && x2Shape.length === 1) { + return [x1Shape[0]]; + } + return [x1Shape[0], x2Shape[1]]; +} + +/** + * MatMulNode represents a fully connected layer in the graph. + * @hidden + */ +export class MatMulNode extends Node { + static readonly X1 = 'x1'; + static readonly X2 = 'x2'; + constructor(graph: Graph, private x1: Tensor, private x2: Tensor) { + super( + graph, 'MatMul', {x1, x2}, + new Tensor(getMatMulOutputShape(x1.shape, x2.shape))); + } + + validate() { + if (this.x1.shape.length === 2 && this.x2.shape.length === 2) { + util.assert( + this.x1.shape[1] === this.x2.shape[0], + 'Error adding matmul op: inner shapes of matrices with shapes ' + + this.x1.shape + ' and ' + this.x2.shape + ' must match.'); + } else if (this.x1.shape.length === 2 && this.x2.shape.length === 1) { + util.assert( + this.x1.shape[1] === this.x2.shape[0], + 'Error adding matmul op: second dimension of matrix with shape ' + + this.x1.shape + ' must match size of vector with shape ' + + this.x2.shape + '.'); + } else if (this.x1.shape.length === 1 && this.x2.shape.length === 2) { + util.assert( + this.x1.shape[0] === this.x2.shape[0], + 'Error adding matmul op: size of vector with shape ' + this.x1.shape + + ' must match first dimension of matrix with ' + + 'shape ' + this.x2.shape + '.'); + } else { + throw new Error( + 'Error adding matmul op: inputs must be vectors or matrices.'); + } + } +} + +/** + * Convolution2DNode represents a 2d convolution operation in the graph. + * @hidden + */ +export class Convolution2DNode extends Node { + static readonly X = 'x'; + static readonly W = 'w'; + static readonly B = 'b'; + constructor( + graph: Graph, private x: Tensor, private w: Tensor, private b: Tensor, + public fieldSize: number, public outputDepth: number, public stride = 1, + public zeroPad?: number) { + super( + graph, 'Convolution 2D', {x, w, b}, + new Tensor(conv_util.computeOutputShape3D( + x.shape as [number, number, number], fieldSize, outputDepth, stride, + zeroPad))); + } + validate() { + util.assert( + this.x.shape.length === 3, + 'Error adding conv2d op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + util.assert( + this.w.shape.length === 4, + 'Error adding conv2d op: weights must be of rank 4, but got shape: ' + + this.w.shape + '.'); + util.assert( + this.b.shape.length === 1, + 'Error adding conv2d op: biases must be of rank 1, but got shape: ' + + this.b.shape + '.'); + + util.assert( + this.x.shape[2] === this.w.shape[2], + 'Error adding conv2d op: depth of input (' + this.x.shape[2] + + ') must match input depth for weights (' + this.w.shape[2] + ').'); + } +} + +/** + * MaxPoolNode represents a 2d max pool operation in the graph. + * @hidden + */ +export class MaxPoolNode extends Node { + static readonly X = 'x'; + constructor( + graph: Graph, private x: Tensor, public fieldSize: number, + public stride = 1, public zeroPad?: number) { + super( + graph, 'Max pool', {x}, + new Tensor(conv_util.computeOutputShape3D( + x.shape as [number, number, number], fieldSize, x.shape[2], stride, + zeroPad))); + } + validate() { + util.assert( + this.x.shape.length === 3, + 'Error adding maxPool op: input must be of rank 3, but got shape: ' + + this.x.shape + '.'); + } +} + +/** + * ReLUNode represents a ReLU operation in the graph. + * @hidden + */ +export class ReLUNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'ReLU', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * ExpNode represents a Exponentiation operation in the graph. + * @hidden + */ +export class ExpNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'Exp', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * LogNode represents a Exponentiation operation in the graph. + * @hidden + */ +export class LogNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'Log', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * TanHNode represents a tanh operation in the graph. + * @hidden + */ +export class TanHNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'TanH', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * SigmoidNode represents a sigmoid operation in the graph. + * @hidden + */ +export class SigmoidNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'Sigmoid', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * Square node represents an element-wise square operation in the graph. + * @hidden + */ +export class SquareNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, x: Tensor) { + super(graph, 'Square', {x}, new Tensor(x.shape)); + } + validate() {} +} + +/** + * SoftmaxCrossEntropyCostNode represents a softmax cross-entropy cost operation + * in the graph. + * @hidden + */ +export class SoftmaxCrossEntropyCostNode extends Node { + static readonly X = 'x'; + static readonly TARGET = 'target'; + constructor(graph: Graph, private x: Tensor, private target: Tensor) { + super(graph, 'SoftmaxCrossEntropyCost', {x, target}, new Tensor([])); + } + validate() { + util.assert( + util.arraysEqual(this.x.shape, this.target.shape), + 'Error adding softmaxCrossEntropyCost op: x shape (' + this.x.shape + + ') must match target shape (' + this.target.shape + ').'); + } +} + +/** + * @hidden + */ +export class SoftmaxNode extends Node { + static readonly X = 'x'; + + constructor(graph: Graph, private x: Tensor) { + super(graph, 'Softmax', {x}, new Tensor(x.shape)); + } + validate() { + util.assert( + this.x.shape.length === 1, + 'The input to a softmax must be a 1-D tensor'); + util.assert( + this.x.shape[0] >= 2, + 'The input to a softmax must have at least 2 values'); + } +} + +/** + * MeanSquaredCostNode represents a mean squared cost operation + * in the graph. + * + * @hidden + */ +export class MeanSquaredCostNode extends Node { + static readonly LABEL = 'label'; + static readonly PREDICTION = 'prediction'; + constructor(graph: Graph, private label: Tensor, private prediction: Tensor) { + super(graph, 'Mean Squared Cost', {label, prediction}, new Tensor([])); + } + validate() { + util.assert( + util.arraysEqual(this.label.shape, this.prediction.shape), + 'Error adding meanSquaredCost op: label shape (' + this.label.shape + + ') must match prediction shape (' + this.prediction.shape + ').'); + } +} + +/** + * ArgMaxNode represents an argmax operation in the graph. + * @hidden + */ +export class ArgMaxNode extends Node { + static readonly X = 'x'; + constructor(graph: Graph, public x: Tensor) { + super(graph, 'ArgMax', {x}, new Tensor([1])); + } + validate() { + util.assert( + util.sizeFromShape(this.x.shape) > 0, + 'Error adding argmax op: input tensor must have at least one entry.'); + } +} + +/** + * ArgMaxEqualsNode represents a argmax equals operation in the graph. + * @hidden + */ +export class ArgMaxEqualsNode extends Node { + static readonly X1 = 'x1'; + static readonly X2 = 'x2'; + constructor(graph: Graph, private x1: Tensor, private x2: Tensor) { + super(graph, 'ArgMaxEquals', {x1, x2}, new Tensor([1])); + } + validate() { + util.assert( + util.arraysEqual(this.x1.shape, this.x2.shape), + 'Error adding ArgMaxEquals op: x1 shape (' + this.x1.shape + + ') must match x2 shape (' + this.x2.shape + ').'); + } +} + +/** + * Split nodes are used to accumulate backprop derivatives when a node's output + * tensor is consumed by multiple nodes. + * @hidden + */ +export class SplitNode extends Node { + static readonly X = 'x'; + + outputs: Tensor[] = []; + + constructor(graph: Graph, x: Tensor) { + super(graph, 'SplitNode', {x}, new Tensor(x.shape)); + } + + /** + * Registers a new consumer of this split node, i.e. a new node that uses the + * node's output tensor. + */ + getNewOutputTensor(): Tensor { + const output = new Tensor(this.inputs[SplitNode.X].shape); + output.node = this; + this.outputs.push(output); + return output; + } + validate() {} +} + +/** + * @hidden + */ +export type ArrayData = + NDArray|number|number[]|number[][]|number[][][]|number[][][][]; diff --git a/src/graph_layers.ts b/src/graph_layers.ts new file mode 100644 index 0000000000..fe0c8c4225 --- /dev/null +++ b/src/graph_layers.ts @@ -0,0 +1,51 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Graph, Tensor} from './graph'; +import {Initializer, VarianceScalingInitializer, ZerosInitializer} from './initializers'; +import {NDArray} from './math/ndarray'; + +/** + * A layers sugar class around the graph that initializes variables + * automatically for layers. + */ +export class GraphLayers { + constructor(private g: Graph) {} + + dense( + name: string, x: Tensor, units: number, + activation: ((x: Tensor) => Tensor)|null = null, useBias = true, + kernelInitializer: Initializer = new VarianceScalingInitializer(), + biasInitializer: Initializer = new ZerosInitializer()) { + const weights = this.g.variable( + name + '-weights', + kernelInitializer.initialize([x.shape[0], units], x.shape[0], units)); + + let out = this.g.matmul(x, weights); + + if (useBias) { + const bias = this.g.variable( + name + '-bias', + biasInitializer.initialize([units], x.shape[0], units)); + out = this.g.add(out, bias); + } + + if (activation != null) { + out = activation(out); + } + + return out; + } +} diff --git a/src/graph_runner.ts b/src/graph_runner.ts new file mode 100644 index 0000000000..9bb0c0d4ed --- /dev/null +++ b/src/graph_runner.ts @@ -0,0 +1,357 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as dataset from './dataset'; +import {Graph, Tensor} from './graph'; +import {InputProvider} from './input_provider'; +import {NDArrayMath} from './math/math'; +import {NDArrayMathCPU} from './math/math_cpu'; +import {NDArray, Scalar} from './math/ndarray'; +import {Optimizer} from './optimizer'; +import {CostReduction, FeedEntry, Session} from './session'; + +const DEFAULT_EVAL_INTERVAL_MS = 1500; +const DEFAULT_COST_INTERVAL_MS = 500; +const DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS = 3000; + +export interface GraphRunnerEventObserver { + batchesTrainedCallback?: (totalBatchesTrained: number) => void; + avgCostCallback?: (avgCost: Scalar) => void; + metricCallback?: (metric: NDArray) => void; + inferenceExamplesCallback?: + (feeds: FeedEntry[][], inferenceValues: NDArray[]) => void; + inferenceExamplesPerSecCallback?: (examplesPerSec: number) => void; + trainExamplesPerSecCallback?: (examplesPerSec: number) => void; + totalTimeCallback?: (totalTimeSec: number) => void; + doneTrainingCallback?: () => void; +} + +export enum MetricReduction { + SUM, + MEAN +} + +/** + * A class that drives the training of a graph model given a dataset. It allows + * the user to provide a set of callbacks for measurements like cost, accuracy, + * and speed of training. + */ +export class GraphRunner { + private costTensor: Tensor; + private trainFeedEntries: FeedEntry[]; + private batchSize: number; + private optimizer: Optimizer; + private currentTrainLoopNumBatches: number|undefined; + private costIntervalMs: number; + + private metricTensor: Tensor|undefined; + private metricFeedEntries: FeedEntry[]|undefined; + private metricBatchSize: number|undefined; + private metricReduction: MetricReduction; + private metricIntervalMs: number; + + private inferenceTensor: Tensor; + private inferenceFeedEntries: FeedEntry[]|undefined; + private inferenceExampleIntervalMs: number; + private inferenceExampleCount: number; + + // Runtime information. + private isTraining: boolean; + private totalBatchesTrained: number; + private batchesTrainedThisRun: number; + private lastComputedMetric: NDArray; + + private isInferring: boolean; + private currentInferenceLoopNumPasses: number|undefined; + private inferencePassesThisRun: number; + + private trainStartTimestamp: number; + private lastCostTimestamp = 0; + private lastEvalTimestamp = 0; + + private lastStopTimestamp: number|null; + private totalIdleTimeMs = 0; + + private zeroScalar: Scalar; + private metricBatchSizeScalar: Scalar; + + constructor( + private math: NDArrayMath, private session: Session, + private eventObserver: GraphRunnerEventObserver) { + this.resetStatistics(); + this.zeroScalar = Scalar.new(0); + } + + resetStatistics() { + this.totalBatchesTrained = 0; + this.totalIdleTimeMs = 0; + this.lastStopTimestamp = null; + } + + /** + * Start the training loop with an optional number of batches to train for. + * Optionally takes a metric tensor and feed entries to compute periodically. + * This can be used for computing accuracy, or a similar metric. + */ + train( + costTensor: Tensor, trainFeedEntries: FeedEntry[], batchSize: number, + optimizer: Optimizer, numBatches?: number, metricTensor?: Tensor, + metricFeedEntries?: FeedEntry[], metricBatchSize?: number, + metricReduction = MetricReduction.MEAN, + evalIntervalMs = DEFAULT_EVAL_INTERVAL_MS, + costIntervalMs = DEFAULT_COST_INTERVAL_MS) { + this.costTensor = costTensor; + this.trainFeedEntries = trainFeedEntries; + this.metricTensor = metricTensor; + this.metricFeedEntries = metricFeedEntries; + if (metricBatchSize != null && this.metricBatchSize !== metricBatchSize) { + if (this.metricBatchSizeScalar != null) { + this.metricBatchSizeScalar.dispose(); + } + this.metricBatchSizeScalar = Scalar.new(metricBatchSize); + } + this.metricBatchSize = metricBatchSize; + this.metricReduction = metricReduction; + this.batchSize = batchSize; + this.optimizer = optimizer; + + this.metricIntervalMs = evalIntervalMs; + this.costIntervalMs = costIntervalMs; + this.currentTrainLoopNumBatches = numBatches; + + this.batchesTrainedThisRun = 0; + this.isTraining = true; + this.trainStartTimestamp = performance.now(); + this.trainNetwork(); + } + + stopTraining() { + this.isTraining = false; + this.lastStopTimestamp = performance.now(); + } + + resumeTraining() { + this.isTraining = true; + if (this.lastStopTimestamp != null) { + this.totalIdleTimeMs += performance.now() - this.lastStopTimestamp; + } + this.trainNetwork(); + } + + private trainNetwork() { + if (this.batchesTrainedThisRun === this.currentTrainLoopNumBatches) { + this.stopTraining(); + } + + if (!this.isTraining) { + if (this.eventObserver.doneTrainingCallback != null) { + this.eventObserver.doneTrainingCallback(); + } + return; + } + + const start = performance.now(); + const shouldComputeCost = this.eventObserver.avgCostCallback != null && + (start - this.lastCostTimestamp > this.costIntervalMs); + if (shouldComputeCost) { + this.lastCostTimestamp = start; + } + + const costReduction = + shouldComputeCost ? CostReduction.MEAN : CostReduction.NONE; + + this.math.scope((keep) => { + const avgCost = this.session.train( + this.costTensor, this.trainFeedEntries, this.batchSize, + this.optimizer, costReduction); + + if (shouldComputeCost) { + const trainTime = performance.now() - start; + + this.eventObserver.avgCostCallback!(avgCost); + + if (this.eventObserver.trainExamplesPerSecCallback != null) { + const examplesPerSec = (this.batchSize * 1000 / trainTime); + this.eventObserver.trainExamplesPerSecCallback(examplesPerSec); + } + } + + if (this.eventObserver.metricCallback != null && + this.metricFeedEntries != null && + start - this.lastEvalTimestamp > this.metricIntervalMs) { + this.lastEvalTimestamp = start; + + if (this.lastComputedMetric != null) { + this.lastComputedMetric.dispose(); + } + this.lastComputedMetric = this.computeMetric(); + this.eventObserver.metricCallback(this.lastComputedMetric); + } + + if (this.eventObserver.totalTimeCallback != null) { + this.eventObserver.totalTimeCallback( + (start - this.trainStartTimestamp) / 1000); + } + + this.batchesTrainedThisRun++; + this.totalBatchesTrained++; + + if (this.eventObserver.batchesTrainedCallback != null) { + this.eventObserver.batchesTrainedCallback(this.totalBatchesTrained); + } + + }); + setTimeout(() => this.trainNetwork()); + } + + infer( + inferenceTensor: Tensor, inferenceFeedEntries: FeedEntry[], + inferenceExampleIntervalMs = DEFAULT_INFERENCE_EXAMPLE_INTERVAL_MS, + inferenceExampleCount = 5, numPasses?: number) { + if (this.eventObserver.inferenceExamplesCallback == null && + this.eventObserver.inferenceExamplesPerSecCallback == null) { + throw new Error( + 'Cannot start inference loop, no inference example or ' + + 'examples/sec observer provided.'); + } + + // Make sure the feed values are providers, and not NDArrays. + for (let i = 0; i < inferenceFeedEntries.length; i++) { + const feedEntry = inferenceFeedEntries[i]; + + if (feedEntry.data instanceof NDArray) { + throw new Error( + 'Cannot start inference on the model runner with feed entries of ' + + 'type NDArray. Please use InputProviders.'); + } + } + + this.inferenceExampleIntervalMs = inferenceExampleIntervalMs; + this.inferenceTensor = inferenceTensor; + this.inferenceFeedEntries = inferenceFeedEntries; + this.inferenceExampleCount = inferenceExampleCount; + this.currentInferenceLoopNumPasses = numPasses; + if (!this.isInferring) { + this.inferencePassesThisRun = 0; + setTimeout(() => this.inferNetwork()); + } + this.isInferring = true; + } + + private inferNetwork() { + if (!this.isInferring || + this.inferencePassesThisRun === this.currentInferenceLoopNumPasses) { + return; + } + + this.math.scope((keep, track) => { + const feeds: FeedEntry[][] = []; + const inferenceValues: NDArray[] = []; + + const start = performance.now(); + for (let i = 0; i < this.inferenceExampleCount; i++) { + // Populate a new FeedEntry[] populated with NDArrays. + const ndarrayFeedEntries: FeedEntry[] = []; + for (let j = 0; j < this.inferenceFeedEntries!.length; j++) { + const feedEntry = this.inferenceFeedEntries![j]; + ndarrayFeedEntries.push({ + tensor: feedEntry.tensor, + data: + track((feedEntry.data as InputProvider).getNextCopy(this.math)) + }); + } + feeds.push(ndarrayFeedEntries); + + inferenceValues.push( + this.session.eval(this.inferenceTensor, ndarrayFeedEntries)); + } + + if (this.eventObserver.inferenceExamplesPerSecCallback != null) { + // Force a GPU download, since inference results are generally needed on + // the CPU and it's more fair to include blocking on the GPU to complete + // its work for the inference measurement. + inferenceValues[inferenceValues.length - 1].getValues(); + + const inferenceExamplesPerSecTime = performance.now() - start; + + const examplesPerSec = + (this.inferenceExampleCount * 1000 / inferenceExamplesPerSecTime); + this.eventObserver.inferenceExamplesPerSecCallback!(examplesPerSec); + } + + if (this.eventObserver.inferenceExamplesCallback != null) { + this.eventObserver.inferenceExamplesCallback(feeds, inferenceValues); + } + this.inferencePassesThisRun++; + + }); + setTimeout(() => this.inferNetwork(), this.inferenceExampleIntervalMs); + } + + stopInferring() { + this.isInferring = false; + } + + isInferenceRunning(): boolean { + return this.isInferring; + } + + computeMetric(): Scalar { + if (this.metricFeedEntries == null) { + throw new Error('Cannot compute metric, no metric FeedEntries provided.'); + } + + let metric = this.zeroScalar; + + return this.math.scope((keep) => { + for (let i = 0; i < this.metricBatchSize!; i++) { + const metricValue = + this.session.eval(this.metricTensor!, this.metricFeedEntries!); + + metric = this.math.add(metric, metricValue); + } + + if (this.metricReduction === MetricReduction.MEAN) { + metric = this.math.divide(metric, this.metricBatchSizeScalar); + } + + return metric; + }); + } + + getTotalBatchesTrained(): number { + return this.totalBatchesTrained; + } + + getLastComputedMetric(): Scalar { + return this.lastComputedMetric; + } + + setMath(math: NDArrayMath) { + this.math = math; + } + + setSession(session: Session) { + this.session = session; + } + + setInferenceTensor(inferenceTensor: Tensor) { + this.inferenceTensor = inferenceTensor; + } + + setInferenceExampleCount(inferenceExampleCount: number) { + this.inferenceExampleCount = inferenceExampleCount; + } +} diff --git a/src/graph_runner_test.ts b/src/graph_runner_test.ts new file mode 100644 index 0000000000..b73cb61f36 --- /dev/null +++ b/src/graph_runner_test.ts @@ -0,0 +1,204 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Graph, Tensor} from './graph'; +import {GraphRunner, GraphRunnerEventObserver, MetricReduction} from './graph_runner'; +import {NDArrayMath} from './math/math'; +import {NDArrayMathCPU} from './math/math_cpu'; +import {Array1D, NDArray, Scalar} from './math/ndarray'; +import {Optimizer} from './optimizer'; +import {CostReduction, FeedEntry, Session} from './session'; +import {SGDOptimizer} from './sgd_optimizer'; + +const FAKE_LEARNING_RATE = 1.0; +const FAKE_BATCH_SIZE = 10; + +function fakeTrainBatch( + math: NDArrayMath, feedEntries: FeedEntry[], batchSize: number, + optimizer: Optimizer, costReduction: CostReduction) { + return Scalar.new(.5); +} + +describe('Model runner', () => { + let math: NDArrayMath; + let g: Graph; + let session: Session; + let optimizer: SGDOptimizer; + let inputTensor: Tensor; + let labelTensor: Tensor; + let costTensor: Tensor; + let predictionTensor: Tensor; + let metricTensor: Tensor; + + let graphRunner: GraphRunner; + + let avgCostCallback: (avgCost: Scalar) => void; + let metricCallback: (metric: Scalar) => void; + + const fakeUserEvents: GraphRunnerEventObserver = { + batchesTrainedCallback: (totalBatchesTrained: number) => null, + avgCostCallback: (avgCost: Scalar) => avgCostCallback(avgCost), + metricCallback: (metric: Scalar) => metricCallback(metric), + inferenceExamplesCallback: + (feeds: FeedEntry[][], inferenceValues: NDArray[]) => null, + trainExamplesPerSecCallback: (examplesPerSec: number) => null, + totalTimeCallback: (totalTime: number) => null + }; + + beforeEach(() => { + math = new NDArrayMathCPU(); + g = new Graph(); + optimizer = new SGDOptimizer(FAKE_LEARNING_RATE); + + inputTensor = g.placeholder('input', [2]); + + predictionTensor = g.add(inputTensor, g.constant(Array1D.new([1, 1]))); + + labelTensor = g.placeholder('label', [2]); + + costTensor = g.softmaxCrossEntropyCost(predictionTensor, labelTensor); + + metricTensor = g.argmaxEquals(predictionTensor, labelTensor); + + session = new Session(g, math); + + spyOn(session, 'train').and.callFake(fakeTrainBatch); + let counter = 0; + spyOn(session, 'eval').and.callFake((evalTensor: Tensor) => { + if (evalTensor === predictionTensor) { + return Array1D.new([1, 0]); + } else if (evalTensor === metricTensor) { + return Scalar.new(counter++ % 2); + } else { + throw new Error('Eval tensor not recognized'); + } + }); + spyOn(fakeUserEvents, 'batchesTrainedCallback').and.callThrough(); + spyOn(fakeUserEvents, 'avgCostCallback').and.callThrough(); + spyOn(fakeUserEvents, 'metricCallback').and.callThrough(); + spyOn(fakeUserEvents, 'inferenceExamplesCallback').and.callThrough(); + spyOn(fakeUserEvents, 'trainExamplesPerSecCallback').and.callThrough(); + spyOn(fakeUserEvents, 'totalTimeCallback').and.callThrough(); + }); + + it('basic train usage, train 3 batches', (doneFn) => { + const numBatches = 3; + const trainFeedEntries: FeedEntry[] = []; + const testExampleProvider: FeedEntry[] = []; + + // Mark this async test as done once the model runner calls our callback, + // and fail if it doesn't. + fakeUserEvents.doneTrainingCallback = () => { + for (let i = 0; i < numBatches; i++) { + // All batches should compute cost. + const args = (session.train as jasmine.Spy).calls.argsFor(i); + expect(args).toEqual([ + costTensor, trainFeedEntries, FAKE_BATCH_SIZE, optimizer, + CostReduction.MEAN + ]); + + const avgCost = + (fakeUserEvents.avgCostCallback as jasmine.Spy).calls.argsFor(i)[0]; + const accuracy = + (fakeUserEvents.metricCallback as jasmine.Spy).calls.argsFor(i)[0]; + } + expect((fakeUserEvents.avgCostCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect((fakeUserEvents.metricCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect((session.train as jasmine.Spy).calls.count()).toEqual(numBatches); + + // 2 test examples are provided per batch. + expect((session.eval as jasmine.Spy).calls.count()) + .toEqual(FAKE_BATCH_SIZE * numBatches); + expect((fakeUserEvents.avgCostCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect((fakeUserEvents.metricCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect((fakeUserEvents.trainExamplesPerSecCallback as jasmine.Spy) + .calls.count()) + .toEqual(numBatches); + expect((fakeUserEvents.totalTimeCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect( + (fakeUserEvents.batchesTrainedCallback as jasmine.Spy).calls.count()) + .toEqual(numBatches); + expect(graphRunner.getTotalBatchesTrained()).toEqual(numBatches); + + // Inference callbacks should not be called during training. + expect((fakeUserEvents.inferenceExamplesCallback as jasmine.Spy) + .calls.count()) + .toEqual(0); + + doneFn(); + }; + + avgCostCallback = (avgCost: Scalar) => { + expect(avgCost.get()).toEqual(.5); + }; + metricCallback = (metric: Scalar) => { + expect(metric.get()).toEqual(.5); + }; + + + graphRunner = new GraphRunner(math, session, fakeUserEvents); + + expect(graphRunner.getTotalBatchesTrained()).toEqual(0); + + graphRunner.train( + costTensor, trainFeedEntries, FAKE_BATCH_SIZE, optimizer, numBatches, + metricTensor, testExampleProvider, FAKE_BATCH_SIZE, + MetricReduction.MEAN, 0, 0); + }); + + it('basic inference usage', (doneFn) => { + const intervalMs = 0; + const exampleCount = 2; + const numPasses = 1; + + fakeUserEvents.inferenceExamplesCallback = + (inputInferenceExamples: FeedEntry[][], + inferenceOutputs: NDArray[]) => { + expect(inputInferenceExamples.length).toEqual(exampleCount); + expect(inferenceOutputs.length).toEqual(exampleCount); + expect((session.eval as jasmine.Spy).calls.count()) + .toEqual(exampleCount * numPasses); + + // No training should have occured. + expect(graphRunner.getTotalBatchesTrained()).toEqual(0); + expect((fakeUserEvents.avgCostCallback as jasmine.Spy).calls.count()) + .toEqual(0); + expect((fakeUserEvents.metricCallback as jasmine.Spy).calls.count()) + .toEqual(0); + expect((fakeUserEvents.trainExamplesPerSecCallback as jasmine.Spy) + .calls.count()) + .toEqual(0); + expect( + (fakeUserEvents.totalTimeCallback as jasmine.Spy).calls.count()) + .toEqual(0); + expect((fakeUserEvents.batchesTrainedCallback as jasmine.Spy) + .calls.count()) + .toEqual(0); + expect(graphRunner.getTotalBatchesTrained()).toEqual(0); + doneFn(); + }; + graphRunner = new GraphRunner(math, session, fakeUserEvents); + + const inferenceFeedEntries: FeedEntry[] = []; + graphRunner.infer( + predictionTensor, inferenceFeedEntries, intervalMs, exampleCount, + numPasses); + }); +}); diff --git a/src/graph_test.ts b/src/graph_test.ts new file mode 100644 index 0000000000..8d9c5d5df1 --- /dev/null +++ b/src/graph_test.ts @@ -0,0 +1,621 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {ConstantNode, Graph, Node, Tensor, VariableNode} from './graph'; +import * as conv_util from './math/conv_util'; +import {NDArray} from './math/ndarray'; +import {FeedDictionary, Session} from './session'; +import * as session_util from './session_util'; + +class TestNode extends Node { + validate() {} +} + +describe('Graph', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('nodes have ascending ids', () => { + const a = new TestNode(g, '', {}, new Tensor([])); + const b = new TestNode(g, '', {}, new Tensor([])); + expect(b.id).toEqual(a.id + 1); + }); + + it('variable creates a node in the graph', () => { + const v = g.variable('', NDArray.zeros([1])); + expect(v.node.graph).toEqual(g); + }); + + it('variable creates a VariableNode in the graph', () => { + const v = g.variable('', NDArray.zeros([1])); + expect(v.node instanceof VariableNode).toEqual(true); + }); + + it('variable passes name to graph node', () => { + const v = g.variable('hello', NDArray.zeros([1])); + expect(v.node.name).toEqual('hello'); + }); + + it('mnist fully-connected', () => { + const input = g.placeholder('input', [28 * 28]); + const fc0W = g.variable('fc0W', NDArray.zeros([32, 28 * 28])); + const fc0B = g.variable('fc0B', NDArray.zeros([32])); + const fc0 = g.add(g.matmul(fc0W, input), fc0B); + const relu0 = g.relu(fc0); + const fc1W = g.variable('fc1W', NDArray.zeros([32, 32])); + const fc1B = g.variable('fc1B', NDArray.zeros([32])); + const fc1 = g.add(g.matmul(fc1W, relu0), fc1B); + const relu1 = g.relu(fc1); + const fc2W = g.variable('fc2W', NDArray.zeros([32, 32])); + const fc2B = g.variable('fc2B', NDArray.zeros([32])); + const fc2 = g.add(g.matmul(fc2W, relu1), fc2B); + const relu2 = g.relu(fc2); + const fc3W = g.variable('fc3W', NDArray.zeros([10, 32])); + const fc3B = g.variable('fc3B', NDArray.zeros([10])); + const fc3 = g.add(g.matmul(fc3W, relu2), fc3B); + + const fd = new FeedDictionary([{tensor: input, data: NDArray.zeros([1])}]); + const orderedEvaluationSet = + session_util.getOrderedEvaluationSetFromEvalTensor([fc3], fd); + }); +}); + +describe('Variable validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('null data throws', () => { + expect(() => g.variable('test', null!)).toThrowError(); + }); + + it('non null data does not throw', () => { + g.variable('test', NDArray.zeros([5])); + }); +}); + +describe('Placeholder validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('does not throw', () => { + expect(g.placeholder('test', [1, 2, 3]).shape).toEqual([1, 2, 3]); + }); +}); + +describe('Constant', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('null data throws', () => { + expect(() => g.constant(null!)).toThrowError(); + }); + + it('non null data does not throw', () => { + expect(g.constant(NDArray.zeros([5])).shape).toEqual([5]); + }); + + it('from a single value', () => { + const c = g.constant(3); + expect(c.shape).toEqual([]); + const values = (c.node as ConstantNode).data.getValues(); + expect(values).toEqual(new Float32Array([3])); + }); + + it('from 1d array', () => { + const c = g.constant([1, 2, 3]); + expect(c.shape).toEqual([3]); + const values = (c.node as ConstantNode).data.getValues(); + expect(values).toEqual(new Float32Array([1, 2, 3])); + }); + + it('from 2d array', () => { + const c = g.constant([[1, 2, 3], [4, 5, 6]]); + expect(c.shape).toEqual([2, 3]); + const values = (c.node as ConstantNode).data.getValues(); + expect(values).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('from 3d array', () => { + const c = g.constant([[[1], [2], [3]], [[4], [5], [6]]]); + expect(c.shape).toEqual([2, 3, 1]); + const values = (c.node as ConstantNode).data.getValues(); + expect(values).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('from 4d array', () => { + const c = g.constant([[[[1]], [[2]], [[3]]], [[[4]], [[5]], [[6]]]]); + expect(c.shape).toEqual([2, 3, 1, 1]); + const values = (c.node as ConstantNode).data.getValues(); + expect(values).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); +}); + +describe('Reshape validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different sizes throws', () => { + expect(() => g.reshape(new Tensor([5, 4]), [3, 3])).toThrowError(); + }); + + it('Same size does not throw', () => { + expect(g.reshape(new Tensor([5, 4]), [20]).shape).toEqual([20]); + }); +}); + +describe('FusedLinearCombination validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different shape tensors throws', () => { + expect( + () => g.fusedLinearCombination( + new Tensor([3, 4]), new Tensor([1]), new Tensor([]), + new Tensor([]))) + .toThrowError(); + }); + + it('Non scalar c1 throws', () => { + expect( + () => g.fusedLinearCombination( + new Tensor([3, 4]), new Tensor([3, 4]), new Tensor([1, 2]), + new Tensor([]))) + .toThrowError(); + }); + + it('Non scalar c2 throws', () => { + expect( + () => g.fusedLinearCombination( + new Tensor([3, 4]), new Tensor([3, 4]), new Tensor([]), + new Tensor([1, 2]))) + .toThrowError(); + }); + + it('does not throw when shapes correct', () => { + expect(g.fusedLinearCombination( + new Tensor([3, 4]), new Tensor([3, 4]), new Tensor([]), + new Tensor([])) + .shape) + .toEqual([3, 4]); + }); +}); + +describe('Add validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different shapes throws', () => { + expect(() => g.add(new Tensor([5, 4]), new Tensor([1, 2, 3]))) + .toThrowError(); + }); + + it('Same size does not throw', () => { + expect(g.add(new Tensor([5, 4]), new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('Subtract validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different shapes throws', () => { + expect(() => g.subtract(new Tensor([5, 4]), new Tensor([1, 2, 3]))) + .toThrowError(); + }); + + it('Same size does not throw', () => { + expect(g.subtract(new Tensor([5, 4]), new Tensor([5, 4])).shape).toEqual([ + 5, 4 + ]); + }); +}); + +describe('Multiply validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different shapes throws', () => { + expect(() => g.multiply(new Tensor([5, 4]), new Tensor([1, 2, 3]))) + .toThrowError(); + }); + + it('Same size does not throw', () => { + expect(g.multiply(new Tensor([5, 4]), new Tensor([5, 4])).shape).toEqual([ + 5, 4 + ]); + }); +}); + +describe('Divide validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Different shapes throws', () => { + expect(() => g.divide(new Tensor([5, 4]), new Tensor([1, 2, 3]))) + .toThrowError(); + }); + + it('Same size does not throw', () => { + expect(g.divide(new Tensor([5, 4]), new Tensor([5, 4])).shape).toEqual([ + 5, 4 + ]); + }); +}); + +describe('Reduce sum validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('does not throw', () => { + expect(g.reduceSum(new Tensor([5, 4, 4, 9])).shape).toEqual([]); + }); +}); + +describe('Concat3d validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Non 3-rank tensor x1 throws', () => { + expect(() => g.concat3d(new Tensor([5, 4]), new Tensor([1, 2, 3]), 0)) + .toThrowError(); + }); + + it('Non 3-rank tensor x2 throws', () => { + expect(() => g.concat3d(new Tensor([5, 4, 1]), new Tensor([1, 2]), 0)) + .toThrowError(); + }); + + it('Axis=0 different shapes throws', () => { + expect(() => g.concat3d(new Tensor([5, 4, 1]), new Tensor([1, 2, 1]), 0)) + .toThrowError(); + }); + + it('Axis=1 different shapes throws', () => { + expect(() => g.concat3d(new Tensor([5, 4, 1]), new Tensor([1, 2, 1]), 0)) + .toThrowError(); + }); + + it('Axis=2 different shapes throws', () => { + expect(() => g.concat3d(new Tensor([5, 4, 1]), new Tensor([1, 2, 1]), 0)) + .toThrowError(); + }); + + it('Axis=0 shapes the same does not throw', () => { + expect(g.concat3d(new Tensor([5, 4, 3]), new Tensor([1, 4, 3]), 0).shape) + .toEqual([6, 4, 3]); + }); + + it('Axis=1 shapes the same does not throw', () => { + expect(g.concat3d(new Tensor([5, 3, 3]), new Tensor([5, 4, 3]), 1).shape) + .toEqual([5, 7, 3]); + }); + + it('Axis=2 shapes the same does not throw', () => { + expect(g.concat3d(new Tensor([5, 4, 3]), new Tensor([5, 4, 1]), 2).shape) + .toEqual([5, 4, 4]); + }); +}); + +describe('matmul validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Wrong rank x1 throws', () => { + expect(() => g.matmul(new Tensor([5, 4, 3]), new Tensor([1, 2]))) + .toThrowError(); + }); + + it('Wrong rank x2 throws', () => { + expect(() => g.matmul(new Tensor([5, 4]), new Tensor([1, 2, 3]))) + .toThrowError(); + }); + + it('Inner dimensions of matrix multiply do not match throws', () => { + expect(() => g.matmul(new Tensor([5, 4]), new Tensor([5, 5]))) + .toThrowError(); + }); + + it('Inner dimensions of matrix times vector does not match throws', () => { + expect(() => g.matmul(new Tensor([5, 4]), new Tensor([5]))).toThrowError(); + }); + + it('Inner dimensions of vector times matrix does not match throws', () => { + expect(() => g.matmul(new Tensor([5]), new Tensor([4, 5]))).toThrowError(); + }); + + it('Vector times vector shapes dont match throws', () => { + expect(() => g.matmul(new Tensor([5]), new Tensor([4]))).toThrowError(); + }); + + it('Matrix times matrix inner dimensions match does not throw', () => { + expect(g.matmul(new Tensor([5, 4]), new Tensor([4, 6])).shape).toEqual([ + 5, 6 + ]); + }); + + it('Vector times matrix inner dimensions match does not throw', () => { + expect(g.matmul(new Tensor([4]), new Tensor([4, 6])).shape).toEqual([6]); + }); + + it('Matrix times vector inner dimensions match does not throw', () => { + expect(g.matmul(new Tensor([4, 6]), new Tensor([6])).shape).toEqual([4]); + }); +}); + +describe('conv2d validation', () => { + let g: Graph; + let fieldSize: number; + let outputDepth: number; + let stride: number; + let zeroPad: number; + + beforeEach(() => { + g = new Graph(); + fieldSize = 4; + outputDepth = 10; + stride = 1; + zeroPad = 1; + }); + + it('Wrong rank x throws', () => { + expect( + () => g.conv2d( + new Tensor([5, 4]), new Tensor([1, 2, 3, 4]), + new Tensor([outputDepth]), fieldSize, outputDepth, stride, zeroPad)) + .toThrowError(); + }); + + it('Wrong rank weights throws', () => { + expect( + () => g.conv2d( + new Tensor([5, 4, 3]), new Tensor([1, 2, 3]), + new Tensor([outputDepth]), fieldSize, outputDepth, stride, zeroPad)) + .toThrowError(); + }); + + it('Wrong rank biases throws', () => { + expect( + () => g.conv2d( + new Tensor([5, 4, 3]), new Tensor([1, 2, 3, 4]), new Tensor([5, 5]), + fieldSize, outputDepth, stride, zeroPad)) + .toThrowError(); + }); + + it('Input depths dont match throws', () => { + expect( + () => g.conv2d( + new Tensor([5, 4, 3]), new Tensor([1, 2, 100, 4]), + new Tensor([outputDepth]), fieldSize, outputDepth, stride, zeroPad)) + .toThrowError(); + }); + + it('Shapes matches does not throw', () => { + const expectedShape = conv_util.computeOutputShape3D( + [5, 4, 3], fieldSize, outputDepth, stride, zeroPad); + expect(g.conv2d( + new Tensor([5, 4, 3]), new Tensor([1, 2, 3, 4]), + new Tensor([outputDepth]), fieldSize, outputDepth, stride, + zeroPad) + .shape) + .toEqual(expectedShape); + }); +}); + +describe('maxpool validation', () => { + let g: Graph; + let fieldSize: number; + let stride: number; + let zeroPad: number; + + beforeEach(() => { + g = new Graph(); + fieldSize = 4; + stride = 1; + zeroPad = 1; + }); + + it('Wrong rank x throws', () => { + expect(() => g.maxPool(new Tensor([5, 4]), fieldSize, stride, zeroPad)) + .toThrowError(); + }); + + it('Shapes matches does not throw', () => { + const expectedShape = conv_util.computeOutputShape3D( + [5, 4, 3], fieldSize, 3, stride, zeroPad); + expect(g.maxPool(new Tensor([5, 4, 3]), fieldSize, stride, zeroPad).shape) + .toEqual(expectedShape); + }); +}); + +describe('relu validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.relu(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('exp validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.exp(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('log validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.log(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('tanh validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.tanh(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('sigmoid validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.sigmoid(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('square validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Does not throw', () => { + expect(g.square(new Tensor([5, 4])).shape).toEqual([5, 4]); + }); +}); + +describe('softmaxCrossEntropy validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Shapes not equal throws', () => { + expect( + () => g.softmaxCrossEntropyCost( + new Tensor([5, 4]), new Tensor([5, 4, 3]))) + .toThrowError(); + }); + + it('Does not throw', () => { + expect( + g.softmaxCrossEntropyCost(new Tensor([5, 4]), new Tensor([5, 4])).shape) + .toEqual([]); + }); +}); + +describe('meanSquaredCost validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Shapes not equal throws', () => { + expect(() => g.meanSquaredCost(new Tensor([5, 4]), new Tensor([5, 4, 3]))) + .toThrowError(); + }); + + it('Does not throw', () => { + expect(g.meanSquaredCost(new Tensor([5, 4]), new Tensor([5, 4])).shape) + .toEqual([]); + }); +}); + +describe('argmaxEquals validation', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('Shapes not equal throws', () => { + expect(() => g.argmaxEquals(new Tensor([5, 4]), new Tensor([5, 4, 3]))) + .toThrowError(); + }); + + it('Does not throw', () => { + expect(g.argmaxEquals(new Tensor([5, 4]), new Tensor([5, 4])).shape) + .toEqual([1]); + }); +}); + +describe('Tensor', () => { + it('captures shape from constructor', () => { + const t = new Tensor([1, 2, 3, 4]); + expect(t.shape).toEqual([1, 2, 3, 4]); + }); + + it('has unique ascending ids', () => { + const a = new Tensor([]); + const b = new Tensor([]); + expect(b.id).toEqual(a.id + 1); + }); +}); diff --git a/src/graph_util.ts b/src/graph_util.ts new file mode 100644 index 0000000000..c1bd3c3137 --- /dev/null +++ b/src/graph_util.ts @@ -0,0 +1,132 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {ConstantNode, Graph, Node, PlaceholderNode, Tensor, VariableNode} from './graph'; +import * as priority_queue from './priority_queue'; +import {PriorityQueue} from './priority_queue'; +import {TensorArrayMap} from './tensor_array_map'; + +/** + * Given a target node in a graph, accumulate the set of all nodes that need to + * be evaluated in order to evaluate the target graph. Traversal stops anywhere + * a node's values are fed in externally via "feed dicts". + * @param nodes The nodes to be evaluated. + * @param terminatingNodes The set of nodes that stop traversal. + * @return The unordered set of nodes that need to be evaluated. + */ +export function getUnorderedEvaluationSet( + nodes: Node[], terminatingNodes: Node[]): Node[] { + const terminatingNodeMap: {[id: number]: Node} = {}; + const seen: {[id: number]: Node} = {}; + const set: Node[] = []; + const visit: Node[] = nodes.slice(); + terminatingNodes.forEach(node => terminatingNodeMap[node.id] = node); + /* Flood fill: While the 'to visit' stack is not empty, pop a node off of it. + * If the node has not yet been visited, add it to the set, mark it as seen, + * and enqueue all of its ancestor (input) nodes. */ + while (visit.length !== 0) { + const cur = visit.pop()!; + if (seen[cur.id] == null) { + if (terminatingNodeMap[cur.id] == null) { + Object.keys(cur.inputs) + .map(inputName => cur.inputs[inputName]) + .forEach(input => visit.push(input.node)); + } + set.push(cur); + seen[cur.id] = cur; + } + } + return set; +} + +/** + * Given a set of nodes, compute their order such that all dependent nodes are + * evaluated after their dependees. This is the 'inference order' for nodes in + * the operation graph. + * @param unorderedEvaluationSet The unordered set of nodes that need to be + * evaluated. + * @return The input nodes in forward evaluation order. + */ +export function getOrderedEvaluationSet(unorderedEvaluationSet: Node[]): + Node[] { + /* A priority queue is used, where the priority is the remaining number of + * unevaluated nodes whose inputs come from the element node. This guarantees + * that all downstream nodes will be dequeued before their ancestors. */ + const set: Node[] = []; + const nodeIndices: {[id: number]: number} = {}; + const pendingDependencies: {[id: number]: number} = {}; + + /* The queue priority callback looks at the number of pending dependencies of + * a given node. The queue index observer callback maintains the location of + * each node in the array, for priority updates. */ + const nodeQueue = new PriorityQueue( + (a: Node, b: Node) => priority_queue.defaultCompare( + pendingDependencies[a.id], pendingDependencies[b.id]), + (node: Node, newIndex: number) => nodeIndices[node.id] = newIndex); + + unorderedEvaluationSet.forEach(node => pendingDependencies[node.id] = 0); + + /* For every descendent of a node (output of ancestor is input to descendant), + * increment the 'pending dependency count' for the ancestor. This prepares + * the 'pending dependency count' as a priority map. */ + unorderedEvaluationSet.forEach( + node => Object.keys(node.inputs) + .map(key => node.inputs[key]) + .forEach(input => { + if (unorderedEvaluationSet.indexOf(input.node) !== -1) { + pendingDependencies[input.node.id]++; + } + })); + + unorderedEvaluationSet.forEach(node => nodeQueue.enqueue(node)); + + while (!nodeQueue.empty()) { + set.unshift(nodeQueue.dequeue()); + /* As each node is visited, decrement the 'pending dependency count' of + * each ancestor, and tell the priority queue that the priority has changed. + */ + Object.keys(set[0].inputs).map(key => set[0].inputs[key]).forEach(input => { + if (unorderedEvaluationSet.indexOf(input.node) === -1) { + return; + } + pendingDependencies[input.node.id]--; + nodeQueue.update(input.node, nodeIndices[input.node.id]); + }); + } + + return set; +} + +/** + * @return True iff the node is an input node. + */ +export function isInputNode(node: Node): boolean { + return Object.keys(node.inputs).length === 0; +} + +export function shouldBackProp(t: Tensor): boolean { + return !(t.node instanceof ConstantNode); +} + +export function isPassthroughNode(node: Node, map: TensorArrayMap): boolean { + const keys = Object.keys(node.inputs); + for (let i = 0; i < keys.length; i++) { + const input = node.inputs[keys[i]]; + if (map.get(input, true) === map.get(node.output, true)) { + return true; + } + } + return false; +} diff --git a/src/graph_util_test.ts b/src/graph_util_test.ts new file mode 100644 index 0000000000..7c48490e5c --- /dev/null +++ b/src/graph_util_test.ts @@ -0,0 +1,225 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {ConstantNode, Graph, Node, PlaceholderNode, ReLUNode, SplitNode, SquareNode, Tensor, VariableNode} from './graph'; +import * as graph_util from './graph_util'; +import {NDArray, Scalar} from './math/ndarray'; +import {TensorArrayMap} from './tensor_array_map'; + +class TestNode extends Node { + validate() {} +} + +describe('graph_util.getUnorderedEvaluationSet', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('returns only node in graph', () => { + const n = new TestNode(g, '', {}, new Tensor([])); + const path = graph_util.getUnorderedEvaluationSet([n], []); + expect(path.length).toEqual(1); + expect(path[0]).toBe(n); + }); + + it('returns both nodes in graph with two connected nodes', () => { + const t = new Tensor([]); + const s = new TestNode(g, '', {}, t); + const e = new TestNode(g, '', {'t': t}, new Tensor([])); + const path = graph_util.getUnorderedEvaluationSet([e], []); + expect(path.length).toEqual(2); + expect(path).toContain(s); + expect(path).toContain(e); + }); + + it('adds nodes in the termination set', () => { + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const n1 = new TestNode(g, '', {'t0': t0}, t1); + const n2 = new TestNode(g, '', {'t1': t1}, new Tensor([])); + const path = graph_util.getUnorderedEvaluationSet([n2], [n0]); + expect(path.length).toEqual(3); + expect(path).toContain(n0); + expect(path).toContain(n1); + expect(path).toContain(n2); + }); + + it('does not process inputs from nodes in the termination set', () => { + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const n1 = new TestNode(g, '', {'t0': t0}, t1); + const n2 = new TestNode(g, '', {'t1': t1}, new Tensor([])); + const path = graph_util.getUnorderedEvaluationSet([n2], [n1]); + expect(path.length).toEqual(2); + expect(path).toContain(n1); + expect(path).toContain(n2); + }); + + it('accumulates multiple inputs from nodes', () => { + const t0 = new Tensor([]); + const i0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const i1 = new TestNode(g, '', {}, t1); + const n = new TestNode(g, '', {'t0': t0, 't1': t1}, new Tensor([])); + const path = graph_util.getUnorderedEvaluationSet([n], []); + expect(path.length).toEqual(3); + expect(path).toContain(i0); + expect(path).toContain(i1); + expect(path).toContain(n); + }); + + it('enqueues each node once even if there are multiple paths to it', () => { + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const n1 = new TestNode(g, '', {'t0': t0}, t1); + const t2 = new Tensor([]); + const n2 = new TestNode(g, '', {'t0': t0}, t2); + const n3 = new TestNode(g, '', {'t1': t1, 't2': t2}, new Tensor([])); + const set = graph_util.getUnorderedEvaluationSet([n3], []); + expect(set.length).toEqual(4); + expect(set).toContain(n0); + expect(set).toContain(n1); + expect(set).toContain(n2); + expect(set).toContain(n3); + }); +}); + +describe('graph_util.getOrderedEvaluationSet', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('returns only node in unordered set', () => { + const n = new TestNode(g, '', {}, new Tensor([])); + expect(graph_util.getOrderedEvaluationSet([n])).toEqual([n]); + }); + + it('orders dependencies first (2 nodes)', () => { + // 0 => 1: [0 1] + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const n1 = new TestNode(g, '', {'t0': t0}, new Tensor([])); + const unordered = [n1, n0]; + const ordered = [n0, n1]; + expect(graph_util.getOrderedEvaluationSet(unordered)).toEqual(ordered); + }); + + it('orders dependencies first (3 nodes)', () => { + // 0 => 1, 1 => 2, 0 => 2: [0 1 2] + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const n1 = new TestNode(g, '', {'t0': t0}, t1); + const n2 = new TestNode(g, '', {'t0': t0, 't1': t1}, new Tensor([])); + const unordered = [n1, n2, n0]; + const ordered = [n0, n1, n2]; + expect(graph_util.getOrderedEvaluationSet(unordered)).toEqual(ordered); + }); + + it('orders dependencies first (5 nodes)', () => { + // 0 => 1, 0 => 2, 0 => 4 + // 1 => 3 + // 2 => 3 + // 3 => 4 + // [0 1 2 3 4] or [0 2 1 3 4] + const t0 = new Tensor([]); + const n0 = new TestNode(g, '', {}, t0); + const t1 = new Tensor([]); + const n1 = new TestNode(g, '', {'t0': t0}, t1); + const t2 = new Tensor([]); + const n2 = new TestNode(g, '', {'t0': t0}, t2); + const t3 = new Tensor([]); + const n3 = new TestNode(g, '', {'t1': t1, 't2': t2}, t3); + const t4 = new Tensor([]); + const n4 = new TestNode(g, '', {'t0': t0, 't3': t3}, new Tensor([])); + const path = graph_util.getOrderedEvaluationSet([n4, n3, n2, n1, n0]); + expect(path[0]).toBe(n0); + const n2n1 = (path[1] === n2) && (path[2] === n1); + const n1n2 = (path[1] === n1) && (path[2] === n2); + expect(n2n1 || n1n2).toBe(true); + expect(path[3]).toBe(n3); + expect(path[4]).toBe(n4); + }); +}); + +describe('graph_util.isInputNode', () => { + let g: Graph; + let nda: NDArray; + + beforeEach(() => { + g = new Graph(); + nda = NDArray.zeros([1]); + }); + + it('returns true for VariableNode', () => { + expect(graph_util.isInputNode(new VariableNode(g, '', nda))).toEqual(true); + }); + + it('returns true for PlaceholderNode', () => { + expect(graph_util.isInputNode(new PlaceholderNode(g, '', [1]))) + .toEqual(true); + }); + + it('returns true for ConstantNode', () => { + expect(graph_util.isInputNode(new ConstantNode(g, NDArray.zeros([1])))) + .toEqual(true); + }); + + it('returns false for ReLUNode', () => { + expect(graph_util.isInputNode(new ReLUNode(g, new Tensor([])))) + .toEqual(false); + }); +}); + +describe('graph_util.isPassthroughNode', () => { + let g: Graph; + + beforeEach(() => { + g = new Graph(); + }); + + it('returns false for a node that produces new NDArray', () => { + const x = g.placeholder('x', []); + const node = new SquareNode(g, x); + const map = new TensorArrayMap(); + const xVal = Scalar.new(3); + map.set(x, xVal); + const yVal = Scalar.new(9); + map.set(node.output, yVal); + + expect(graph_util.isPassthroughNode(node, map)).toBe(false); + xVal.dispose(); + yVal.dispose(); + }); + + it('returns true for a node that passes through the input', () => { + const x = g.placeholder('x', []); + const node = new SplitNode(g, x); + const map = new TensorArrayMap(); + const xVal = Scalar.new(3); + map.set(x, xVal); + map.set(node.output, xVal); + + expect(graph_util.isPassthroughNode(node, map)).toBe(true); + xVal.dispose(); + }); +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000000..dea2fe7577 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,37 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from './math/conv_util'; +import * as gpgpu_util from './math/webgl/gpgpu_util'; +import * as render_ndarray_gpu_util from './math/webgl/render_ndarray_gpu_util'; +import * as webgl_util from './math/webgl/webgl_util'; +import * as util from './util'; + +export {CheckpointLoader} from './checkpoint_loader'; +export {DataStats, InMemoryDataset} from './dataset'; +export {Graph, Tensor} from './graph'; +export {GraphRunner, GraphRunnerEventObserver, MetricReduction} from './graph_runner'; +export {ConstantInitializer, Initializer, NDArrayInitializer, OnesInitializer, RandomNormalInitializer, RandomTruncatedNormalInitializer, RandomUniformInitializer, VarianceScalingInitializer, ZerosInitializer} from './initializers'; +export {InCPUMemoryShuffledInputProviderBuilder, InGPUMemoryShuffledInputProviderBuilder, InputProvider} from './input_provider'; +export {MatrixOrientation, NDArrayMath} from './math/math'; +export {NDArrayMathCPU} from './math/math_cpu'; +export {NDArrayMathGPU} from './math/math_gpu'; +export {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from './math/ndarray'; +export {GPGPUContext} from './math/webgl/gpgpu_context'; +export {Optimizer} from './optimizer'; +export {CostReduction, FeedEntry, Session} from './session'; +export {SGDOptimizer} from './sgd_optimizer'; +// Second level exports. +export {conv_util, gpgpu_util, render_ndarray_gpu_util, util, webgl_util}; diff --git a/src/initializers.ts b/src/initializers.ts new file mode 100644 index 0000000000..52b1df5365 --- /dev/null +++ b/src/initializers.ts @@ -0,0 +1,125 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArray} from './math/ndarray'; + +/** + * Initializer interface, all initializer implement this interface. + */ +export interface Initializer { + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray; +} + +export class VarianceScalingInitializer implements Initializer { + constructor( + private scale = 1.0, + private mode: 'fan_in'|'fan_out'|'fan_avg' = 'fan_in', + private distribution: 'uniform'|'normal' = 'normal') {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + let n = 0; + if (this.mode === 'fan_in') { + n = inputUnits; + } else if (this.mode === 'fan_out') { + n = outputUnits; + } else if (this.mode === 'fan_avg') { + n = (inputUnits + outputUnits) / 2; + } else { + throw new Error( + 'Unexpected mode for variance scaling initializer: ' + this.mode); + } + + if (this.distribution === 'normal') { + return NDArray.randTruncatedNormal( + weightsShape, 0.0, Math.sqrt(this.scale / n)); + } else if (this.distribution === 'uniform') { + return NDArray.randUniform( + weightsShape, 0.0, Math.sqrt(3 * this.scale / n)); + } else { + throw new Error( + 'Unexpected distribution for variance scaling initializer: ' + + this.distribution); + } + } +} + +export class ZerosInitializer implements Initializer { + constructor() {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + return NDArray.zeros(weightsShape); + } +} + +export class OnesInitializer implements Initializer { + constructor() {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + const values = NDArray.zeros(weightsShape); + values.fill(1); + return values; + } +} + +export class ConstantInitializer implements Initializer { + constructor(private value = 0) {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + const values = NDArray.zeros(weightsShape); + values.fill(this.value); + return values; + } +} + +export class NDArrayInitializer implements Initializer { + constructor(private ndarray: NDArray) {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + return this.ndarray; + } +} + +export class RandomNormalInitializer implements Initializer { + constructor(private mean = 0, private stdev = .05) {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + return NDArray.randNormal(weightsShape, this.mean, this.stdev); + } +} + +export class RandomTruncatedNormalInitializer implements Initializer { + constructor(private mean = 0, private stdev = .05) {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + return NDArray.randTruncatedNormal(weightsShape, this.mean, this.stdev); + } +} + +export class RandomUniformInitializer implements Initializer { + constructor(private minval = -.05, private maxval = .05) {} + + initialize(weightsShape: number[], inputUnits: number, outputUnits: number): + NDArray { + return NDArray.randUniform(weightsShape, this.minval, this.maxval); + } +} diff --git a/src/input_provider.ts b/src/input_provider.ts new file mode 100644 index 0000000000..e62027ba56 --- /dev/null +++ b/src/input_provider.ts @@ -0,0 +1,169 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMath} from './math/math'; +import {NDArray} from './math/ndarray'; +import * as util from './util'; + +/** + * The interface for input providers. + */ +export interface InputProvider { + /** + * Get the next input as a copy. This is important because the data might + * get uploaded to the GPU and modify the original data. + * @param math NDArrayMath + */ + getNextCopy(math: NDArrayMath): NDArray; + /** + * Dispose the input copy. + * @param math NDArrayMath + * @param copy The copy provided from getNextCopy + */ + disposeCopy(math: NDArrayMath, copy: NDArray): void; +} + +/** + * A common interface for shuffled input provider builders. This returns + * InputProviders that are synchronized. + * @hidden + */ +export interface ShuffledInputProviderBuilder { + getInputProviders(): InputProvider[]; +} + +/** + * @hidden + */ +export abstract class InMemoryShuffledInputProviderBuilder implements + ShuffledInputProviderBuilder { + protected shuffledIndices: Uint32Array; + protected numInputs: number; + + protected idx = 0; + // Counter for how many times the current index has been called. Resets to 0 + // when it reaches the number of inputs. + protected inputCounter = 0; + protected epoch = 0; + + /** + * Constructs an `InMemoryShuffledInputProvider`. All of the inputs must be + * in memory. + * @param inputs All of the inputs, size: [number of inputs][number of + * examples]. + */ + constructor(protected inputs: NDArray[][]) { + this.shuffledIndices = util.createShuffledIndices(inputs[0].length); + this.numInputs = inputs.length; + + // Make sure the number of examples in each input matches. + const numExamples = this.inputs[0].length; + for (let i = 0; i < this.numInputs; i++) { + util.assert( + this.inputs[i].length === numExamples, + 'Number of examples must match across different inputs.'); + } + + // Make sure the shapes within inputs all match. + for (let i = 0; i < this.numInputs; i++) { + const inputShape = this.inputs[i][0].shape; + for (let j = 0; j < this.inputs[i].length; j++) { + util.assertShapesMatch(inputShape, this.inputs[i][j].shape); + } + } + } + + protected getCurrentExampleIndex(): number { + const returnIdx = this.idx; + + this.inputCounter++; + if (this.inputCounter >= this.numInputs) { + this.idx++; + this.inputCounter = 0; + + if (this.idx >= this.inputs[0].length) { + this.idx = 0; + this.epoch++; + } + } + return returnIdx; + } + + protected getNextInput(inputId: number): NDArray { + const currentExampleIndex = this.getCurrentExampleIndex(); + + return this.inputs[inputId][this.shuffledIndices[currentExampleIndex]]; + } + + getEpoch() { + return this.epoch; + } + + /** + * Returns input providers which shuffle the inputs and stay in sync. + */ + getInputProviders(): InputProvider[] { + const inputProviders: InputProvider[] = []; + + for (let i = 0; i < this.numInputs; i++) { + inputProviders.push(this.getInputProvider(i)); + } + return inputProviders; + } + + abstract getInputProvider(inputId: number): InputProvider; +} + +/** + * An in CPU memory ShuffledInputProviderBuilder that shuffles NDArrays on the + * CPU and keeps them mutually in sync. + */ +export class InCPUMemoryShuffledInputProviderBuilder extends + InMemoryShuffledInputProviderBuilder { + getInputProvider(inputId: number) { + const shuffledInputProvider = this; + + return { + getNextCopy(math: NDArrayMath): NDArray { + return NDArray.like(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy(math: NDArrayMath, copy: NDArray) { + copy.dispose(); + } + }; + } +} + +/** + * An in GPU memory ShuffledInputProviderBuilder that shuffles NDArrays on the + * GPU and keeps them mutually in sync. This is more performant than the CPU + * version as textures will stay in memory, however this is more GPU memory + * intensive as it keeps textures resident in GPU memory. + */ +export class InGPUMemoryShuffledInputProviderBuilder extends + InMemoryShuffledInputProviderBuilder { + getInputProvider(inputId: number) { + const shuffledInputProvider = this; + + return { + getNextCopy(math: NDArrayMath): NDArray { + return math.clone(shuffledInputProvider.getNextInput(inputId)); + }, + disposeCopy(math: NDArrayMath, copy: NDArray) { + copy.dispose(); + } + }; + } +} diff --git a/src/input_provider_test.ts b/src/input_provider_test.ts new file mode 100644 index 0000000000..0204926567 --- /dev/null +++ b/src/input_provider_test.ts @@ -0,0 +1,140 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {InCPUMemoryShuffledInputProviderBuilder} from './input_provider'; +import {NDArrayMathCPU} from './math/math_cpu'; +import {NDArrayMathGPU} from './math/math_gpu'; +import * as ndarray from './math/ndarray'; +import {Array1D, Scalar} from './math/ndarray'; + + +describe('InCPUMemoryShuffledInputProviderBuilder', () => { + let math: NDArrayMathCPU; + + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('ensure inputs stay in sync', () => { + const x1s = [Scalar.new(1), Scalar.new(2), Scalar.new(3)]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + const shuffledInputProvider = + new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s]); + + const [x1provider, x2provider] = shuffledInputProvider.getInputProviders(); + + const seenNumbers: {[key: number]: boolean} = {}; + for (let i = 0; i < x1s.length; i++) { + const x1 = x1provider.getNextCopy(math); + const x2 = x2provider.getNextCopy(math); + + expect(x1.get() * 10).toEqual(x2.get()); + + seenNumbers[x1.get()] = true; + seenNumbers[x2.get()] = true; + } + + // Values are shuffled, make sure we've seen everything. + const expectedSeenNumbers = [1, 2, 3, 10, 20, 30]; + for (let i = 0; i < expectedSeenNumbers.length; i++) { + expect(seenNumbers[expectedSeenNumbers[i]]).toEqual(true); + } + }); + + it('different number of examples', () => { + const x1s = [Scalar.new(1), Scalar.new(2)]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + expect(() => new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s])) + .toThrowError(); + }); + + it('different shapes within input', () => { + const x1s = [Scalar.new(1), Array1D.new([1, 2])]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + expect(() => new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s])) + .toThrowError(); + }); +}); + +describe('InGPUMemoryShuffledInputProviderBuilder', () => { + let math: NDArrayMathGPU; + + beforeEach(() => { + math = new NDArrayMathGPU(); + }); + + it('ensure inputs stay in sync', () => { + const x1s = [Scalar.new(1), Scalar.new(2), Scalar.new(3)]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + const shuffledInputProvider = + new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s]); + + const [x1provider, x2provider] = shuffledInputProvider.getInputProviders(); + + const seenNumbers: {[key: number]: boolean} = {}; + for (let i = 0; i < x1s.length; i++) { + const x1 = x1provider.getNextCopy(math); + const x2 = x2provider.getNextCopy(math); + + expect(x1.get() * 10).toEqual(x2.get()); + + seenNumbers[x1.get()] = true; + seenNumbers[x2.get()] = true; + + x1provider.disposeCopy(math, x1); + x2provider.disposeCopy(math, x1); + } + + // Values are shuffled, make sure we've seen everything. + const expectedSeenNumbers = [1, 2, 3, 10, 20, 30]; + for (let i = 0; i < expectedSeenNumbers.length; i++) { + expect(seenNumbers[expectedSeenNumbers[i]]).toEqual(true); + } + }); + + it('different number of examples', () => { + const x1s = [Scalar.new(1), Scalar.new(2)]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + expect(() => new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s])) + .toThrowError(); + + x1s.forEach(x1 => { + x1.dispose(); + }); + x2s.forEach(x2 => { + x2.dispose(); + }); + }); + + it('different shapes within input', () => { + const x1s = [Scalar.new(1), Array1D.new([1, 2])]; + const x2s = [Scalar.new(10), Scalar.new(20), Scalar.new(30)]; + + expect(() => new InCPUMemoryShuffledInputProviderBuilder([x1s, x2s])) + .toThrowError(); + + x1s.forEach(x1 => { + x1.dispose(); + }); + x2s.forEach(x2 => { + x2.dispose(); + }); + }); +}); \ No newline at end of file diff --git a/src/math/activation_functions.ts b/src/math/activation_functions.ts new file mode 100644 index 0000000000..0c357f2372 --- /dev/null +++ b/src/math/activation_functions.ts @@ -0,0 +1,84 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMath} from './math'; +import {NDArray, Scalar} from './ndarray'; + +/** A node's activation function and its derivative. */ +export interface ActivationFunction { + output(math: NDArrayMath, input: T): T; + der(math: NDArrayMath, input: T, output: T): T; +} + +export class TanHFunc implements ActivationFunction { + output(math: NDArrayMath, x: NDArray) { + return math.scope(() => { + return math.tanh(x); + }); + } + + der(math: NDArrayMath, x: NDArray, y: NDArray) { + return math.scope(() => { + const ySquared = math.elementWiseMul(y, y); + // 1 - y^2. + return math.scalarMinusArray(Scalar.ONE, ySquared); + }); + } +} + +export class ReLUFunc implements ActivationFunction { + output(math: NDArrayMath, x: NDArray) { + return math.scope(() => { + return math.relu(x); + }); + } + + der(math: NDArrayMath, x: NDArray, y: NDArray) { + return math.scope(() => { + return math.step(x); + }); + } +} + +export class SigmoidFunc implements ActivationFunction { + output(math: NDArrayMath, x: NDArray) { + return math.scope(() => { + return math.sigmoid(x); + }); + } + + der(math: NDArrayMath, x: NDArray, y: NDArray) { + return math.scope(() => { + // y * (1 - y) = y - y^2 + const ySquared = math.elementWiseMul(y, y); + return math.sub(y, ySquared); + }); + } +} + +export class SquareFunc implements ActivationFunction { + output(math: NDArrayMath, x: NDArray) { + return math.scope(() => { + return math.elementWiseMul(x, x); + }); + } + + der(math: NDArrayMath, x: NDArray, y: NDArray) { + return math.scope(() => { + // dy/dx = 2*x. + return math.scalarTimesArray(Scalar.TWO, x); + }); + } +} diff --git a/src/math/activation_functions_test.ts b/src/math/activation_functions_test.ts new file mode 100644 index 0000000000..cd98828d0a --- /dev/null +++ b/src/math/activation_functions_test.ts @@ -0,0 +1,97 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; + +import {ReLUFunc, SigmoidFunc, TanHFunc} from './activation_functions'; +import {NDArrayMathCPU} from './math_cpu'; +import {Array1D} from './ndarray'; + +describe('Activation functions', () => { + let math: NDArrayMathCPU; + + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('Tanh output', () => { + const x = Array1D.new([1, 3, -2, 100, -100, 0]); + const tanH = new TanHFunc(); + const y = tanH.output(math, x); + + expect(y.get(0)).toBeCloseTo(util.tanh(x.get(0))); + expect(y.get(1)).toBeCloseTo(util.tanh(x.get(1))); + expect(y.get(2)).toBeCloseTo(util.tanh(x.get(2))); + expect(y.get(3)).toBeCloseTo(1); + expect(y.get(4)).toBeCloseTo(-1); + expect(y.get(5)).toBeCloseTo(0); + }); + + it('Tanh derivative', () => { + const x = Array1D.new([1, 3, -2, 100, -100, 0]); + const tanH = new TanHFunc(); + const y = tanH.output(math, x); + const dx = tanH.der(math, x, y); + + expect(dx.get(0)).toBeCloseTo(1 - Math.pow(y.get(0), 2)); + expect(dx.get(1)).toBeCloseTo(1 - Math.pow(y.get(1), 2)); + expect(dx.get(2)).toBeCloseTo(1 - Math.pow(y.get(2), 2)); + }); + + it('ReLU output', () => { + const x = Array1D.new([1, 3, -2]); + const relu = new ReLUFunc(); + const y = relu.output(math, x); + + expect(y.get(0)).toBeCloseTo(1); + expect(y.get(1)).toBeCloseTo(3); + expect(y.get(2)).toBeCloseTo(0); + }); + + it('ReLU derivative', () => { + const x = Array1D.new([1, 3, -2]); + const relu = new ReLUFunc(); + const y = relu.output(math, x); + const dx = relu.der(math, x, y); + + expect(dx.get(0)).toBeCloseTo(1); + expect(dx.get(1)).toBeCloseTo(1); + expect(dx.get(2)).toBeCloseTo(0); + }); + + it('Sigmoid output', () => { + const x = Array1D.new([1, 3, -2, 100, -100, 0]); + const sigmoid = new SigmoidFunc(); + const y = sigmoid.output(math, x); + + expect(y.get(0)).toBeCloseTo(1 / (1 + Math.exp(-1))); + expect(y.get(1)).toBeCloseTo(1 / (1 + Math.exp(-3))); + expect(y.get(2)).toBeCloseTo(1 / (1 + Math.exp(2))); + expect(y.get(3)).toBeCloseTo(1); + expect(y.get(4)).toBeCloseTo(0); + expect(y.get(5)).toBeCloseTo(0.5); + }); + + it('Sigmoid derivative', () => { + const x = Array1D.new([1, 3, -2, 100, -100, 0]); + const sigmoid = new SigmoidFunc(); + const y = sigmoid.output(math, x); + const dx = sigmoid.der(math, x, y); + + expect(dx.get(0)).toBeCloseTo(y.get(0) * (1 - y.get(0))); + expect(dx.get(1)).toBeCloseTo(y.get(1) * (1 - y.get(1))); + expect(dx.get(2)).toBeCloseTo(y.get(2) * (1 - y.get(2))); + }); +}); diff --git a/src/math/concat3d_util.ts b/src/math/concat3d_util.ts new file mode 100644 index 0000000000..2f5fda3aec --- /dev/null +++ b/src/math/concat3d_util.ts @@ -0,0 +1,49 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; + +export function assertConcat3DShapesMatch( + x1Shape: number[], x2Shape: number[], axis: number, + errorMessagePrefix = '') { + util.assert( + x1Shape.length === 3, + errorMessagePrefix + 'Concat3D x1 shape should be of rank 3.'); + util.assert( + x2Shape.length === 3, + errorMessagePrefix + 'Concat3D x2 shape should be of rank 3.'); + + util.assert( + axis >= 0 && axis < 3, 'Axis for concat3D must be between 0 and 2.'); + + for (let i = 0; i < 3; i++) { + util.assert( + (i === axis) || (x1Shape[i] === x2Shape[i]), + errorMessagePrefix + + `Shape (${x1Shape}) does not match (${x2Shape}) along ` + + `non-concatenated axis.`); + } +} + +export function computeConcat3DOutputShape( + x1Shape: number[], x2Shape: number[], + axis: number): [number, number, number] { + util.assert(x1Shape.length === 3, 'Concat3D x1 shape should be of rank 3.'); + util.assert(x2Shape.length === 3, 'Concat3D x2shape should be of rank 3.'); + + const outputShape = x1Shape.slice(); + outputShape[axis] += x2Shape[axis]; + return outputShape as [number, number, number]; +} \ No newline at end of file diff --git a/src/math/concat3d_util_test.ts b/src/math/concat3d_util_test.ts new file mode 100644 index 0000000000..9f72e8a4c9 --- /dev/null +++ b/src/math/concat3d_util_test.ts @@ -0,0 +1,66 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../test_util'; +import * as concat3d_util from './concat3d_util'; + +describe('concat3d_util.assertConcat3DShapesMatch', () => { + it('Non-3D tensor x1', () => { + const assertFn = () => { + concat3d_util.assertConcat3DShapesMatch([1], [1, 2, 3], 1); + }; + + expect(assertFn).toThrow(); + }); + + it('Non-3D tensor x2', () => { + const assertFn = () => { + concat3d_util.assertConcat3DShapesMatch([1, 2, 3], [2, 3], 1); + }; + + expect(assertFn).toThrow(); + }); + + it('axis out of bound', () => { + const assertFn = () => { + concat3d_util.assertConcat3DShapesMatch([1, 2, 3], [1, 2, 3], 4); + }; + + expect(assertFn).toThrow(); + }); + + it('non-axis shape mismatch', () => { + const assertFn = () => { + concat3d_util.assertConcat3DShapesMatch([2, 3, 3], [2, 2, 4], 2); + }; + + expect(assertFn).toThrow(); + }); + + it('shapes line up', () => { + const assertFn = () => { + concat3d_util.assertConcat3DShapesMatch([2, 3, 3], [2, 3, 4], 2); + }; + + expect(assertFn).not.toThrow(); + }); +}); + +describe('concat3d_util.computeConcat3DOutputShape', () => { + it('compute output shape, axis=0', () => { + expect(concat3d_util.computeConcat3DOutputShape([2, 2, 3], [1, 2, 3], 0)) + .toEqual([3, 2, 3]); + }); +}); \ No newline at end of file diff --git a/src/math/conv_util.ts b/src/math/conv_util.ts new file mode 100644 index 0000000000..36d8455cda --- /dev/null +++ b/src/math/conv_util.ts @@ -0,0 +1,73 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; + +export function computeOutputShape3D( + inputShapeRowColDepth: [number, number, number], fieldSize: number, + depth: number, stride: number, zeroPad?: number): [number, number, number] { + if (zeroPad == null) { + zeroPad = computeDefaultPad(inputShapeRowColDepth, fieldSize, stride); + } + const inputRows = inputShapeRowColDepth[0]; + const inputCols = inputShapeRowColDepth[1]; + const outputRows = (inputRows - fieldSize + 2 * zeroPad) / stride + 1; + util.assert( + util.isInt(outputRows), + `The output # of rows (${outputRows}) must be an integer. Change the ` + + `stride and/or zero pad parameters`); + + const outputCols = (inputCols - fieldSize + 2 * zeroPad) / stride + 1; + util.assert( + util.isInt(outputCols), + `The output # of columns (${outputCols}) must be an integer. Change ` + + `the stride and/or zero pad parameters`); + + return [outputRows, outputCols, depth]; +} + +export function computeDefaultPad( + inputShape: [number, number, number], fieldSize: number, + stride: number): number { + return Math.floor((inputShape[0] * (stride - 1) - stride + fieldSize) / 2); +} + +export function computeTexShapeFrom3D( + shapeRowColDepth: [number, number, number]): [number, number] { + return [shapeRowColDepth[0], shapeRowColDepth[1] * shapeRowColDepth[2]]; +} + +export function computeWeightsShape4D( + inputDepth: number, outputDepth: number, + fSize: number): [number, number, number, number] { + return [fSize, fSize, inputDepth, outputDepth]; +} + +export function computeWeightsTexShape( + inputDepth: number, outputDepth: number, + fieldSize: number): [number, number] { + return [fieldSize * fieldSize * inputDepth, outputDepth]; +} + +export function computeBiasesTexShape(outputDepth: number): [number, number] { + return [1, outputDepth]; +} + +export function computeDilatedRC( + rc: [number, number], origStride: number): [number, number] { + const rowsDilated = (rc[0] - 1) * origStride + 1; + const colsDilated = (rc[1] - 1) * origStride + 1; + return [rowsDilated, colsDilated]; +} diff --git a/src/math/copy2d_util.ts b/src/math/copy2d_util.ts new file mode 100644 index 0000000000..6b2f6e750e --- /dev/null +++ b/src/math/copy2d_util.ts @@ -0,0 +1,27 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export function validateShapes( + sourceSize: [number, number], destSize: [number, number]) { + const srcArea = sourceSize[0] * sourceSize[1]; + const dstArea = destSize[0] * destSize[1]; + if (srcArea !== dstArea) { + const srcStr = '[' + sourceSize[0] + ', ' + sourceSize[1] + ']'; + const dstStr = '[' + destSize[0] + ', ' + destSize[1] + ']'; + throw new Error( + 'copy2D shapes have different areas:\n sourceSize ' + srcStr + + ', area ' + srcArea + '\n destSize ' + dstStr + ', area ' + dstArea); + } +} diff --git a/src/math/cost_functions.ts b/src/math/cost_functions.ts new file mode 100644 index 0000000000..44590a0878 --- /dev/null +++ b/src/math/cost_functions.ts @@ -0,0 +1,49 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMath} from './math'; +import {NDArray, Scalar} from './ndarray'; + +/** + * An error function and its derivative. + */ +export interface ElementWiseCostFunction { + cost(math: NDArrayMath, x1: T, x2: T): T; + der(math: NDArrayMath, x1: T, x2: T): T; + dispose(): void; +} + +export class SquareCostFunc implements ElementWiseCostFunction { + private halfOne = Scalar.new(0.5); + + cost(math: NDArrayMath, x1: NDArray, x2: NDArray): NDArray { + const diff = math.sub(x1, x2); + const diffSquared = math.elementWiseMul(diff, diff); + const result = math.scalarTimesArray(this.halfOne, diffSquared); + + diff.dispose(); + diffSquared.dispose(); + + return result; + } + + der(math: NDArrayMath, x1: NDArray, x2: NDArray): NDArray { + return math.sub(x1, x2); + } + + dispose() { + this.halfOne.dispose(); + } +} diff --git a/src/math/cost_functions_test.ts b/src/math/cost_functions_test.ts new file mode 100644 index 0000000000..32409af90e --- /dev/null +++ b/src/math/cost_functions_test.ts @@ -0,0 +1,49 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {SquareCostFunc} from './cost_functions'; +import {NDArrayMathCPU} from './math_cpu'; +import {Array1D} from './ndarray'; + +describe('Cost functions', () => { + let math: NDArrayMathCPU; + + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('Square cost', () => { + const y = Array1D.new([1, 3, -2]); + const target = Array1D.new([0, 3, -1.5]); + const square = new SquareCostFunc(); + const cost = square.cost(math, y, target); + + // The cost function is 1/2 * (y - target)^2 + expect(cost.get(0)).toBeCloseTo(1 / 2); + expect(cost.get(1)).toBeCloseTo(0 / 2); + expect(cost.get(2)).toBeCloseTo(0.25 / 2); + }); + + it('Square derivative', () => { + const y = Array1D.new([1, 3, -2]); + const target = Array1D.new([0, 3, -1.5]); + const square = new SquareCostFunc(); + const dy = square.der(math, y, target); + + expect(dy.get(0)).toBeCloseTo(1); + expect(dy.get(1)).toBeCloseTo(0); + expect(dy.get(2)).toBeCloseTo(-0.5); + }); +}); // Close describe. diff --git a/src/math/math.ts b/src/math/math.ts new file mode 100644 index 0000000000..2c445c2549 --- /dev/null +++ b/src/math/math.ts @@ -0,0 +1,1065 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; +import * as concat3d_util from './concat3d_util'; +import * as copy2d_util from './copy2d_util'; + +import {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from './ndarray'; + +export type ScopeResult = NDArray[]|NDArray|void; + +export abstract class NDArrayMath { + private ndarrayScopes: NDArray[][] = []; + private activeScope: NDArray[]; + + private ndarraysToKeep: NDArray[][] = []; + private activeScopeNDArraysToKeep: NDArray[] = []; + + /** + * @param safeMode In safe mode, you must use math operations inside + * a math.scope() which will automatically clean up intermediate NDArrays. + */ + constructor(private safeMode: boolean) {} + + /** + * Create a new math scope. Put chained math operations inside a scope + * function closure so that the library automatically cleans up NDArrays + * from intermediate math operations. You must create a scope in safe mode + * to call math operations. If a result is returned from the scope, it will + * also be tracked, which means there must be yet another wrapping scope. + * @param scopeFn The function to execute with chained math operations. + */ + scope( + scopeFn: + (keep: (ndarray: T1) => T1, + track: (ndarray: T2) => T2) => T) { + this.startScope(); + + const keepFn = (ndarray: T): T => this.keep(ndarray); + const trackFn = (ndarray: T): T => this.track(ndarray); + const result = scopeFn(keepFn, trackFn); + + this.endScope(result); + + return result; + } + + /** + * Start a scope. Use this with endScope() to achieve the same functionality + * as scope() without the need for a function closure. + */ + startScope() { + const newScope: NDArray[] = []; + this.ndarrayScopes.push(newScope); + this.activeScope = newScope; + + const newNDArraysToKeep: NDArray[] = []; + this.ndarraysToKeep.push(newNDArraysToKeep); + this.activeScopeNDArraysToKeep = newNDArraysToKeep; + } + + /** + * End a scope. Use this with startScope() to achieve the same functionality + * as scope() without the need for a function closure. + */ + endScope(result: ScopeResult) { + // Dispose the current scope. + for (let i = 0; i < this.activeScope.length; i++) { + const ndarray = this.activeScope[i]; + + if (this.isNDArrayDataInList(ndarray, this.activeScopeNDArraysToKeep) || + (result != null && result instanceof NDArray && + ndarray.getData() === (result as NDArray).getData())) { + continue; + } + ndarray.dispose(); + } + + // Pop the current scope. + this.ndarrayScopes.pop(); + this.activeScope = this.ndarrayScopes.length === 0 ? + null! : + this.ndarrayScopes[this.ndarrayScopes.length - 1]; + + // Track the current result in the parent scope. + if (result instanceof NDArray && + !this.isNDArrayDataInList(result, this.activeScopeNDArraysToKeep)) { + this.track(result); + } else if (Array.isArray(result)) { + result.forEach(r => { + if (r instanceof NDArray && + !this.isNDArrayDataInList(r, this.activeScopeNDArraysToKeep)) { + this.track(r); + } + }); + } + + this.ndarraysToKeep.pop(); + this.activeScopeNDArraysToKeep = this.ndarraysToKeep.length === 0 ? + null! : + this.ndarraysToKeep[this.ndarraysToKeep.length - 1]; + } + + private isNDArrayDataInList(ndarray: NDArray, ndarrayList: NDArray[]) { + for (let i = 0; i < ndarrayList.length; i++) { + if (ndarrayList[i].getData() === ndarray.getData()) { + return true; + } + } + return false; + } + + /** + * Keeps an NDArray in the current scope from being disposed automatically. + * @param result The NDArray to keep from being disposed. + */ + keep(result: T): T { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error( + 'You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScopeNDArraysToKeep.push(result); + return result; + } + + /** + * Tracks an NDArray in the current scope to be automatically cleaned up when + * the current scope ends, and returns the value. + * @param result The NDArray to track in the current scope. + */ + track(result: T): T { + if (this.activeScope == null) { + if (this.safeMode) { + throw new Error( + 'You are using math in safe mode. Enclose all ' + + 'math.method() calls inside a scope: ' + + 'math.scope(() => {math.method();...}) to avoid memory ' + + 'leaks.'); + } + return result; + } + this.activeScope.push(result); + return result; + } + + /** + * Computes the dot product of two matrices, A * B. These must be matrices, + * use matrixTimesVector and vectorTimesMatrix, dotProduct, and outerProduct + * in other cases. + * @param a First matrix in dot product operation. + * @param b Second matrix in dot product operation. + * @param aOrientation The MatrixOrientation of A. If using TRANSPOSED, will + * compute A^T * B. + * @param bOrientation The MatrixOrientation of B. If using TRANSPOSED, will + * compute A * B^T. + */ + matMul( + a: Array2D, b: Array2D, aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Array2D { + const innerShapeA = + (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + const innerShapeB = + (bOrientation === MatrixOrientation.REGULAR) ? b.shape[0] : b.shape[1]; + + util.assert( + a.rank === 2 && b.rank === 2, + `Error in matMul: inputs must be rank 2, got ranks ${a.rank}` + + `and ${b.rank}.`); + + util.assert( + innerShapeA === innerShapeB, + `Error in matMul: inner shapes (${innerShapeA}) and (` + + `${innerShapeB}) of NDArrays with shapes ${a.shape} and ` + + `${b.shape} and orientations ${MatrixOrientation[aOrientation]}` + + ` and ${MatrixOrientation[bOrientation]} must match.`); + + return this.track(this.matMulInternal(a, b, aOrientation, bOrientation)); + } + protected abstract matMulInternal( + a: Array2D, b: Array2D, aOrientation: MatrixOrientation, + bOrientation: MatrixOrientation): Array2D; + + /** + * Computes the dot product of a vector and a matrix, v * B. + * @param v The vector in dot product operation. + * @param matrix The matrix in dot product operation. + */ + vectorTimesMatrix(v: Array1D, matrix: Array2D): Array1D { + util.assert( + v.rank === 1, + `Error in vectorTimesMatrix: first input must be rank 1, but got ` + + `rank ${v.rank}.`); + util.assert( + matrix.rank === 2, + `Error in vectorTimesMatrix: second input must be rank 2, but got ` + + `rank ${matrix.rank}.`); + util.assert( + v.size === matrix.shape[0], + `Error in vectorTimesMatrix: size of first rank 1 input (${v.size}) ` + + `must match inner dimension of second rank 2 input, but got ` + + `rank ${matrix.rank}.`); + + return this.matMul(v.as2D(1, v.size), matrix).as1D(); + } + + /** + * Computes the dot product of a matrix and vector, A * v. + * @param matrix The matrix in dot product operation. + * @param v The vector in dot product operation. + */ + matrixTimesVector(matrix: Array2D, v: Array1D): Array1D { + util.assert( + v.rank === 1, + `Error in vectorTimesMatrix: second input must rank 1, but got ` + + `rank ${v.rank}.`); + util.assert( + matrix.rank === 2, + `Error in vectorTimesMatrix: first input must be a rank 2, but got ` + + `rank ${matrix.rank}.`); + util.assert( + v.size === matrix.shape[1], + `Error in vectorTimesMatrix: size of first rank 1 input ${v.size} ` + + `must match inner dimension of second rank 2 input, but got ` + + `shape ${matrix.shape}.`); + + return this.matMul(matrix, v.as2D(v.size, 1)).as1D(); + } + + /** + * Computes the dot product of two vectors, v1 * v2. + * @param v1 The first vector in the dot product operation. + * @param v2 The second vector in the dot product operation. + */ + dotProduct(v1: Array1D, v2: Array1D): Scalar { + util.assert( + v1.rank === 1 && v2.rank === 1, + `Error in dotProduct: inputs must be rank 1, but got ranks ` + + `${v1.rank} and ${v2.rank}.`); + util.assert( + v1.size === v2.size, + `Error in dotProduct: size of inputs (${v1.size}) and (` + + `${v2.size}) must match.`); + return this.matMul(v1.as2D(1, v1.size), v2.as2D(v2.size, 1)).asScalar(); + } + + /** + * Computes the outer product of two vectors, v1 and v2. + * @param v1 The first vector in the outer product operation. + * @param v2 The second vector in the dot product operation. + */ + outerProduct(v1: Array1D, v2: Array1D): Array2D { + util.assert( + v1.rank === 1 && v2.rank === 1, + `Error in outerProduct: inputs must be rank 1, but got ranks ` + + `${v1.rank} and ${v2.rank}.`); + + return this.matMul(v1.as2D(v1.size, 1), v2.as2D(1, v2.size)); + } + + /////////////// + // Shape ops // + /////////////// + + /** + * Clones an NDArray of any shape. + * @param ndarray The NDArray to clone. + */ + clone(ndarray: T): T { + return this.track(this.cloneInternal(ndarray)); + } + protected abstract cloneInternal(ndarray: T): T; + + /** + * Reshapes an NDArray to a new shape. The size of the input NDArray must + * match the size of the requested shape. + * @param ndarray The input NDArray. + * @param newShape The new shape to reshape the NDArray to. Must be the same + * size as the NDArray. + */ + reshape( + ndarray: T1, newShape: number[]): T2 { + util.assert( + ndarray.size === util.sizeFromShape(newShape), + `Error in reshape: old size ${ndarray.size} must match new size ` + + `${util.sizeFromShape(newShape)}.`); + return this.track(this.reshapeInternal(ndarray, newShape)); + } + protected abstract reshapeInternal( + ndarray: T1, newShape: number[]): T2; + + /** + * Extracts a slice from a matrix. The operation extraces a slice from input + * that starts at coordinates `begin` and is of size `size`. + * @param input The input matrix to slice from. + * @param begin The 2D coordinates in the input matrix to start the slice + * from. + * @param size The sice of the 2D window to slice. + */ + slice2D(input: Array2D, begin: [number, number], size: [number, number]): + Array2D { + util.assert( + begin[0] + size[0] <= input.shape[0] && + begin[1] + size[1] <= input.shape[1], + `Error in slice2D: requested start position ${begin} and size ` + + `${size} would overflow input of shape ${input.shape}.`); + return this.track(this.slice2DInternal(input, begin, size)); + } + protected abstract slice2DInternal( + input: Array2D, begin: [number, number], size: [number, number]): Array2D; + + /** + * Copies a window from the `source` matrix starting at `sourceBegin` and is + * of size `sourceSize` to a window in the `dest` matrix starting at + * `destBegin` and is of size `destSize`/ + * @param source The source matrix to copy from. + * @param sourceBegin The coordinates to start the copy from. + * @param sourceSize The size of the copy window. + * @param dest The destination matrix to copy to. + * @param destBegin The coordinates in `dest` to copy to. + * @param destSize The size of the destination window. + */ + copy2D( + source: Array2D, sourceBegin: [number, number], + sourceSize: [number, number], dest: Array2D, destBegin: [number, number], + destSize: [number, number]) { + util.assert( + sourceBegin[0] + sourceSize[0] <= source.shape[0] && + sourceBegin[1] + sourceSize[1] <= source.shape[1], + `Error in copy2D: requested source start position ${sourceBegin} ` + + `and source size ${sourceSize} would overflow source NDArray` + + `of shape ${source.shape}.`); + util.assert( + destBegin[0] + destSize[0] <= dest.shape[0] && + destBegin[1] + destSize[1] <= dest.shape[1], + `Error in copy2D: requested dest start position ${destBegin} ` + + `and source size ${destSize} would overflow dest NDArray of` + + `shape ${dest.shape}.`); + copy2d_util.validateShapes(sourceSize, destSize); + + return this.copy2DInternal( + source, sourceBegin, sourceSize, dest, destBegin, destSize); + } + protected abstract copy2DInternal( + source: Array2D, sourceBegin: [number, number], + sourceSize: [number, number], dest: Array2D, destBegin: [number, number], + destSize: [number, number]): void; + + /** + * Concatenates two 3D ndarrays along a given axis. + * + * For example, if: + * A: shape(2, 1, 3) = | r1, g1, b1 | + * | r2, g2, b2 | + * + * B: shape(2, 1, 3) = | r3, g3, b3 | + * | r4, g4, b4 | + * + * C = concat3D(A, B, axis) + * + * if axis = 0: + * C: shape(4, 1, 3) = | r1, g1, b1 | + * | r2, g2, b2 | + * | r3, g3, b3 | + * | r4, g4, b4 | + * + * if axis = 1: + * C: shape(2, 2, 3) = | r1, g1, b1, r3, g3, b3 | + * | r2, g2, b2, r4, g4, b4 | + * + * if axis = 2: + * C = shape(2, 1, 6) = | r1, g1, b1, r3, g3, b3 | + * | r2, g2, b2, r4, g4, b4 | + * + * @param ndarray1 The first array to concat. + * @param ndarray2 The second array to conat. + * @param axis The axis to concate along. + */ + concat3D(ndarray1: Array3D, ndarray2: Array3D, axis: number): Array3D { + concat3d_util.assertConcat3DShapesMatch( + ndarray1.shape, ndarray2.shape, axis, 'Error in concat3d: '); + return this.track(this.concat3DInternal(ndarray1, ndarray2, axis)); + } + protected abstract concat3DInternal( + ndarray1: Array3D, ndarray2: Array3D, axis: number): Array3D; + + /////////////////// + // Reduction ops // + /////////////////// + + /** + * Computes the the log(sum(e ^ x)) for each x in the input ndarray. + * @param ndarray The input NDArray to compute the logSumExp over. + */ + logSumExp(ndarray: NDArray): Scalar { + return this.track(this.logSumExpInternal(ndarray)); + } + protected abstract logSumExpInternal(ndarray: NDArray): Scalar; + + /** + * Computes the sum of all the entries in the input NDArray. + * @param ndarray The input NDArray to compute the sum over. + */ + sum(ndarray: NDArray): Scalar { + return this.track(this.sumInternal(ndarray)); + } + protected abstract sumInternal(ndarray: NDArray): Scalar; + + /** + * Computes the flattened index of the minimum element in the ndarray. + * @param ndarray The input NDArray. + */ + argMin(ndarray: NDArray): Scalar { + return this.track(this.argMinInternal(ndarray)); + } + protected abstract argMinInternal(ndarray: NDArray): Scalar; + + /** + * Computes the flattened index of the maximum element in the ndarray. + * @param ndarray The input NDArray. + */ + argMax(ndarray: NDArray): Scalar { + return this.track(this.argMaxInternal(ndarray)); + } + protected abstract argMaxInternal(ndarray: NDArray): Scalar; + + /** + * Returns a 1 if the argMax of x1 and x2 are the same, otherwise 0. + * @param x1 The first input NDArray. + * @param x2 The second input NDArray. + */ + argMaxEquals(x1: NDArray, x2: NDArray): Scalar { + util.assertShapesMatch(x1.shape, x2.shape, 'Error in argMaxEquals: '); + return this.track(this.argMaxEqualsInternal(x1, x2)); + } + protected abstract argMaxEqualsInternal(x1: NDArray, x2: NDArray): Scalar; + + /** + * Computes the top K values and flattened indices. + * @param ndarray The input NDArray. + * @param k How many top values to compute. + */ + topK(ndarray: NDArray, k: number): {values: Array1D, indices: Array1D} { + util.assert( + k <= ndarray.size, + `Error in topK: k value (${k}) must be less than size of input ` + + `ndarray, got shape ${ndarray.shape}.`); + const result = this.topKInternal(ndarray, k); + this.track(result.values); + this.track(result.indices); + return result; + } + protected abstract topKInternal(ndarray: NDArray, k: number): + {values: Array1D, indices: Array1D}; + + /** + * Computes the minimum value from the input. + * @param ndarray The input NDArray. + */ + min(ndarray: NDArray): Scalar { + return this.track(this.minInternal(ndarray)); + } + protected abstract minInternal(ndarray: NDArray): Scalar; + + /** + * Computes the maximum value from the input. + * @param ndarray The input NDArray. + */ + max(ndarray: NDArray): Scalar { + return this.track(this.maxInternal(ndarray)); + } + protected abstract maxInternal(ndarray: NDArray): Scalar; + + /** + * Computes the softmax normalized vector from the input vector. + * @param x The input vector. + */ + softmax(x: Array1D): Array1D { + return this.scope(() => { + // Do it in log space for numerical stability. + // exp(X - logSumExp(X)) + const lse = this.logSumExp(x); + const logResult = this.arrayMinusScalar(x, lse); + return this.exp(logResult); + }); + } + + ////////////////////// + // Element-wise ops // + ////////////////////// + + /** + * Switches dimensions of the input NDArray. + * @param a The input NDArray. + * @param newDim The new indices that define which shapes values to switch. + */ + switchDim(a: T, newDim: number[]): T { + util.assert( + a.rank === newDim.length, + `Error in switchDim: length of input shape ${a.shape} ` + + `must match size of newDim array ${newDim}.`); + return this.track(this.switchDimInternal(a, newDim)); + } + protected abstract switchDimInternal( + a: T, newDim: number[]): T; + + /** + * Computes a scalar plus NDArray, c + A. + * @param c The scalar c in c + A. + * @param a The NDArray A in c + A. + */ + scalarPlusArray(c: Scalar, a: T): T { + util.assert( + c.size === 1, + `Error in scalarPlusArray: first argument must be rank 0, but got ` + + `rank ${c.rank}.`); + return this.track(this.scalarPlusArrayInternal(c, a)); + } + protected abstract scalarPlusArrayInternal( + c: Scalar, a: T): T; + + /** + * Computes a scalar minus NDArray, c - A. + * @param c The scalar c in c - A. + * @param a The NDArray A in c - A. + */ + scalarMinusArray(c: Scalar, a: T): T { + util.assert( + c.size === 1, + `Error in scalarMinusArray: first argument must be rank 0, but got ` + + `rank ${c.rank}.`); + return this.track(this.scalarMinusArrayInternal(c, a)); + } + protected abstract scalarMinusArrayInternal( + c: Scalar, a: T): T; + + /** + * Computes a scalar minus NDArray, A - c. + * @param a The NDArray A in A - c. + * @param c The scalar c in A - c. + */ + arrayMinusScalar(a: T, c: Scalar): T { + util.assert( + c.size === 1, + `Error in arrayMinusScalar: second argument must be rank 0, but ` + + `got rank ${c.rank}.`); + return this.track(this.arrayMinusScalarInternal(a, c)); + } + protected abstract arrayMinusScalarInternal( + a: T, c: Scalar): T; + + /** + * Computes -1 * A element-wise. + * @param a The input array. + */ + neg(a: T): T { + return this.track(this.negInternal(a)); + } + protected abstract negInternal(a: T): T; + + /** + * Adds two NDArrays element-wise, A + B. Inputs must be the same shape. + * @param a The first NDArray to add element-wise. + * @param b The second NDArray to add element-wise. + */ + add(a: T, b: T): T { + util.assertShapesMatch(a.shape, b.shape, 'Error in add: '); + return this.track(this.addInternal(a, b)); + } + protected abstract addInternal(a: T, b: T): T; + + /** + * Subtracts two NDArrays element-wise, A - B. Inputs must be the same shape. + * @param a The first NDArray to subtract element-wise. + * @param b The second NDArray to subtract element-wise. + */ + sub(a: T, b: T): T { + util.assertShapesMatch(a.shape, b.shape, 'Error in sub: '); + return this.track(this.subInternal(a, b)); + } + protected abstract subInternal(a: T, b: T): T; + + /** + * Multiplies two NDArrays element-wise (hadamard product), A * B. Inputs must + * be the same shape. + * @param a The first NDArray to multiply element-wise. + * @param b The second NDArray to multiply element-wise. + */ + elementWiseMul(a: T, b: T): T { + util.assertShapesMatch(a.shape, b.shape, 'Error in elementWiseMul: '); + return this.track(this.elementWiseMulInternal(a, b)); + } + protected abstract elementWiseMulInternal(a: T, b: T): T; + + /** + * Divides two NDArrays element-wise (hadamard product), A / B. Inputs must be + * the same shape. + * @param a The first NDArray to divide element-wise. + * @param b The second NDArray to divide element-wise. + */ + divide(a: T, b: T): T { + util.assertShapesMatch(a.shape, b.shape, 'Error in divide: '); + return this.track(this.divideInternal(a, b)); + } + protected abstract divideInternal(a: T, b: T): T; + + /** + * Computes a scalar divided by an NDArray, broadcasted over the NDArray, c / + * A. + * @param c The scalar value in c / A. + * @param a The NDArray value in c / A. + */ + scalarDividedByArray(c: Scalar, a: T): T { + util.assert( + c.size === 1, + `Error in scalarDividedByArray: first argument must be rank 0, but ` + + `got NDArray of rank ${c.rank}.`); + return this.track(this.scalarDividedByArrayInternal(c, a)); + } + protected abstract scalarDividedByArrayInternal( + c: Scalar, a: T): T; + + /** + * Computes an NDArray divided by a scalar, broadcasted over the NDArray, A / + * c. + * @param a The NDArray value in A / c. + * @param c The scalar value in A / c. + */ + arrayDividedByScalar(a: T, c: Scalar): T { + util.assert( + c.size === 1, + `Error in arrayDividedByScalar: second argument must be rank 0, ` + + `but got NDArray of rank ${c.rank}.`); + return this.track(this.arrayDividedByScalarInternal(a, c)); + } + protected abstract arrayDividedByScalarInternal( + a: T, c: Scalar): T; + + /** + * Computes exponential of the input NDArray element-wise. y = e ^ x + * @param ndarray The input NDArray. + */ + exp(ndarray: T): T { + return this.track(this.expInternal(ndarray)); + } + protected abstract expInternal(ndarray: T): T; + + /** + * Computes natural logarithm of the input NDArray element-wise. y = ln(x) + * @param ndarray The input NDArray. + */ + log(ndarray: T): T { + return this.track(this.logInternal(ndarray)); + } + protected abstract logInternal(ndarray: T): T; + + /** + * Computes rectified linear element-wise, max(x, 0). + * @param ndarray The input NDArray. + */ + relu(ndarray: T): T { + return this.track(this.reluInternal(ndarray)); + } + protected abstract reluInternal(ndarray: T): T; + + /** + * Computes sigmoid element-wise, y = 1 / (1 + exp(-x)). + * @param ndarray The input NDArray. + */ + sigmoid(ndarray: T): T { + return this.track(this.sigmoidInternal(ndarray)); + } + protected abstract sigmoidInternal(ndarray: T): T; + + /** + * Computes hyperbolic tangent of the input NDArray element-wise. + * @param ndarray The input NDArray. + */ + tanh(ndarray: T): T { + return this.track(this.tanhInternal(ndarray)); + } + protected abstract tanhInternal(ndarray: T): T; + + /** + * Computes sin of the input NDArray element-wise, y = sin(x). + * @param ndarray The input NDArray. + */ + sin(ndarray: T): T { + return this.track(this.sinInternal(ndarray)); + } + protected abstract sinInternal(ndarray: T): T; + + /** + * Computes step of the input NDArray element-wise, y = 1 if x > 0 | 0 if x <= + * 0 + * @param ndarray The input NDArray. + */ + step(ndarray: T): T { + return this.track(this.stepInternal(ndarray)); + } + protected abstract stepInternal(ndarray: T): T; + + /** + * Computes a scaled array add operation, c1 * A + c2 * B. + * @param c1 The first scalar in the scaled array add computation. + * @param a The first NDArray in the scaled array add computation. + * @param c2 The second scalar in the scaled array add computation. + * @param cb The second NDArray in the scaled array add computation. + */ + scaledArrayAdd(c1: Scalar, a: T, c2: Scalar, b: T): T { + util.assert( + c1.size === 1, + `Error in scaledArrayAdd: first argument must rank 0, but got ` + + ` rank ${c1.rank}.`); + util.assert( + c2.size === 1, + `Error in scaledArrayAdd: third argument must be rank 0, but got ` + + `NDArray of rank ${c2.rank}.`); + util.assertShapesMatch(a.shape, b.shape, 'Error in scaledArrayAdd: '); + + return this.track(this.scaledArrayAddInternal(c1, a, c2, b)); + } + protected abstract scaledArrayAddInternal( + c1: Scalar, a: T, c2: Scalar, b: T): T; + + /** + * Computes a scalar times array operation broadcasted over the NDArray, c * + * A. + * @param c The scalar in the operation. + * @param A the NDArray in the operation that will be broadcasted over. + */ + scalarTimesArray(c: Scalar, a: T): T { + util.assert( + c.size === 1, + `Error in arrayDividedByScalar: first argument must be rank 0, but ` + + `got rank ${c.rank}.`); + return this.track(this.scalarTimesArrayInternal(c, a)); + } + protected abstract scalarTimesArrayInternal( + c: Scalar, a: T): T; + + /** + * Computes an element-wise broadcasted multiplication of two matrices A and + * B. Will return a new matrix that is the max of A and B, where the smaller + * matrix will broadcast over the larger matrix. + * @param c The scalar in the operation. + * @param A the NDArray in the operation that will be broadcasted over. + */ + elementWiseMulBroadcast(a: Array2D, b: Array2D): Array2D { + util.assert( + a.rank === 2, + `Error in elementWiseMulBroadcast: first argument must be ` + + `rank 2, but got rank ${a.rank}.`); + util.assert( + b.rank === 2, + `Error in elementWiseMulBroadcast: second argument must be ` + + `rank 2, but got rank ${b.rank}.`); + return this.track(this.elementWiseMulBroadcastInternal(a, b)); + } + protected abstract elementWiseMulBroadcastInternal(a: Array2D, b: Array2D): + Array2D; + + ///////////////////// + // Convolution ops // + ///////////////////// + + /** + * Computes a 2D convolution over the input x. + * @param x The input image, must be rank 3, of shape [rows, cols, depth1]. + * @param weights The weights NDArray, must be rank 4, of shape [f, f, depth1, + * depth2]. + * @param biases Optional biases NDArray, must be rank 1 of shape [depth2]. + * @param stride The stride of the convolution. + * @param zeroPad The zero padding of each side of the input NDArray. Will pad + * equally on all sides. + */ + conv2d( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + zeroPad: number): Array3D { + util.assert( + x.rank === 3, + `Error in conv2d: x must be rank 3, but got rank ${x.rank}.`); + util.assert( + weights.rank === 4, + `Error in conv2d: weights must be rank 4, but got rank ` + + `${weights.rank}.`); + if (biases != null) { + util.assert( + biases.rank === 1, + `Error in conv2d: biases must be rank 1, but got rank ` + + `${biases.rank}.`); + } + + util.assert( + x.shape[2] === weights.shape[2], + `Error in conv2d: depth of input (${x.shape[2]}) must match ` + + `input depth for weights ${weights.shape[2]}.`); + + + return this.track(this.conv2dInternal(x, weights, biases, stride, zeroPad)); + } + protected abstract conv2dInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + zeroPad: number): Array3D; + + /** + * Computes the backprop of a 2D convolution. + * @param x The input image, must be rank 3, of shape [xrows, xcols, depth1]. + * @param dy The dy image, must be rank 3, of shape [yrows, ycols, depth2]. + * @param weights The weights NDArray, must be rank 4, of shape [f, f, depth1, + * depth2]. + * @param stride The stride of the original convolution. + * @param pad The padding of the original convolution. + */ + conv2dBackProp( + x: Array3D, dy: Array3D, weights: Array4D, stride: number, + pad: number): {dx: Array3D, dw: Array4D, db: Array1D} { + util.assert( + x.rank === 3, + `Error in conv2dBackProp: x must be rank 3, but got shape ` + + `${x.shape}.`); + util.assert( + dy.rank === 3, + `Error in conv2dBackProp: dy must be rank 3, but got shape ` + + `${dy.shape}.`); + util.assert( + weights.rank === 4, + `Error in conv2dBackProp: weights must be rank 4, but got shape ` + + `${weights.shape}.`); + util.assert( + x.shape[2] === weights.shape[2], + `Error in conv2dBackProp: depth of x ${x.shape[2]}) must ` + + `match input depth for weights (${weights.shape[2]}.`); + util.assert( + dy.shape[2] === weights.shape[3], + `Error in conv2dBackProp: depth of dy (${dy.shape[2]}) must ` + + `match output depth for weights (${weights.shape[3]}).`); + + const backpropResult = + this.conv2dBackPropInternal(x, dy, weights, stride, pad); + + this.track(backpropResult.db); + this.track(backpropResult.dw); + this.track(backpropResult.dx); + + return backpropResult; + } + protected abstract conv2dBackPropInternal( + x: Array3D, dy: Array3D, weights: Array4D, stride: number, + pad: number): {dx: Array3D, dw: Array4D, db: Array1D}; + + /** + * Computes the transposed 2D convolution of an image, also known as a + * deconvolution. + * @param x The input image, must be rank 3, of shape [xrows, xcols, depth1]. + * @param weights The weights NDArray, must be rank 4, of shape [f, f, depth1, + * depth2]. + * @param biases Optional biases NDArray, must be rank 1 of shape [depth2]. + * @param stride The stride of the convolution. + * @param pad The padding of each side of the input NDArray. Will pad equally + * on all sides. + */ + conv2dTranspose( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + pad: number): Array3D { + util.assert( + x.rank === 3, + `Error in conv2dTranspose: x must be rank 3, but got rank ` + + `${x.rank}.`); + util.assert( + weights.rank === 4, + `Error in conv2dTranspose: weights must be rank 4, but got ` + + `rank ${weights.rank}`); + if (biases != null) { + util.assert( + biases.rank === 1, + `Error in conv2dTranspose: biases must be rank 1, but got ' + + 'rank ${biases.rank}.`); + } + util.assert( + x.shape[2] === weights.shape[3], + `Error in conv2dTranspose: depth of input (${x.shape[2]}) must ` + + `match input depth for weights ${weights.shape[3]}.`); + + return this.track( + this.conv2dTransposeInternal(x, weights, biases, stride, pad)); + } + protected abstract conv2dTransposeInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + pad: number): Array3D; + + /** + * Computes the 2D max pooling of an image. + * @param x The input image, must be rank 3. + * @param fSize The field size of the max pool. + * @param stride The stride of the max pool. + * @param pad The padding of each side of the input NDArray. Will pad equally + * on all sides. + */ + maxPool(x: Array3D, fSize: number, stride: number, pad: number): Array3D { + util.assert( + x.rank === 3, + 'Error in maxPool: x must be rank 3 but got rank ' + x.rank + '.'); + return this.track(this.maxPoolInternal(x, fSize, stride, pad)); + } + protected abstract maxPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D; + + /** + * Computes the backprop of a max pool. + * @param dy The dy error. + * @param x The input image, must be rank 3. + * @param fSize The field size of the max pool. + * @param stride The stride of the max pool. + * @param pad The padding of each side of the input NDArray. Will pad equally + * on all sides. + */ + maxPoolBackprop( + dy: Array3D, x: Array3D, fSize: number, stride: number, + pad: number): Array3D { + util.assert( + dy.rank === 3, + `Error in maxPoolBackprop: dy must be rank 3 but got rank ` + + `${dy.rank}.`); + util.assert( + x.rank === 3, + `Error in maxPoolBackprop: x must be rank 3 but got rank ` + + `${x.rank}.`); + + return this.track(this.maxPoolBackpropInternal(dy, x, fSize, stride, pad)); + } + protected abstract maxPoolBackpropInternal( + dy: Array3D, x: Array3D, fSize: number, stride: number, + pad: number): Array3D; + + /** + * Computes the 2D min pooling of an image. + * @param x The input image, must be rank 3. + * @param fSize The field size of the max pool. + * @param stride The stride of the max pool. + * @param pad The padding of each side of the input NDArray. Will pad equally + * on all sides. + */ + minPool(x: Array3D, fSize: number, stride: number, pad: number): Array3D { + util.assert( + x.rank === 3, + `Error in minPool: x must be rank 3 but got rank ${x.rank}.`); + return this.track(this.minPoolInternal(x, fSize, stride, pad)); + } + protected abstract minPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D; + + /** + * Computes the 2D average pooling of an image. + * @param x The input image, must be rank 3. + * @param fSize The field size of the max pool. + * @param stride The stride of the max pool. + * @param pad The padding of each side of the input NDArray. Will pad equally + * on all sides. + */ + avgPool(x: Array3D, fSize: number, stride: number, pad: number): Array3D { + util.assert( + x.rank === 3, + `Error in avgPool: x must be rank 3 but got rank ${x.rank}.`); + return this.track(this.avgPoolInternal(x, fSize, stride, pad)); + } + protected abstract avgPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D; + + /* + * Bilinear resize a 3D array per each channel to a new 2D shape. + * @param x The input Array3D. + * @param newShape2D The new shape to resize the Array3D to. Each channel is + * resized individually. + * @param alignCorners An optional bool. Defaults to False. If true, rescale + * input by (new_height - 1) / (height - 1), which exactly aligns the 4 + * corners of images and resized images. If false, rescale by new_height / + * height. Treat similarly the width dimension. + */ + resizeBilinear3D( + x: Array3D, newShape2D: [number, number], alignCorners = false): Array3D { + util.assert( + x.rank === 3, + `Error in resizeBilinear3D: x must be rank 3 but got rank ${x.rank}.`); + util.assert( + newShape2D.length === 2, + `Error in resizeBilinear3D: new shape must 2D, but got shape ` + + `${newShape2D}.`); + return this.track( + this.resizeBilinear3DInternal(x, newShape2D, alignCorners)); + } + protected abstract resizeBilinear3DInternal( + x: Array3D, newShape2D: [number, number], alignCorners: boolean): Array3D; + + /** + * Batch normalization 3D. Mean, variance, scale, and offset can be of two + * shapes: 1) The same shape as the input: an Array3D. 2) In the common case, + * the depth dimension is the last dimension of x, so the values would be an + * Array1D of shape [depth]. + * @param x The input NDArray. + * @param mean A mean NDArray. + * @param variance A variance NDArray. + * @param varianceEpsilon A small float number to avoid dividing by 0. + * @param scale A scale NDArray. + * @param offset An offset NDArray. + */ + batchNormalization3D( + x: Array3D, mean: Array3D|Array1D, variance: Array3D|Array1D, + varianceEpsilon = .001, scale?: Array3D|Array1D, + offset?: Array3D|Array1D): Array3D { + util.assert( + x.rank === 3, + `Error in batchNormalization3D: x must be rank 3 but got rank ` + + `${x.rank}.`); + util.assert( + mean.rank === 3 || mean.rank === 1, + `Error in batchNormalization3D: mean must be rank 3 or rank 1 but ` + + `got rank ${mean.rank}.`); + util.assert( + variance.rank === 3 || variance.rank === 1, + `Error in batchNormalization3D: variance must be rank 3 or rank 1 ` + + `but got rank ${variance.rank}.`); + if (scale != null) { + util.assert( + scale.rank === 3 || scale.rank === 1, + `Error in batchNormalization3D: scale must be rank 3 or rank 1 ` + + `but got rank ${scale!.rank}.`); + } + if (offset != null) { + util.assert( + offset.rank === 3 || offset.rank === 1, + `Error in batchNormalization3D: offset must be rank 3 or rank 1 ` + + `but got rank ${offset!.rank}.`); + } + + return this.track(this.batchNormalization3DInternal( + x, mean, variance, varianceEpsilon, scale, offset)); + } + protected abstract batchNormalization3DInternal( + x: Array3D, mean: Array3D|Array1D, variance: Array3D|Array1D, + varianceEpsilon: number, scale?: Array3D|Array1D, + offset?: Array3D|Array1D): Array3D; +} + +export enum MatrixOrientation { + REGULAR, + TRANSPOSED +} diff --git a/src/math/math_cpu.ts b/src/math/math_cpu.ts new file mode 100644 index 0000000000..30ba4524fe --- /dev/null +++ b/src/math/math_cpu.ts @@ -0,0 +1,898 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../math/conv_util'; +import * as util from '../util'; + +import * as concat3d_util from './concat3d_util'; +import * as copy2D_util from './copy2d_util'; +import {MatrixOrientation, NDArrayMath} from './math'; +import {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from './ndarray'; + +export class NDArrayMathCPU extends NDArrayMath { + constructor(safeMode = false) { + super(safeMode); + } + + protected cloneInternal(ndarray: T): T { + return NDArray.make( + ndarray.shape, {values: new Float32Array(ndarray.getValues())}); + } + + protected reshapeInternal( + ndarray: T1, newShape: number[]): T2 { + return this.cloneInternal(ndarray).reshape(newShape); + } + + protected slice2DInternal( + input: Array2D, beginRowCol: [number, number], + sizeRowCol: [number, number]): Array2D { + const result = Array2D.zeros(sizeRowCol); + this.copy2DInternal( + input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + } + + protected copy2DInternal( + source: Array2D, sourceBeginRowCol: [number, number], + sourceSizeRowCol: [number, number], dest: Array2D, + destBeginRowCol: [number, number], + destSizeRowCol: [number, number]): void { + copy2D_util.validateShapes(sourceSizeRowCol, destSizeRowCol); + const srcValues = source.getValues(); + const dstValues = dest.getValues(); + const n = sourceSizeRowCol[0] * sourceSizeRowCol[1]; + for (let i = 0; i < n; ++i) { + const srcRow = sourceBeginRowCol[0] + Math.floor(i / sourceSizeRowCol[1]); + const srcCol = sourceBeginRowCol[1] + (i % sourceSizeRowCol[1]); + const srcOff = srcRow * source.shape[1] + srcCol; + const dstRow = destBeginRowCol[0] + Math.floor(i / destSizeRowCol[1]); + const dstCol = destBeginRowCol[1] + (i % destSizeRowCol[1]); + const dstOff = dstRow * dest.shape[1] + dstCol; + dstValues[dstOff] = srcValues[srcOff]; + } + } + + protected concat3DInternal(x1: Array3D, x2: Array3D, axis: number): Array3D { + const outputShape = + concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + + const values = NDArray.zeros(outputShape); + + for (let i = 0; i < outputShape[0]; i++) { + for (let j = 0; j < outputShape[1]; j++) { + for (let k = 0; k < outputShape[2]; k++) { + // Shader begins. + const index: [number, number, number] = [i, j, k]; + let value: number; + if (index[axis] < x1.shape[axis]) { + value = x1.get(i, j, k); + } else { + index[axis] -= x1.shape[axis]; + const [i2, j2, k2] = index; + value = x2.get(i2, j2, k2); + } + + values.set(value, i, j, k); + } + } + } + + return values; + } + + protected scalarPlusArrayInternal(c: Scalar, a: T): T { + const resultValues = new Float32Array(a.size); + const aValues = a.getValues(); + const cVal = c.get(); + for (let i = 0; i < resultValues.length; ++i) { + resultValues[i] = cVal + aValues[i]; + } + return NDArray.make(a.shape, {values: resultValues}); + } + + protected scaledArrayAddInternal( + c1: Scalar, a: T, c2: Scalar, b: T) { + const cValues = new Float32Array(a.size); + const aValues = a.getValues(); + const bValues = b.getValues(); + const c1Val = c1.get(); + const c2Val = c2.get(); + for (let i = 0; i < cValues.length; ++i) { + cValues[i] = c1Val * aValues[i] + c2Val * bValues[i]; + } + return NDArray.make(a.shape, {values: cValues}); + } + + protected scalarTimesArrayInternal(c: Scalar, a: T): T { + const newValues = new Float32Array(a.size); + const aValues = a.getValues(); + const cVal = c.get(); + for (let i = 0; i < aValues.length; ++i) { + newValues[i] = cVal * aValues[i]; + } + return NDArray.make(a.shape, {values: newValues}); + } + + protected scalarMinusArrayInternal(c: Scalar, a: T): T { + const negA = this.negInternal(a); + const result = this.scalarPlusArrayInternal(c, negA); + + negA.dispose(); + + return result; + } + + protected arrayMinusScalarInternal(a: T, c: Scalar): T { + const negC = this.negInternal(c); + const result = this.scalarPlusArrayInternal(negC, a); + + negC.dispose(); + + return result; + } + + protected negInternal(a: T): T { + return this.scalarTimesArrayInternal(Scalar.NEG_ONE, a); + } + + protected addInternal(a: T, b: T): T { + return this.scaledArrayAddInternal(Scalar.ONE, a, Scalar.ONE, b); + } + + protected subInternal(a: T, b: T): T { + return this.scaledArrayAddInternal(Scalar.ONE, a, Scalar.NEG_ONE, b); + } + + protected matMulInternal( + a: Array2D, b: Array2D, aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Array2D { + const sharedDim = + (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + + const leftDim = + (aOrientation === MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + const rightDim = + (bOrientation === MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + + const normalGetter = (matrix: Array2D, i: number, j: number) => + matrix.get(i, j); + const transposedGetter = (matrix: Array2D, i: number, j: number) => + matrix.get(j, i); + + const aGetter = (aOrientation === MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + const bGetter = (bOrientation === MatrixOrientation.REGULAR) ? + normalGetter : + transposedGetter; + const values = new Float32Array(leftDim * rightDim); + let index = 0; + + for (let i = 0; i < leftDim; ++i) { + for (let j = 0; j < rightDim; ++j) { + let sum = 0; + for (let k = 0; k < sharedDim; ++k) { + // TODO: optimize CPU matmul. + sum += aGetter(a, i, k) * bGetter(b, k, j); + } + values[index++] = sum; + } + } + return Array2D.new([leftDim, rightDim], values); + } + + protected elementWiseMulInternal(a: T, b: T): T { + const newValues = new Float32Array(a.size); + const aValues = a.getValues(); + const bValues = b.getValues(); + for (let i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] * bValues[i]; + } + return NDArray.make(a.shape, {values: newValues}); + } + + protected elementWiseMulBroadcastInternal(a: Array2D, b: Array2D): Array2D { + const maxRow = Math.max(a.shape[0], b.shape[0]); + const maxCol = Math.max(a.shape[1], b.shape[1]); + + const values = new Float32Array(maxRow * maxCol); + let index = 0; + for (let row = 0; row < maxRow; row++) { + for (let col = 0; col < maxCol; col++) { + values[index++] = a.get(row % a.shape[0], col % a.shape[1]) * + b.get(row % b.shape[0], col % b.shape[1]); + } + } + return Array2D.new([maxRow, maxCol], values); + } + + protected divideInternal(a: T, b: T): T { + const newValues = new Float32Array(a.size); + const aValues = a.getValues(); + const bValues = b.getValues(); + for (let i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / bValues[i]; + } + return NDArray.make(a.shape, {values: newValues}); + } + + protected scalarDividedByArrayInternal(c: Scalar, a: T): + T { + const newValues = new Float32Array(a.size); + const aValues = a.getValues(); + const cValue = c.get(); + for (let i = 0; i < aValues.length; ++i) { + newValues[i] = cValue / aValues[i]; + } + return NDArray.make(a.shape, {values: newValues}); + } + + protected arrayDividedByScalarInternal(a: T, c: Scalar): + T { + const newValues = new Float32Array(a.size); + const aValues = a.getValues(); + const cValue = c.get(); + for (let i = 0; i < aValues.length; ++i) { + newValues[i] = aValues[i] / cValue; + } + return NDArray.make(a.shape, {values: newValues}); + } + + protected sumInternal(ndarray: NDArray): Scalar { + let sum = 0; + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + sum += values[i]; + } + return Scalar.new(sum); + } + + protected argMinInternal(ndarray: NDArray): Scalar { + let min = Number.MAX_VALUE; + let minIndex = -1; + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + const value = values[i]; + if (isNaN(value)) { + return Scalar.new(NaN); + } + if (value < min) { + min = value; + minIndex = i; + } + } + return Scalar.new(minIndex); + } + + protected argMaxInternal(ndarray: NDArray): Scalar { + let max = Number.NEGATIVE_INFINITY; + let maxIndex = -1; + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + const value = values[i]; + if (isNaN(value)) { + return Scalar.new(NaN); + } + if (value > max) { + max = value; + maxIndex = i; + } + } + return Scalar.new(maxIndex); + } + + protected argMaxEqualsInternal(x1: NDArray, x2: NDArray): Scalar { + const argMax1 = this.argMaxInternal(x1).get(); + const argMax2 = this.argMaxInternal(x2).get(); + if (isNaN(argMax1) || isNaN(argMax2)) { + return Scalar.new(NaN); + } + return Scalar.new(+(argMax1 === argMax2)); + } + + protected topKInternal(ndarray: NDArray, k: number): + {values: Array1D, indices: Array1D} { + const values = ndarray.getValues(); + const valuesAndIndices: Array<{value: number, index: number}> = []; + for (let i = 0; i < values.length; i++) { + valuesAndIndices.push({value: values[i], index: i}); + } + valuesAndIndices.sort((a, b) => { + return b.value - a.value; + }); + const topkValues = new Float32Array(k); + const topkIndices = new Float32Array(k); + for (let i = 0; i < k; i++) { + topkValues[i] = valuesAndIndices[i].value; + topkIndices[i] = valuesAndIndices[i].index; + } + return {values: Array1D.new(topkValues), indices: Array1D.new(topkIndices)}; + } + + protected minInternal(ndarray: NDArray): Scalar { + const values = ndarray.getValues(); + let min = values[0]; + for (let i = 1; i < values.length; ++i) { + const value = values[i]; + if (isNaN(value)) { + return Scalar.new(NaN); + } + if (value < min) { + min = value; + } + } + return Scalar.new(min); + } + + protected maxInternal(ndarray: NDArray): Scalar { + const values = ndarray.getValues(); + let max = values[0]; + for (let i = 1; i < values.length; ++i) { + const value = values[i]; + if (isNaN(value)) { + return Scalar.new(NaN); + } + if (value > max) { + max = value; + } + } + return Scalar.new(max); + } + + protected expInternal(ndarray: T): T { + const values = ndarray.getValues(); + const newValues = new Float32Array(values.length); + for (let i = 0; i < values.length; ++i) { + newValues[i] = Math.exp(values[i]); + } + return NDArray.make(ndarray.shape, {values: newValues}); + } + + protected logInternal(ndarray: T): T { + const values = ndarray.getValues(); + const newValues = new Float32Array(values.length); + for (let i = 0; i < values.length; ++i) { + const value = values[i]; + newValues[i] = Math.log(value); + } + return NDArray.make(ndarray.shape, {values: newValues}); + } + + protected logSumExpInternal(ndarray: NDArray): Scalar { + const xMax = this.max(ndarray); + const a = this.arrayMinusScalar(ndarray, xMax); + const b = this.exp(a); + const c = this.sum(b); + const d = this.log(c); + const result = this.add(xMax, d); + + xMax.dispose(); + a.dispose(); + b.dispose(); + c.dispose(); + d.dispose(); + + return result; + } + + protected reluInternal(ndarray: T): T { + const resultValues = new Float32Array(ndarray.size); + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + resultValues[i] = Math.max(0, values[i]); + } + return NDArray.make(ndarray.shape, {values: resultValues}); + } + + protected sigmoidInternal(ndarray: T): T { + const resultValues = new Float32Array(ndarray.size); + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + resultValues[i] = 1 / (1 + Math.exp(-values[i])); + } + return NDArray.make(ndarray.shape, {values: resultValues}); + } + + protected tanhInternal(ndarray: T): T { + const resultValues = new Float32Array(ndarray.size); + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + resultValues[i] = util.tanh(values[i]); + } + return NDArray.make(ndarray.shape, {values: resultValues}); + } + + protected sinInternal(ndarray: T): T { + const resultValues = new Float32Array(ndarray.size); + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + resultValues[i] = Math.sin(values[i]); + } + return NDArray.make(ndarray.shape, {values: resultValues}); + } + + protected stepInternal(ndarray: T): T { + const resultValues = new Float32Array(ndarray.size); + const values = ndarray.getValues(); + for (let i = 0; i < values.length; ++i) { + const value = values[i]; + resultValues[i] = value > 0 ? 1 : (value < 0 ? 0 : value); + } + return NDArray.make(ndarray.shape, {values: resultValues}); + } + + /** + * image is of shape [r, c, d1]. + * weights is of shape [F, F, d1, d2]. + */ + protected conv2dInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + pad: number): Array3D { + const [xRows, xCols, inputDepth] = x.shape; + const fieldSize = weights.shape[0]; + const outputDepth = weights.shape[3]; + const outputShape = conv_util.computeOutputShape3D( + [xRows, xCols, inputDepth], fieldSize, outputDepth, stride, pad); + const y = Array3D.zeros(outputShape); + for (let d2 = 0; d2 < outputDepth; ++d2) { + for (let yR = 0; yR < y.shape[0]; ++yR) { + const xRCorner = yR * stride - pad; + const xRMin = Math.max(0, xRCorner); + const xRMax = Math.min(xRows, fieldSize + xRCorner); + for (let yC = 0; yC < y.shape[1]; ++yC) { + const xCCorner = yC * stride - pad; + const xCMin = Math.max(0, xCCorner); + const xCMax = Math.min(xCols, fieldSize + xCCorner); + let dotProd = 0; + for (let xR = xRMin; xR < xRMax; ++xR) { + const wR = xR - xRCorner; + for (let xC = xCMin; xC < xCMax; ++xC) { + const wC = xC - xCCorner; + for (let d1 = 0; d1 < inputDepth; ++d1) { + const pixel = x.get(xR, xC, d1); + const weight = weights.get(wR, wC, d1, d2); + dotProd += pixel * weight; + } + } + } + const bias = (biases != null) ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + } + + protected conv2dBackPropInternal( + x: Array3D, dy: Array3D, weights: Array4D, stride: number, + pad: number): {dx: Array3D, dw: Array4D, db: Array1D} { + const fSize = weights.shape[0]; + const dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + const db = this.conv2dDerBias(dy); + const dx = this.conv2dTransposeInternal(dy, weights, null, stride, pad); + return {dx, db, dw}; + } + + /** + * image is of shape [r, c, d1]. + * weights is of shape [F, F, d1, d2]. + */ + protected conv2dTransposeInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, origStride: number, + origPad: number): Array3D { + const fSize = weights.shape[0]; + const pad = fSize - 1 - origPad; + const origInputDepth = weights.shape[2]; + const origOutputDepth = weights.shape[3]; + const [xRows, xCols, xDepth] = x.shape; + + // Dilate the input. + const xRowsDilated = (xRows - 1) * origStride + 1; + const xColsDilated = (xCols - 1) * origStride + 1; + + const outputShape = conv_util.computeOutputShape3D( + [xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, + pad); + const y = Array3D.zeros(outputShape); + for (let d2 = 0; d2 < origInputDepth; ++d2) { + for (let yR = 0; yR < y.shape[0]; ++yR) { + const xRCorner = yR - pad; + const xRMin = Math.max(0, Math.ceil(xRCorner / origStride)); + const xRMax = Math.min(xRows, (fSize + xRCorner) / origStride); + + for (let yC = 0; yC < y.shape[1]; ++yC) { + const xCCorner = yC - pad; + const xCMin = Math.max(0, Math.ceil(xCCorner / origStride)); + const xCMax = Math.min(xCols, (fSize + xCCorner) / origStride); + + let dotProd = 0; + for (let xR = xRMin; xR < xRMax; ++xR) { + const wR = xR * origStride - xRCorner; + + for (let xC = xCMin; xC < xCMax; ++xC) { + const wC = xC * origStride - xCCorner; + + for (let d1 = 0; d1 < origOutputDepth; ++d1) { + const pixel = x.get(xR, xC, d1); + const weight = + weights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + const bias = biases != null ? biases.get(d2) : 0; + y.set(dotProd + bias, yR, yC, d2); + } + } + } + return y; + } + + /** + * image is of shape [r, c, d1]. + * weights is of shape [F, F, d1, d2]. + */ + protected conv2dTransposeShaderLike( + x: Array3D, origWeights: Array4D, origStride: number, + origPad: number): Array3D { + const fSize = origWeights.shape[0]; + const pad = fSize - 1 - origPad; + const origInputDepth = origWeights.shape[2]; + const origOutputDepth = origWeights.shape[3]; + const [xRows, xCols, xDepth] = x.shape; + + // Dilate the input. + const xRowsDilated = (xRows - 1) * origStride + 1; + const xColsDilated = (xCols - 1) * origStride + 1; + + const outputShape = conv_util.computeOutputShape3D( + [xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, + pad); + const y = Array3D.zeros(outputShape); + + for (let d2 = 0; d2 < origInputDepth; ++d2) { + for (let yR = 0; yR < y.shape[0]; ++yR) { + for (let yC = 0; yC < y.shape[1]; ++yC) { + // Shader code begins. + const xRCorner = yR - pad; + const xCCorner = yC - pad; + let dotProd = 0; + for (let wR = 0; wR < fSize; ++wR) { + const xR = (xRCorner + wR) / origStride; + if (xR < 0 || xR >= xRows || Math.floor(xR) !== xR) { + continue; + } + for (let wC = 0; wC < fSize; ++wC) { + const xC = (xCCorner + wC) / origStride; + if (xC < 0 || xC >= xCols || Math.floor(xC) !== xC) { + continue; + } + for (let d1 = 0; d1 < origOutputDepth; ++d1) { + const pixel = x.get(xR, xC, d1); + const weight = + origWeights.get(fSize - 1 - wR, fSize - 1 - wC, d2, d1); + dotProd += pixel * weight; + } + } + } + y.set(dotProd, yR, yC, d2); + } + } + } + return y; + } + + conv2dDerWeights( + x: Array3D, dY: Array3D, fSize: number, stride: number, + zeroPad: number): Array4D { + const inputDepth = x.shape[2]; + const outputDepth = dY.shape[2]; + const weightsShape = + conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + const dW = Array4D.zeros(weightsShape); + + const yNumRows = dY.shape[0]; + const yNumCols = dY.shape[1]; + const xNumRows = x.shape[0]; + const xNumCols = x.shape[1]; + + for (let wR = 0; wR < fSize; ++wR) { + const yRMin = Math.max(0, Math.ceil((zeroPad - wR) / stride)); + const yRMax = Math.min(yNumRows, (xNumRows + zeroPad - wR) / stride); + + for (let wC = 0; wC < fSize; ++wC) { + const yCMin = Math.max(0, Math.ceil((zeroPad - wC) / stride)); + const yCMax = Math.min(yNumCols, (xNumCols + zeroPad - wC) / stride); + + for (let d1 = 0; d1 < inputDepth; ++d1) { + for (let d2 = 0; d2 < outputDepth; ++d2) { + // Need to convolve. + let dotProd = 0; + for (let yR = yRMin; yR < yRMax; ++yR) { + const xR = wR + yR * stride - zeroPad; + for (let yC = yCMin; yC < yCMax; ++yC) { + const xC = wC + yC * stride - zeroPad; + dotProd += x.get(xR, xC, d1) * dY.get(yR, yC, d2); + } + } + dW.set(dotProd, wR, wC, d1, d2); + } + } + } + } + return dW; + } + + conv2dDerBias(dY: Array3D): Array1D { + const outputDepth = dY.shape[2]; + const numRows = dY.shape[0]; + const numCols = dY.shape[1]; + const values = new Float32Array(outputDepth); + for (let d2 = 0; d2 < outputDepth; ++d2) { + let sum = 0; + for (let r = 0; r < numRows; ++r) { + for (let c = 0; c < numCols; ++c) { + sum += dY.get(r, c, d2); + } + } + values[d2] = sum; + } + return Array1D.new(values); + } + + protected switchDimInternal(t: T, newDim: number[]): T { + const newShape: number[] = new Array(t.rank); + for (let i = 0; i < newShape.length; i++) { + newShape[i] = t.shape[newDim[i]]; + } + const resultValues = new Float32Array(t.size); + const values = t.getValues(); + const result = NDArray.make(newShape, {values: resultValues}); + for (let i = 0; i < t.size; ++i) { + const loc = t.indexToLoc(i); + + // Permute location. + const newLoc: number[] = new Array(loc.length); + for (let i = 0; i < newLoc.length; i++) { + newLoc[i] = loc[newDim[i]]; + } + + const newIndex = result.locToIndex(newLoc); + resultValues[newIndex] = values[i]; + } + return result; + } + + private pool( + x: Array3D, fSize: number, stride: number, pad: number, + poolType: 'max'|'min'|'avg') { + const [xRows, xCols, depth] = x.shape; + const outputShape = conv_util.computeOutputShape3D( + [xRows, xCols, depth], fSize, depth, stride, pad); + const y = Array3D.zeros(outputShape); + for (let d = 0; d < depth; ++d) { + for (let yR = 0; yR < y.shape[0]; ++yR) { + const xRCorner = yR * stride - pad; + const xRMin = Math.max(0, xRCorner); + const xRMax = Math.min(xRows, fSize + xRCorner); + for (let yC = 0; yC < y.shape[1]; ++yC) { + const xCCorner = yC * stride - pad; + const xCMin = Math.max(0, xCCorner); + const xCMax = Math.min(xCols, fSize + xCCorner); + + + let minMaxValue = + (poolType === 'max' ? Number.NEGATIVE_INFINITY : + Number.POSITIVE_INFINITY); + let avgValue = 0; + + for (let xR = xRMin; xR < xRMax; ++xR) { + const wR = xR - xRCorner; + for (let xC = xCMin; xC < xCMax; ++xC) { + const wC = xC - xCCorner; + const pixel = x.get(xR, xC, d); + if (isNaN(pixel)) { + minMaxValue = NaN; + avgValue = NaN; + break; + } + if ((poolType === 'max' && pixel > minMaxValue) || + (poolType === 'min' && pixel < minMaxValue)) { + minMaxValue = pixel; + } else if (poolType === 'avg') { + avgValue += pixel / (fSize * fSize); + } + } + if (isNaN(minMaxValue)) { + break; + } + } + y.set(poolType === 'avg' ? avgValue : minMaxValue, yR, yC, d); + } + } + } + return y; + } + + protected maxPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + return this.pool(x, fSize, stride, pad, 'max'); + } + + maxPoolPositions(x: Array3D, fSize: number, stride: number, pad: number) { + const [xRows, xCols, depth] = x.shape; + const outputShape = + conv_util.computeOutputShape3D(x.shape, fSize, depth, stride, pad); + const maxPositions = Array3D.zeros(outputShape); + for (let d = 0; d < depth; ++d) { + for (let yR = 0; yR < outputShape[0]; ++yR) { + const xRCorner = yR * stride - pad; + const xRMin = Math.max(0, xRCorner); + const xRMax = Math.min(xRows, fSize + xRCorner); + for (let yC = 0; yC < outputShape[1]; ++yC) { + const xCCorner = yC * stride - pad; + const xCMin = Math.max(0, xCCorner); + const xCMax = Math.min(xCols, fSize + xCCorner); + let maxValue = Number.NEGATIVE_INFINITY; + let maxPosition = -1; + for (let xR = xRMin; xR < xRMax; ++xR) { + const wR = xR - xRCorner; + for (let xC = xCMin; xC < xCMax; ++xC) { + const wC = xC - xCCorner; + const pixel = x.get(xR, xC, d); + if (pixel > maxValue) { + maxValue = pixel; + maxPosition = wR * fSize + wC; + } + } + } + maxPositions.set(maxPosition, yR, yC, d); + } + } + } + return maxPositions; + } + + protected maxPoolBackpropInternal( + dy: Array3D, x: Array3D, fSize: number, origStride: number, + origPad: number): Array3D { + const maxPositions = this.maxPoolPositions(x, fSize, origStride, origPad); + const pad = fSize - 1 - origPad; + const [dyRows, dyCols, depth] = dy.shape; + + // Dilate the input. + const dyRowsDilated = (dyRows - 1) * origStride + 1; + const dxColsDilated = (dyCols - 1) * origStride + 1; + + const outputShape = conv_util.computeOutputShape3D( + [dyRowsDilated, dxColsDilated, depth], fSize, depth, 1, pad); + const dx = Array3D.zeros(outputShape); + + for (let d = 0; d < depth; ++d) { + for (let dxR = 0; dxR < dx.shape[0]; ++dxR) { + for (let dxC = 0; dxC < dx.shape[1]; ++dxC) { + // Shader code begins. + const dyRCorner = dxR - pad; + const dyCCorner = dxC - pad; + let dotProd = 0; + for (let wR = 0; wR < fSize; ++wR) { + const dyR = (dyRCorner + wR) / origStride; + if (dyR < 0 || dyR >= dyRows || Math.floor(dyR) !== dyR) { + continue; + } + for (let wC = 0; wC < fSize; ++wC) { + const dyC = (dyCCorner + wC) / origStride; + if (dyC < 0 || dyC >= dyCols || Math.floor(dyC) !== dyC) { + continue; + } + const maxPos = fSize * fSize - 1 - maxPositions.get(dyR, dyC, d); + const curPos = wR * fSize + wC; + + const mask = maxPos === curPos ? 1 : 0; + if (mask === 0) { + continue; + } + + const pixel = dy.get(dyR, dyC, d); + dotProd += pixel * mask; + } + } + dx.set(dotProd, dxR, dxC, d); + } + } + } + return dx; + } + + protected minPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + return this.pool(x, fSize, stride, pad, 'min'); + } + + protected avgPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + return this.pool(x, fSize, stride, pad, 'avg'); + } + + protected resizeBilinear3DInternal( + x: Array3D, newShape2D: [number, number], + alignCorners: boolean): Array3D { + const output = Array3D.zeros([newShape2D[0], newShape2D[1], x.shape[2]]); + + const effectiveInputSize = + alignCorners ? [x.shape[0] - 1, x.shape[1] - 1, x.shape[2]] : x.shape; + const effectiveOutputSize = alignCorners ? + [output.shape[0] - 1, output.shape[1] - 1, output.shape[2]] : + output.shape; + for (let r = 0; r < output.shape[0]; r++) { + for (let c = 0; c < output.shape[1]; c++) { + for (let d = 0; d < output.shape[2]; d++) { + // Begin shader. + + // Compute the fractional index of the source. + const sourceFracRow = + (effectiveInputSize[0]) * r / (effectiveOutputSize[0]); + const sourceFracCol = + (effectiveInputSize[1]) * c / (effectiveOutputSize[1]); + + const sourceRowFloor = Math.floor(sourceFracRow); + const sourceRowCeil = + Math.min(x.shape[0] - 1, Math.ceil(sourceFracRow)); + const sourceColFloor = Math.floor(sourceFracCol); + const sourceColCeil = + Math.min(x.shape[1] - 1, Math.ceil(sourceFracCol)); + + const topLeft = x.get(sourceRowFloor, sourceColFloor, d); + const bottomLeft = x.get(sourceRowCeil, sourceColFloor, d); + const topRight = x.get(sourceRowFloor, sourceColCeil, d); + const bottomRight = x.get(sourceRowCeil, sourceColCeil, d); + + const rowFrac = sourceFracRow - sourceRowFloor; + const colFrac = sourceFracCol - sourceColFloor; + + const top = topLeft + (topRight - topLeft) * colFrac; + const bottom = bottomLeft + (bottomRight - bottomLeft) * colFrac; + const newValue = top + (bottom - top) * rowFrac; + + output.set(newValue, r, c, d); + } + } + } + + return output; + } + + protected batchNormalization3DInternal( + x: Array3D, mean: Array3D|Array1D, variance: Array3D|Array1D, + varianceEpsilon = .001, scale?: Array3D|Array1D, + offset?: Array3D|Array1D): Array3D { + const xValues = x.getValues(); + const meanValues = mean.getValues(); + const varianceValues = variance.getValues(); + const scaleValues = scale ? scale.getValues() : new Float32Array([1]); + const offsetValues = offset ? offset.getValues() : new Float32Array([0]); + const outValues = new Float32Array(xValues.length); + + for (let i = 0; i < xValues.length; i++) { + outValues[i] = offsetValues[i % offsetValues.length] + + (xValues[i] - meanValues[i % meanValues.length]) * + scaleValues[i % scaleValues.length] / + Math.sqrt( + varianceValues[i % varianceValues.length] + varianceEpsilon); + } + return NDArray.make(x.shape, {values: outValues}); + } +} diff --git a/src/math/math_cpu_test.ts b/src/math/math_cpu_test.ts new file mode 100644 index 0000000000..97bd3f84b5 --- /dev/null +++ b/src/math/math_cpu_test.ts @@ -0,0 +1,1550 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../test_util'; +import * as util from '../util'; + +import {MatrixOrientation} from './math'; +import {NDArrayMathCPU} from './math_cpu'; +import {Array1D, Array2D, Array3D, NDArray, Scalar} from './ndarray'; + +describe('NDArrayMathCPU clone', () => { + it('returns a ndarray with the same shape and data', () => { + const math = new NDArrayMathCPU(); + const a = Array2D.new([3, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const aPrime = math.clone(a); + expect(aPrime.shape).toEqual(a.shape); + expect(aPrime.getValues()).toEqual(a.getValues()); + }); +}); + +describe('NDArrayMathCPU slice2D', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('slicing a 1x1 from a 1x1 returns a 1x1', () => { + const a = Array2D.new([1, 1], [0]); + const b = math.slice2D(a, [0, 0], [1, 1]); + expect(b.shape).toEqual([1, 1]); + }); + + it('returns a ndarray of slice size', () => { + const a = Array2D.zeros([100, 100]); + const b = math.slice2D(a, [0, 0], [12, 34]); + expect(b.shape).toEqual([12, 34]); + }); + + it('returns the upper-left submatrix when begin is [0, 0]', () => { + const a = NDArray.randUniform([10, 10], -1, 1); + const b = math.slice2D(a, [0, 0], [2, 2]); + const aValues = a.getValues(); + const expected = + new Float32Array([aValues[0], aValues[1], aValues[10], aValues[11]]); + test_util.expectArraysClose(b.getValues(), expected, 0); + }); + + it('returns the rectangle specified', () => { + const a = Array2D.new([4, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const b = math.slice2D(a, [1, 1], [3, 2]); + const expected = new Float32Array([5, 6, 8, 9, 11, 12]); + expect(b.getValues()).toEqual(expected); + }); + + it('throws when requesting out of bounds slice', () => { + const a = Array2D.new([4, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + expect(() => math.slice2D(a, [1, 1], [10, 10])).toThrowError(); + }); +}); + +describe('NDArrayMathCPU copy2D', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('throws an error if source and dest shapes have different areas', () => { + const source = Array2D.zeros([100, 100]); + const dest = Array2D.zeros([100, 100]); + const sourceSize: [number, number] = [20, 20]; + const destSize: [number, number] = [5, 5]; + expect( + () => math.copy2D(source, [0, 0], sourceSize, dest, [0, 0], destSize)) + .toThrowError(); + }); + + it('copies a src shape into a dst shape', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + math.copy2D(source, [1, 1], [2, 3], dest, [2, 0], [3, 2]); + expect(dest.getValues()).toEqual(new Float32Array([ + 0, 0, 0, 0, 6, 7, 8, 10, 11, 12, 0, 0 + ])); + }); + + it('throws when requesting out of bounds source copy', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + + expect(() => math.copy2D(source, [1, 1], [10, 10], dest, [2, 0], [ + 3, 2 + ])).toThrowError(); + }); + + it('throws when requesting out of bounds dest copy', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + + expect(() => math.copy2D(source, [1, 1], [2, 3], dest, [2, 0], [ + 3, 10 + ])).toThrowError(); + }); +}); + +describe('NDArrayMathCPU concat3D', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('shapes correct concat axis=0', () => { + const ndarray1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const ndarray2 = Array3D.new([1, 1, 3], [4, 5, 6]); + const values = math.concat3D(ndarray1, ndarray2, 0); + expect(values.shape).toEqual([2, 1, 3]); + expect(values.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('concat axis=0', () => { + const ndarray1 = Array3D.new([1, 2, 3], [1, 11, 111, 2, 22, 222]); + const ndarray2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const values = math.concat3D(ndarray1, ndarray2, 0); + expect(values.shape).toEqual([3, 2, 3]); + expect(values.getValues()).toEqual(new Float32Array([ + 1, 11, 111, 2, 22, 222, 5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888 + ])); + }); + + it('shapes correct concat axis=1', () => { + const ndarray1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const ndarray2 = Array3D.new([1, 1, 3], [4, 5, 6]); + const values = math.concat3D(ndarray1, ndarray2, 1); + expect(values.shape).toEqual([1, 2, 3]); + expect(values.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('concat axis=1', () => { + const ndarray1 = Array3D.new([2, 1, 3], [1, 11, 111, 3, 33, 333]); + const ndarray2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const values = math.concat3D(ndarray1, ndarray2, 1); + expect(values.shape).toEqual([2, 3, 3]); + expect(values.getValues()).toEqual(new Float32Array([ + 1, 11, 111, 5, 55, 555, 6, 66, 666, 3, 33, 333, 7, 77, 777, 8, 88, 888 + ])); + }); + + it('shapes correct concat axis=2', () => { + const ndarray1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const ndarray2 = Array3D.new([1, 1, 3], [4, 5, 6]); + const values = math.concat3D(ndarray1, ndarray2, 2); + expect(values.shape).toEqual([1, 1, 6]); + expect(values.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('concat axis=2', () => { + const ndarray1 = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const ndarray2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const values = math.concat3D(ndarray1, ndarray2, 2); + expect(values.shape).toEqual([2, 2, 5]); + expect(values.getValues()).toEqual(new Float32Array([ + 1, 11, 5, 55, 555, 2, 22, 6, 66, 666, + 3, 33, 7, 77, 777, 4, 44, 8, 88, 888 + ])); + }); + + it('concat throws when invalid non-axis shapes, axis=0', () => { + const axis = 0; + const x1 = Array3D.new([1, 1, 3], [1, 11, 111]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); + + it('concat throws when invalid non-axis shapes, axis=1', () => { + const axis = 1; + const x1 = Array3D.new([1, 1, 3], [1, 11, 111]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); + + it('concat throws when invalid non-axis shapes, axis=2', () => { + const axis = 2; + const x1 = Array3D.new([1, 2, 2], [1, 11, 2, 22]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU matMul', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('A x B', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([3, 2], [0, 1, -3, 2, 2, 1]); + const c = math.matMul(a, b); + expect(c.shape).toEqual([2, 2]); + expect(c.getValues()).toEqual(new Float32Array([0, 8, -3, 20])); + }); + + it('A x B^t', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([2, 3], [1, 0, 2, 4, 3, 0]); + const c = math.matMul( + a, b, MatrixOrientation.REGULAR, MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([7, 10, 16, 31]); + expect(c.getValues()).toEqual(expected); + }); + + it('A^t x B', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([2, 3], [1, 0, 2, 4, 3, 0]); + const c = math.matMul( + a, b, MatrixOrientation.TRANSPOSED, MatrixOrientation.REGULAR); + const expected = new Float32Array([17, 12, 2, 22, 15, 4, 27, 18, 6]); + expect(c.getValues()).toEqual(expected); + }); + + it('A^t x B^t', () => { + const a = Array2D.new([3, 2], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([2, 3], [1, 0, 2, 4, 3, 0]); + const c = math.matMul( + a, b, MatrixOrientation.TRANSPOSED, MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([11, 13, 14, 20]); + expect(c.getValues()).toEqual(expected); + }); + + it('A x B^t shapes do not match', () => { + const a = NDArray.zeros([2, 3]); + const b = NDArray.zeros([3, 2]); + const f = () => { + math.matMul( + a, b, MatrixOrientation.REGULAR, MatrixOrientation.TRANSPOSED); + }; + expect(f).toThrowError(); + }); + + it('A^t x B shapes do not match', () => { + const a = NDArray.zeros([2, 3]); + const b = NDArray.zeros([3, 2]); + const f = () => { + math.matMul( + a, b, MatrixOrientation.TRANSPOSED, MatrixOrientation.REGULAR); + }; + expect(f).toThrowError(); + }); + + it('A^t x B^t shapes do not match', () => { + const a = NDArray.zeros([3, 2]); + const b = NDArray.zeros([3, 2]); + const f = () => { + math.matMul( + a, b, MatrixOrientation.TRANSPOSED, MatrixOrientation.TRANSPOSED); + }; + expect(f).toThrowError(); + }); + + it('matmul throws when inner dimensions dont match', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([4, 2], [0, 1, -3, 2, 2, 1, 2, 2]); + expect(() => math.matMul(a, b)).toThrowError(); + }); + + it('matmul throws when passed non matrices', () => { + // tslint:disable-next-line:no-any + const a: any = + Array3D.new([2, 3, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const b = Array2D.new([4, 2], [0, 1, -3, 2, 2, 1, 2, 2]); + expect(() => math.matMul(a, b)).toThrowError(); + expect(() => math.matMul(b, a)).toThrowError(); + }); + + it('Vector times matrix', () => { + const v = Array1D.new([2, 3]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const result = math.vectorTimesMatrix(v, matrix); + + const expected = new Float32Array([11, 16]); + expect(result.getValues()).toEqual(expected); + }); + + it('Vector times matrix throws when not passed a vector', () => { + // tslint:disable-next-line:no-any + const v: any = Array2D.new([2, 2], [1, 2, 3, 4]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + expect(() => math.vectorTimesMatrix(v, matrix)).toThrowError(); + }); + + it('Vector times matrix throws when not passed a matrix', () => { + const v = Array1D.new([2, 3]); + // tslint:disable-next-line:no-any + const matrix: any = Array3D.new([2, 2, 2], [1, 2, 3, 4, 5, 6, 7, 8]); + expect(() => math.vectorTimesMatrix(v, matrix)).toThrowError(); + }); + + it('Matrix times vector', () => { + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const v = Array1D.new([2, 3]); + const result = math.matrixTimesVector(matrix, v); + + const expected = new Float32Array([8, 18]); + expect(result.getValues()).toEqual(expected); + }); + + it('matrix times vector throws when not passed a vector', () => { + // tslint:disable-next-line:no-any + const v: any = Array2D.new([2, 2], [1, 2, 3, 4]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + expect(() => math.matrixTimesVector(matrix, v)).toThrowError(); + }); + + it('matrix times vector throws when not passed a matrix', () => { + const v = Array1D.new([2, 3]); + // tslint:disable-next-line:no-any + const matrix: any = Array3D.new([2, 2, 2], [1, 2, 3, 4, 5, 6, 7, 8]); + expect(() => math.matrixTimesVector(matrix, v)).toThrowError(); + }); + + it('Dot product', () => { + const v1 = Array1D.new([2, 3]); + const v2 = Array1D.new([2, 1]); + const result = math.dotProduct(v1, v2); + + expect(result.get()).toEqual(7); + }); + + it('Dot product throws when vectors are different size', () => { + const v1 = Array1D.new([2, 3, 3]); + const v2 = Array1D.new([2, 1]); + expect(() => math.dotProduct(v1, v2)).toThrowError(); + expect(() => math.dotProduct(v2, v1)).toThrowError(); + }); + + it('Dot product throws when passed non vectors', () => { + // tslint:disable-next-line:no-any + const v1: any = Array2D.new([2, 2], [1, 2, 3, 3]); + const v2 = Array1D.new([2, 1]); + expect(() => math.dotProduct(v1, v2)).toThrowError(); + expect(() => math.dotProduct(v2, v1)).toThrowError(); + }); + + it('Outer product', () => { + const v1 = Array1D.new([2, 3]); + const v2 = Array1D.new([2, 1]); + const result = math.outerProduct(v1, v2); + + const expected = new Float32Array([4, 2, 6, 3]); + expect(result.shape).toEqual([2, 2]); + expect(result.getValues()).toEqual(expected); + }); + + it('Dot product propagates NaNs', () => { + const v1 = Array1D.new([2, NaN]); + const v2 = Array1D.new([2, 1]); + const result = math.dotProduct(v1, v2); + expect(result.get()).toEqual(NaN); + }); + + it('Matrix * vector propagates NaNs', () => { + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const v = Array1D.new([2, NaN]); + const result = math.matrixTimesVector(matrix, v); + + const expected = new Float32Array([NaN, NaN]); + expect(result.getValues()).toEqual(expected); + }); +}); + +describe('NDArrayMathCPU element-wise mul/div', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('multiplication with broadcasting.', () => { + // Same shapes, no broadcasting. + let a = Array2D.new([2, 2], [1, 2, 3, 4]); + let b = Array2D.new([2, 2], [5, 4, 3, 2]); + let expected = Array2D.new([2, 2], [5, 8, 9, 8]); + expect(expected.equals(math.elementWiseMulBroadcast(a, b))).toBe(true); + + // Broadcast a over b. + a = Array2D.new([2, 2], [1, 2, 3, 4]); + b = Array2D.new([4, 4], [2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8]); + expected = Array2D.new( + [4, 4], [2, 6, 4, 10, 9, 16, 15, 24, 4, 10, 6, 14, 15, 24, 21, 32]); + expect(expected.equals(math.elementWiseMulBroadcast(a, b))).toBe(true); + }); + + it('multiplication, no broadcasting', () => { + const a = Array2D.new([2, 2], [1, 2, 3, 4]); + const b = Array2D.new([2, 2], [5, 4, 3, 2]); + const expected = Array2D.new([2, 2], [5, 8, 9, 8]); + expect(expected.equals(math.elementWiseMul(a, b))).toBe(true); + }); + + it('multiplication propagates NaNs', () => { + const a = Array2D.new([2, 2], [1, 3, 4, 0]); + const b = Array2D.new([2, 2], [NaN, 3, NaN, 3]); + const result = math.elementWiseMul(a, b).getValues(); + expect(result).toEqual(new Float32Array([NaN, 9, NaN, 0])); + }); + + it('mul throws when passed ndarrays of different shapes', () => { + const a = Array2D.new([2, 3], [1, 2, -3, -4, 5, 6]); + const b = Array2D.new([2, 2], [5, 3, 4, -7]); + expect(() => math.elementWiseMul(a, b)).toThrowError(); + expect(() => math.elementWiseMul(b, a)).toThrowError(); + }); + + it('divide', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c = Array2D.new([2, 3], [1, 2, 3, 4, 2, 5]); + const r = math.divide(a, c); + + expect(r.get(0, 0)).toBeCloseTo(1); + expect(r.get(0, 1)).toBeCloseTo(1); + expect(r.get(0, 2)).toBeCloseTo(1); + expect(r.get(1, 0)).toBeCloseTo(1); + expect(r.get(1, 1)).toBeCloseTo(2.5); + expect(r.get(1, 2)).toBeCloseTo(6 / 5); + }); + + it('divide propagates NaNs', () => { + const a = Array2D.new([2, 1], [1, 2]); + const c = Array2D.new([2, 1], [3, NaN]); + const r = math.divide(a, c).getValues(); + expect(r[0]).toBeCloseTo(1 / 3); + expect(r[1]).toEqual(NaN); + }); + + it('divide throws when passed ndarrays of different shapes', () => { + const a = Array2D.new([2, 3], [1, 2, -3, -4, 5, 6]); + const b = Array2D.new([2, 2], [5, 3, 4, -7]); + expect(() => math.divide(a, b)).toThrowError(); + expect(() => math.divide(b, a)).toThrowError(); + }); + + it('scalar divided by array', () => { + const c = Scalar.new(2); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const r = math.scalarDividedByArray(c, a); + + expect(r.get(0, 0)).toBeCloseTo(2 / 1); + expect(r.get(0, 1)).toBeCloseTo(2 / 2); + expect(r.get(0, 2)).toBeCloseTo(2 / 3); + expect(r.get(1, 0)).toBeCloseTo(2 / 4); + expect(r.get(1, 1)).toBeCloseTo(2 / 5); + expect(r.get(1, 2)).toBeCloseTo(2 / 6); + }); + + it('scalar divided by array propagates NaNs', () => { + const c = Scalar.new(NaN); + const a = Array2D.new([1, 3], [1, 2, 3]); + const r = math.scalarDividedByArray(c, a).getValues(); + expect(r).toEqual(new Float32Array([NaN, NaN, NaN])); + }); + + it('scalar divided by array throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + expect(() => math.scalarDividedByArray(c, a)).toThrowError(); + }); + + it('array divided by scalar', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c = Scalar.new(2); + const r = math.arrayDividedByScalar(a, c); + + expect(r.get(0, 0)).toBeCloseTo(1 / 2); + expect(r.get(0, 1)).toBeCloseTo(2 / 2); + expect(r.get(0, 2)).toBeCloseTo(3 / 2); + expect(r.get(1, 0)).toBeCloseTo(4 / 2); + expect(r.get(1, 1)).toBeCloseTo(5 / 2); + expect(r.get(1, 2)).toBeCloseTo(6 / 2); + }); + + it('array divided by scalar propagates NaNs', () => { + const a = Array2D.new([1, 3], [1, 2, NaN]); + const c = Scalar.new(2); + const r = math.arrayDividedByScalar(a, c).getValues(); + expect(r[0]).toBeCloseTo(1 / 2); + expect(r[1]).toBeCloseTo(2 / 2); + expect(r[2]).toEqual(NaN); + }); + + it('array divided by scalar throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + expect(() => math.arrayDividedByScalar(a, c)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU add/sub', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('add', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, 2, -1]); + const expected = Array1D.new([6, 7, 0]); + expect(expected.getValues()).toEqual(math.add(a, b).getValues()); + }); + + it('add propagates NaNs', () => { + const a = Array1D.new([2, 5, NaN]); + const b = Array1D.new([4, 2, -1]); + const res = math.add(a, b).getValues(); + expect(res).toEqual(new Float32Array([6, 7, NaN])); + }); + + it('add throws when passed ndarrays with different shape', () => { + const a = Array1D.new([2, 5, 1, 5]); + const b = Array1D.new([4, 2, -1]); + expect(() => math.add(a, b)).toThrowError(); + expect(() => math.add(b, a)).toThrowError(); + }); + + it('sub', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, 2, -1]); + const expected = Array1D.new([-2, 3, 2]); + expect(expected.getValues()).toEqual(math.sub(a, b).getValues()); + }); + + it('sub propagates NaNs', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, NaN, -1]); + const res = math.sub(a, b).getValues(); + expect(res).toEqual(new Float32Array([-2, NaN, 2])); + }); + + it('sub throws when passed ndarrays with different shape', () => { + const a = Array1D.new([2, 5, 1, 5]); + const b = Array1D.new([4, 2, -1]); + expect(() => math.sub(a, b)).toThrowError(); + expect(() => math.sub(b, a)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU scalarTimesNDArray', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('scalar times ndarray', () => { + const a = Array2D.new([3, 2], [2, -5, 1, 1, 4, 0]); + const c = Scalar.new(2); + const expected = Array2D.new([3, 2], [4, -10, 2, 2, 8, 0]); + expect(expected.getValues()) + .toEqual(math.scalarTimesArray(c, a).getValues()); + }); + + it('scalar times ndarray throws when passed non-scalar', () => { + const a = Array2D.new([3, 2], [2, -5, 1, 1, 4, 0]); + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3, 4]); + expect(() => math.scalarTimesArray(c, a)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU scaledNDArrayAdd', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('Scaled ndarray add', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + const expected = Array2D.new([2, 3], [8, 16, 24, 32, 40, 48]); + expect(math.scaledArrayAdd(c1, a, c2, b).equals(expected)) + .toBe(true); + + // Different sizes throws an error. + const wrongSizeMat = Array2D.new([2, 2], [1, 2, 3, 4]); + expect(() => math.scaledArrayAdd(c1, wrongSizeMat, c2, b)) + .toThrowError(); + }); + + it('throws when passed non-scalars', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c1 = Array1D.randNormal([10]); + const c2 = Scalar.new(2); + + expect(() => math.scaledArrayAdd(c1 as Scalar, a, c2, b)) + .toThrowError(); + expect(() => math.scaledArrayAdd(c2, a, c1 as Scalar, b)) + .toThrowError(); + }); + + it('throws when NDArrays are different shape', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 4], [1, 2, 3, 4, 5, 6, 7, 8]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + expect(() => math.scaledArrayAdd(c1, a, c2, b)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU argmin/max, argmaxequals, min/max', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('Arg max', () => { + expect(math.argMax(Array1D.new([5, 0, 3, 7, 3])).get()).toBe(3); + expect(math.argMax(Array1D.new([-100.3, .3, 11.1, 9.9, 7.33])).get()) + .toBe(2); + expect(math.argMax(Array1D.new([-100.3, -20.0, -10.0, -5])).get()).toBe(3); + }); + + it('Arg max propagates NaNs', () => { + expect(math.argMax(Array1D.new([5, 0, 3, NaN, 3])).get()).toEqual(NaN); + }); + + it('Argmaxequals equals', () => { + const a = Array1D.new([5, 0, 3, 7]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toBe(1); + }); + + it('Argmaxequals not equals', () => { + const a = Array1D.new([5, 0, 3, 1]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toBe(0); + }); + + it('Argmaxequals propagates NaNs', () => { + const a = Array1D.new([5, 3, 1, 3]); + const b = Array1D.new([NaN, -20.0, -10.0, -5]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toEqual(NaN); + }); + + it('throws when given arrays of different shape', () => { + const a = Array1D.new([5, 0, 3, 7, 3, 10]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5, -100]); + expect(() => math.argMaxEquals(a, b)).toThrowError(); + }); + + it('topk', () => { + const topk = math.topK(Array1D.new([1, -1, 100, -5, -10.6, 3.3, 5]), 3); + test_util.expectArraysClose( + topk.values.getValues(), new Float32Array([100, 5, 3.3]), 1e-6); + test_util.expectArraysClose( + topk.indices.getValues(), new Float32Array([2, 6, 5]), 1e-6); + }); + + it('Arg min', () => { + expect(math.argMin(Array1D.new([5, 0, 3, 7, 3])).get()).toBe(1); + expect(math.argMin(Array1D.new([-100.3, .3, 11.1, 9.9, 7.33])).get()) + .toBe(0); + }); + + it('Arg min propagates NaNs', () => { + expect(math.argMin(Array1D.new([5, 0, NaN, 7, 3])).get()).toEqual(NaN); + }); + + it('min', () => { + expect(math.min(Array1D.new([3, -1, 0, 100, -7, 2])).get()).toBe(-7); + }); + + it('min propagates NaNs', () => { + expect(math.min(Array1D.new([3, NaN, 2])).get()).toEqual(NaN); + }); + + it('max', () => { + expect(math.max(Array1D.new([3, -1, 0, 100, -7, 2])).get()).toBe(100); + }); + + it('max propagates NaNs', () => { + expect(math.max(Array1D.new([3, NaN, 2])).get()).toEqual(NaN); + }); +}); + +describe('NDArrayMathCPU log/exp', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('exp', () => { + const r = math.exp(Array1D.new([1, 2, 0])); + + expect(r.get(0)).toBeCloseTo(Math.exp(1)); + expect(r.get(1)).toBeCloseTo(Math.exp(2)); + expect(r.get(2)).toBeCloseTo(1); + }); + + it('exp propagates NaNs', () => { + const a = Array1D.new([1, NaN, 0]); + const r = math.exp(a).getValues(); + expect(r).toEqual(new Float32Array([Math.exp(1), NaN, 1])); + }); + + it('log', () => { + const r = math.log(Array1D.new([1, 2])); + + expect(r.get(0)).toBeCloseTo(Math.log(1)); + expect(r.get(1)).toBeCloseTo(Math.log(2)); + }); + + it('log propagates NaNs', () => { + const r = math.log(Array1D.new([1, NaN])).getValues(); + expect(r).toEqual(new Float32Array([Math.log(1), NaN])); + }); + + it('logSumExp', () => { + const a = Array1D.new([1, 2, -3]); + const result = math.logSumExp(a); + expect(result.get()) + .toBeCloseTo(Math.log(Math.exp(1) + Math.exp(2) + Math.exp(-3))); + }); + + it('logSumExp propagates NaNs', () => { + const a = Array1D.new([1, 2, NaN]); + const result = math.logSumExp(a); + expect(result.get()).toEqual(NaN); + }); +}); + +describe('softmax', () => { + let math: NDArrayMathCPU; + + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('regular test', () => { + const y = math.softmax(Array1D.new([2, 1, 3])); + expect(y.get(0)).toBeCloseTo(0.24472847, 6); + expect(y.get(1)).toBeCloseTo(0.09003057, 6); + expect(y.get(2)).toBeCloseTo(0.66524095, 6); + expect(y.get(0) + y.get(1) + y.get(2)).toBeCloseTo(1, 6); + }); + + it('Overflow', () => { + const y = math.softmax(Array1D.new([10000, 10000])); + expect(y.get(0)).toBeCloseTo(0.5, 3); + expect(y.get(1)).toBeCloseTo(0.5, 3); + }); + + it('Underflow', () => { + const y = math.softmax(Array1D.new([-10000, -10000])); + expect(y.get(0)).toBeCloseTo(0.5, 3); + expect(y.get(1)).toBeCloseTo(0.5, 3); + }); + + it('Huge difference between probabilities', () => { + const y = math.softmax(Array1D.new([-10000, +10000])); + expect(y.get(0)).toBeCloseTo(0.0, 6); + expect(y.get(1)).toBeCloseTo(1, 6); + }); + + it('Propagates NaNs', () => { + const y = math.softmax(Array1D.new([2, 1, NaN])); + expect(y.getValues()).toEqual(new Float32Array([NaN, NaN, NaN])); + }); +}); + +describe('NDArrayMathCPU sum', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('sums values in ndarray', () => { + const a = Array2D.new([3, 2], [1, 2, 3, 0, 0, 1]); + expect(math.sum(a).get()).toBe(7); + }); + + it('propagates NaNs', () => { + const a = Array2D.new([3, 2], [1, 2, 3, NaN, 0, 1]); + expect(math.sum(a).get()).toEqual(NaN); + }); +}); + +describe('NDArrayMathCPU unary ops', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('relu', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1]); + const result = math.relu(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 3, 0])); + }); + + it('relu propagates NaNs', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1, NaN]); + const result = math.relu(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 3, 0, NaN])); + }); + + it('step', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1]); + const result = math.step(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 1, 0])); + }); + + it('step propagates NaNs', () => { + const a = Array1D.new([1, -2, 0, 3, NaN]); + const result = math.step(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 1, NaN])); + }); + + it('neg', () => { + const a = Array1D.new([1, -3, 2, 7, -4]); + expect(math.neg(a).getValues()).toEqual(new Float32Array([ + -1, 3, -2, -7, 4 + ])); + }); + + it('neg propagate NaNs', () => { + const a = Array1D.new([1, -3, 2, 7, NaN]); + expect(math.neg(a).getValues()).toEqual(new Float32Array([ + -1, 3, -2, -7, NaN + ])); + }); + + it('sigmoid', () => { + const a = Array1D.new([3, 5]); + const res = math.sigmoid(a).getValues(); + const expected = [3, 5].map(x => 1 / (1 + Math.exp(-x))); + expect(res).toEqual(new Float32Array(expected)); + }); + + it('sigmoid propagates NaNs', () => { + const a = Array1D.new([3, NaN]); + const res = math.sigmoid(a).getValues(); + expect(res).toEqual(new Float32Array([1 / (1 + Math.exp(-3)), NaN])); + }); + + it('tanh', () => { + const a = Array1D.new([4, -3, 0]); + const res = math.tanh(a).getValues(); + const expected = [util.tanh(4), util.tanh(-3), util.tanh(0)]; + expect(res).toEqual(new Float32Array(expected)); + }); + + it('tanh propagates NaNs', () => { + const a = Array1D.new([4, NaN, 0]); + const res = math.tanh(a).getValues(); + const expected = [util.tanh(4), NaN, util.tanh(0)]; + expect(res).toEqual(new Float32Array(expected)); + }); + + it('sin', () => { + const a = Array1D.new([4, -3, 0]); + const res = math.sin(a).getValues(); + const expected = [Math.sin(4), Math.sin(-3), Math.sin(0)]; + expect(res).toEqual(new Float32Array(expected)); + }); + + it('sin propagates NaNs', () => { + const a = Array1D.new([4, NaN, 0]); + const res = math.sin(a).getValues(); + const expected = [Math.sin(4), NaN, Math.sin(0)]; + expect(res).toEqual(new Float32Array(expected)); + }); +}); + +describe('NDArrayMathCPU scalar OP ndarray', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('c + A', () => { + const c = Scalar.new(5); + const a = Array1D.new([1, 2, 3]); + expect(math.scalarPlusArray(c, a).getValues()).toEqual(new Float32Array([ + 6, 7, 8 + ])); + }); + + it('c + A propagates NaNs', () => { + const c = Scalar.new(NaN); + const a = Array1D.new([1, 2, 3]); + const res = math.scalarPlusArray(c, a).getValues(); + expect(res).toEqual(new Float32Array([NaN, NaN, NaN])); + }); + + it('c + A throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.scalarPlusArray(c, a)).toThrowError(); + }); + + it('c - A', () => { + const c = Scalar.new(5); + const a = Array1D.new([1, 2, 3]); + expect(math.scalarMinusArray(c, a).getValues()).toEqual(new Float32Array([ + 4, 3, 2 + ])); + }); + + it('c - A throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.scalarMinusArray(c, a)).toThrowError(); + }); + + it('A - c', () => { + const a = Array1D.new([1, 2, 3]); + const c = Scalar.new(5); + expect(math.arrayMinusScalar(a, c).getValues()).toEqual(new Float32Array([ + -4, -3, -2 + ])); + }); + + it('A - c propagates NaNs', () => { + const a = Array1D.new([1, NaN, 3]); + const c = Scalar.new(5); + const res = math.arrayMinusScalar(a, c).getValues(); + expect(res).toEqual(new Float32Array([-4, NaN, -2])); + }); + + it('A - c throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.arrayMinusScalar(a, c)).toThrowError(); + }); +}); + +describe('NDArrayMathCPU switchDim', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('Switch dim 2D (no change)', () => { + const t = Array2D.new([2, 4], [1, 11, 2, 22, 3, 33, 4, 44]); + const t2 = math.switchDim(t, [0, 1]); + expect(t2.shape).toEqual(t.shape); + expect(t2.getValues()).toEqual(t.getValues()); + }); + + it('Switch dim 2D (transpose)', () => { + const t = Array2D.new([2, 4], [1, 11, 2, 22, 3, 33, 4, 44]); + const t2 = math.switchDim(t, [1, 0]); + expect(t2.shape).toEqual([4, 2]); + const expected = new Float32Array([1, 3, 11, 33, 2, 4, 22, 44]); + expect(t2.getValues()).toEqual(expected); + }); + + it('Switch dim 3D [r, c, d] => [d, r, c]', () => { + const t = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const t2 = math.switchDim(t, [2, 0, 1]); + expect(t2.shape).toEqual([2, 2, 2]); + const expected = new Float32Array([1, 2, 3, 4, 11, 22, 33, 44]); + expect(t2.getValues()).toEqual(expected); + }); + + it('Switch dim 3D [r, c, d] => [d, c, r]', () => { + const t = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const t2 = math.switchDim(t, [2, 1, 0]); + expect(t2.shape).toEqual([2, 2, 2]); + const expected = new Float32Array([1, 3, 2, 4, 11, 33, 22, 44]); + expect(t2.getValues()).toEqual(expected); + }); +}); + +describe('NDArrayMathCPU maxPool', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('1x1x1 in, 1x1 filter, 1 stride: [0] => [0]', () => { + const a = Array3D.new([1, 1, 1], [0]); + const result = math.maxPool(a, 1, 1, 0); + expect(result.getValues()).toBeCloseTo(0); + }); + + it('3x3x1 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, 9, 8]); + const result = math.maxPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([5, 6, 9, 9])); + }); + + it('3x3x1 in, 2x2 filter, 1 stride, propagates NaNs', () => { + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, NaN, 9]); + const result = math.maxPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([5, 6, NaN, NaN])); + }); + + it('3x3x2 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new( + [3, 3, 2], + [1, 99, 2, 88, 3, 77, 4, 66, 5, 55, 6, 44, 7, 33, 9, 22, 8, 11]); + const result = math.maxPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 2]); + expect(result.getValues()).toEqual(new Float32Array([ + 5, 99, 6, 88, 9, 66, 9, 55 + ])); + }); + + it('4x4x1 in, 2x2 filter, 2 stride', () => { + // Feed forward. + const a = Array3D.new( + [4, 4, 1], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + const result = math.maxPool(a, 2, 2, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([5, 7, 13, 15])); + }); + + it('2x2x1 in, 2x2 filter, 2 stride, pad=1', () => { + // Feed forward. + const a = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const result = math.maxPool(a, 2, 2, 1); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([1, 2, 3, 4])); + }); +}); + +describe('NDArrayMathCPU minPool', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('1x1x1 in, 1x1 filter, 1 stride: [0] => [0]', () => { + const a = Array3D.new([1, 1, 1], [0]); + const result = math.minPool(a, 1, 1, 0); + expect(result.getValues()).toBeCloseTo(0); + }); + + it('3x3x1 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, 9, 8]); + const result = math.minPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([1, 2, 4, 5])); + }); + + it('3x3x1 in, 2x2 filter, 1 stride, propagates NaNs', () => { + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, NaN, 8]); + const result = math.minPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([1, 2, NaN, NaN])); + }); + + it('3x3x2 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new( + [3, 3, 2], + [1, 99, 2, 88, 3, 77, 4, 66, 5, 55, 6, 44, 7, 33, 9, 22, 8, 11]); + const result = math.minPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 2]); + expect(result.getValues()).toEqual(new Float32Array([ + 1, 55, 2, 44, 4, 22, 5, 11 + ])); + }); + + it('4x4x1 in, 2x2 filter, 2 stride', () => { + // Feed forward. + const a = Array3D.new( + [4, 4, 1], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + const result = math.minPool(a, 2, 2, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([0, 2, 8, 10])); + }); + + it('2x2x1 in, 2x2 filter, 2 stride, pad=1', () => { + // Feed forward. + const a = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const result = math.minPool(a, 2, 2, 1); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([1, 2, 3, 4])); + }); +}); + +describe('NDArrayMathCPU avgPool', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('1x1x1 in, 1x1 filter, 1 stride: [0] => [0]', () => { + const a = Array3D.new([1, 1, 1], [0]); + const result = math.avgPool(a, 1, 1, 0); + expect(result.getValues()).toBeCloseTo(0); + }); + + it('3x3x1 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, 9, 8]); + const result = math.avgPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([3, 4, 6.25, 7])); + }); + + it('3x3x1 in, 2x2 filter, 1 stride, propagates NaNs', () => { + // Feed forward. + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, NaN, 8]); + const result = math.avgPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([3, 4, NaN, NaN])); + }); + + it('3x3x2 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new( + [3, 3, 2], + [1, 99, 2, 88, 3, 77, 4, 66, 5, 55, 6, 44, 7, 33, 9, 22, 8, 11]); + const result = math.avgPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 2]); + expect(result.getValues()).toEqual(new Float32Array([ + 3, 77, 4, 66, 6.25, 44, 7, 33 + ])); + }); + + it('4x4x1 in, 2x2 filter, 2 stride', () => { + // Feed forward. + const a = Array3D.new( + [4, 4, 1], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + const result = math.avgPool(a, 2, 2, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([ + 2.5, 4.5, 10.5, 12.5 + ])); + }); + + it('2x2x1 in, 2x2 filter, 2 stride, pad=1', () => { + // Feed forward. + const a = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const result = math.avgPool(a, 2, 2, 1); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([0.25, 0.5, 0.75, 1])); + }); +}); + +describe('NDArrayMathCPU maxPoolBackprop', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('x=3x3x1, f=2, s=1, no duplicate max value, test #1', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const expected = new Float32Array([0, 0, 0, 0, 1, 2, 0, 3, 4]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=3x3x1, f=2, s=1, no duplicate max value, test #2', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([3, 3, 1], [9, 5, 6, 6, 8, 4, 9, 5, 10]); + const expected = new Float32Array([1, 0, 0, 0, 2, 0, 3, 0, 4]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=3x3x1, f=2, s=1 duplicate max value, test 1', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([3, 3, 1], [0, 0, 0, 0, 5, 0, 0, 0, 0]); + const expected = new Float32Array([0, 0, 0, 0, 10, 0, 0, 0, 0]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=3x3x1, f=2, s=1 duplicate max value, test 2', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([3, 3, 1], [1, 3, 2, 1, 2, 1, 1, 1, 5]); + const expected = new Float32Array([0, 3, 0, 0, 3, 0, 0, 0, 4]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=4x4x1, f=2, s=2, test #1', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new( + [4, 4, 1], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + const expected = + new Float32Array([0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 3, 0, 4]); + const dx = math.maxPoolBackprop(dy, x, 2, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=4x4x1, f=2, s=2, test #2', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new( + [4, 4, 1], [1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1]); + const expected = + new Float32Array([0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0]); + const dx = math.maxPoolBackprop(dy, x, 2, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=5x5x1, f=3, s=2 no duplicate max value', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([5, 5, 1], [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 + ]); + const expected = new Float32Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 4 + ]); + const dx = math.maxPoolBackprop(dy, x, 3, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=5x5x1, f=3, s=2 duplicate max value', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const x = Array3D.new([5, 5, 1], [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12 + ]); + const expected = new Float32Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ]); + const dx = math.maxPoolBackprop(dy, x, 3, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); + + // Max pool backprop depth > 1. + it('x=3x3x2, f=2, s=1, no duplicate max value', () => { + // This test combines the first two 3x3x1 tests with no duplicates to + // make depth=2, + // dy is slightly modified to show the difference. + const dy = Array3D.new([2, 2, 2], [1, 44, 2, 33, 3, 22, 4, 11]); + const x = Array3D.new( + [3, 3, 2], + [1, 99, 2, 55, 3, 66, 4, 66, 5, 88, 6, 44, 7, 99, 8, 55, 9, 100]); + const expected = new Float32Array( + [0, 44, 0, 0, 0, 0, 0, 0, 1, 33, 2, 0, 0, 22, 3, 0, 4, 11]); + + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=3x3x2, f=2, s=1, duplicate max value', () => { + // This test combines the first two 3x3x1 tests with duplicates to + // make depth=2, + // dy is slightly modified to show the difference. + const dy = Array3D.new([2, 2, 2], [1, 44, 2, 33, 3, 22, 4, 11]); + const x = Array3D.new( + [3, 3, 2], [0, 1, 0, 3, 0, 2, 0, 1, 5, 2, 0, 1, 0, 1, 0, 1, 0, 5]); + const expected = new Float32Array( + [0, 0, 0, 77, 0, 0, 0, 0, 10, 22, 0, 0, 0, 0, 0, 0, 0, 11]); + + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=4x4x2, f=2, s=1', () => { + // This test combines the first two 4x4x1 tests with duplicates to make + // depth=2, + // dy is slightly modified to show the difference. + const dy = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const x = Array3D.new([4, 4, 2], [ + 0, 1, 1, 2, 2, 2, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, + 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 13, 2, 14, 2, 15, 1 + ]); + const expected = new Float32Array([ + 0, 0, 0, 11, 0, 22, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 33, 0, 44, 4, 0 + ]); + const dx = math.maxPoolBackprop(dy, x, 2, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); + + it('x=5x5x2, f=3, s=2 no duplicate max value', () => { + // This test combines the first two 5x5x1 tests with duplicates to make + // depth=2, + // dy is slightly modified to show the difference. + const dy = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const x = Array3D.new([5, 5, 2], [ + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 24, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 12 + ]); + const expected = new Float32Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 110, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0 + ]); + const dx = math.maxPoolBackprop(dy, x, 3, 2, 0); + expect(dx.getValues()).toEqual(expected); + }); +}); + +describe('NDArrayMathCPU resizeBilinear', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('simple alignCorners=false', () => { + const input = Array3D.new([2, 2, 1], [2, 2, 4, 4]); + const output = math.resizeBilinear3D(input, [3, 3], false); + + test_util.expectArraysClose( + output.getValues(), + new Float32Array([2, 2, 2, 10 / 3, 10 / 3, 10 / 3, 4, 4, 4]), 1e-4); + }); + + it('simple alignCorners=true', () => { + const input = Array3D.new([2, 2, 1], [2, 2, 4, 4]); + const output = math.resizeBilinear3D(input, [3, 3], true); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([2, 2, 2, 3, 3, 3, 4, 4, 4]), + 1e-4); + }); + + it('matches tensorflow w/ random numbers alignCorners=false', () => { + const input = Array3D.new([2, 3, 2], [ + 1.19074044, 0.91373104, 2.01611669, -0.52270832, 0.38725395, 1.30809779, + 0.61835143, 3.49600659, 2.09230986, 0.56473997, 0.03823943, 1.19864896 + ]); + const output = math.resizeBilinear3D(input, [4, 5], false); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([ + 1.19074047, 0.91373104, 1.68596613, 0.05186744, 1.69034398, + -0.15654698, 0.7130264, 0.94193673, 0.38725394, 1.30809784, + 0.9045459, 2.20486879, 1.59434628, 0.89455694, 1.68591988, + 0.26748738, 0.58103991, 1.00690198, 0.21274668, 1.25337338, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893 + ]), + 1e-4); + }); + + it('matches tensorflow w/ random numbers alignCorners=true', () => { + const input = Array3D.new([2, 3, 2], [ + 1.56324531, 2.13817752, 1.44398421, 1.07632684, 0.59306785, -0.36970865, + 1.62451879, 1.8367334, 1.13944798, 2.01993218, 2.01919952, 2.67524054 + ]); + const output = math.resizeBilinear3D(input, [4, 5], true); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([ + 1.5632453, 2.13817763, 1.50361478, 1.60725224, 1.44398427, + 1.07632685, 1.01852608, 0.35330909, 0.59306782, -0.36970866, + 1.58366978, 2.03769612, 1.46307099, 1.71427906, 1.3424722, + 1.39086199, 1.20545864, 1.01806819, 1.06844509, 0.6452744, + 1.60409427, 1.93721485, 1.42252707, 1.82130599, 1.24096, + 1.70539713, 1.3923912, 1.68282723, 1.54382229, 1.66025746, + 1.62451875, 1.83673346, 1.38198328, 1.92833281, 1.13944793, + 2.01993227, 1.57932377, 2.34758639, 2.01919961, 2.67524052 + ]), + 1e-4); + }); +}); + +describe('NDArrayMathCPU batchNorm', () => { + let math: NDArrayMathCPU; + beforeEach(() => { + math = new NDArrayMathCPU(); + }); + + it('simple batchnorm, no offset or scale, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, undefined, undefined); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + (x.get(0, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(0, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon), + (x.get(1, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(1, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-6); + }); + + it('simple batchnorm, no offset, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const scale = Array1D.new([4, 5]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, undefined); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + (x.get(0, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(0, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon), + (x.get(1, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(1, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-6); + }); + + it('simple batchnorm, no scale, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const offset = Array1D.new([4, 5]); + + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, undefined, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + offset.get(0) + + (x.get(0, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(0, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon), + offset.get(0) + + (x.get(1, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(1, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-6); + }); + + it('simple batchnorm, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const offset = Array1D.new([3, 4]); + const scale = Array1D.new([4, 5]); + + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + offset.get(0) + + (x.get(0, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(0, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon), + offset.get(0) + + (x.get(1, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(1, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-6); + }); + + it('batchnorm matches tensorflow, 2x3x3', () => { + const x = + Array3D.new([2, 3, 3], new Float32Array([ + 0.49955603, 0.04158615, -1.09440524, 2.03854165, + -0.61578344, 2.87533573, 1.18105987, 0.807462, 1.87888837, + 2.26563962, -0.37040935, 1.35848753, -0.75347094, + 0.15683117, 0.91925946, 0.34121279, 0.92717143, 1.89683965 + ])); + const mean = Array1D.new([0.39745062, -0.48062894, 0.4847822]); + const variance = Array1D.new([0.32375343, 0.67117643, 1.08334653]); + const offset = Array1D.new([0.69398749, -1.29056387, 0.9429723]); + const scale = Array1D.new([-0.5607271, 0.9878457, 0.25181573]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + 0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019, + 1.52106473, -0.07704776, 0.26144429, 1.28010017, -1.14422404, + -1.15776136, 1.15425493, 1.82644104, -0.52249442, 1.04803919, + 0.74932291, 0.40568101, 1.2844412 + ]), + 1e-5); + }); +}); diff --git a/src/math/math_gpu.ts b/src/math/math_gpu.ts new file mode 100644 index 0000000000..f13ab81003 --- /dev/null +++ b/src/math/math_gpu.ts @@ -0,0 +1,1303 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; + +import * as concat3d_util from './concat3d_util'; +import * as conv_util from './conv_util'; +import {MatrixOrientation, NDArrayMath} from './math'; +import * as ndarray from './ndarray'; +import {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from './ndarray'; +import * as addscaledmat_gpu from './webgl/addscaledmat_gpu'; +import * as addsubmuldiv_gpu from './webgl/addsubmuldiv_gpu'; +import {OperandType} from './webgl/addsubmuldiv_gpu'; +import * as argmaxequals_gpu from './webgl/argmaxequals_gpu'; +import * as argminmax_gpu from './webgl/argminmax_gpu'; +import * as avg_pool_gpu from './webgl/avg_pool_gpu'; +import * as batchnorm_gpu from './webgl/batchnorm_gpu'; +import * as concat3d_gpu from './webgl/concat3d_gpu'; +import * as conv_backprop_gpu from './webgl/conv_backprop_gpu'; +import * as conv_gpu from './webgl/conv_gpu'; +import * as copy_gpu from './webgl/copy_gpu'; +import * as exp_gpu from './webgl/exp_gpu'; +import {GPGPUContext} from './webgl/gpgpu_context'; +import * as gpgpu_util from './webgl/gpgpu_util'; +import * as log_gpu from './webgl/log_gpu'; +import * as logsumexp_gpu from './webgl/logsumexp_gpu'; +import * as max_pool_backprop_gpu from './webgl/max_pool_backprop_gpu'; +import * as max_pool_gpu from './webgl/max_pool_gpu'; +import * as min_pool_gpu from './webgl/min_pool_gpu'; +import * as minmax_gpu from './webgl/minmax_gpu'; +import * as mulmat_gpu from './webgl/mulmat_gpu'; +import * as neg_gpu from './webgl/neg_gpu'; +import * as pool_gpu from './webgl/pool_gpu'; +import * as reducesum_gpu from './webgl/reducesum_gpu'; +import * as relu_gpu from './webgl/relu_gpu'; +import * as reshape_gpu from './webgl/reshape_gpu'; +import * as resize_bilinear_gpu from './webgl/resize_bilinear_gpu'; +import * as shader_compiler from './webgl/shader_compiler'; +import * as sigmoid_gpu from './webgl/sigmoid_gpu'; +import * as step_gpu from './webgl/step_gpu'; +import {TextureManager} from './webgl/texture_manager'; +import * as trig_gpu from './webgl/trig_gpu'; +import * as webgl_util from './webgl/webgl_util'; + +const ARGMAX_PROG = 'argmax'; +const ARGMAX_EQUALS_PROG = 'argmaxequals'; +const ARGMIN_PROG = 'argmin'; + +const BATCHNORM_PROG = 'batchnorm'; + +const COPY_PROG = 'copy'; +const CONCAT_PROG = 'concat'; + +// Matrix algebra. +const ADD_SCALED_MAT_PROG = 'addscaledmat'; +const MATMUL_PROG = 'matmul'; + +// Element-wise ops. +const RELU_PROG = 'relu'; +const TANH_PROG = 'tanh'; +const SIN_PROG = 'sin'; +const SIGMOID_PROG = 'sigmoid'; +const MAX_PROG = 'max'; +const MIN_PROG = 'min'; +const NEG_PROG = 'neg'; +const EXP_PROG = 'exp'; +const LOG_PROG = 'log'; +const SUM_PROG = 'sum'; +const STEP_PROG = 'step'; +const LOGSUMEXP_PROG = 'logsumexp'; +const RESHAPE_PROG = 'reshape'; +const ADD_SUM_MUL_DIV_PROG = 'addsummuldiv'; + +// Convolution. +const CONV2D_PROG = 'conv'; +const CONV2D_TRANSPOSE_PROG = 'conv_transpose'; +const CONV2D_DERW_PROG = 'conv_derw'; +const CONV2D_DERB_PROG = 'conv_derb'; +const MAX_POOL_PROG = 'maxpool'; +const MAX_POOL_POSITIONS_PROG = 'maxpool_posn'; +const MAX_POOL_BACKPROP_PROG = 'maxpool_backprop'; +const MIN_POOL_PROG = 'minpool'; +const AVG_POOL_PROG = 'avgpool'; + +const RESIZE_BILINEAR_PROG = 'resizebilin'; + +function makeCopyProgramName( + sourceShapeRowCol: [number, number], sourceSizeRowCol: [number, number], + destSizeRowCol: [number, number]): string { + const shapeName = `${sourceShapeRowCol[0]}_${sourceShapeRowCol[1]}`; + const srcSizeName = `${sourceSizeRowCol[0]}_${sourceSizeRowCol[1]}`; + const dstSizeName = `${destSizeRowCol[0]}_${destSizeRowCol[1]}`; + return `${COPY_PROG}_${shapeName}_${srcSizeName}_${dstSizeName}`; +} + +export class NDArrayMathGPU extends NDArrayMath { + private gpgpu: GPGPUContext; + private textureManager: TextureManager; + private programCache: {[key: string]: WebGLProgram} = {}; + private gpgpuCreatedLocally: boolean; + + constructor(gpgpu?: GPGPUContext, safeMode = true) { + super(safeMode); + if (gpgpu == null) { + const gl = gpgpu_util.createWebGLContext(); + this.gpgpu = new GPGPUContext(gl); + this.gpgpuCreatedLocally = true; + } else { + this.gpgpu = gpgpu; + this.gpgpuCreatedLocally = false; + } + + this.textureManager = new TextureManager(this.gpgpu); + + ndarray.initializeGPU(this.gpgpu, this.textureManager); + } + + getGPGPUContext(): GPGPUContext { + return this.gpgpu; + } + + protected cloneInternal(ndarray: T): T { + const textureShapeRC = ndarray.getTextureShapeRC(); + const program = this.getAndSaveProgram( + makeCopyProgramName(textureShapeRC, textureShapeRC, textureShapeRC), + () => copy_gpu.getFragmentShaderSource( + textureShapeRC, textureShapeRC, textureShapeRC)); + + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + copy_gpu.copy( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC, [0, 0], + textureShapeRC, resultTexture, textureShapeRC, [0, 0], textureShapeRC); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected reshapeInternal( + ndarray: T1, newShape: number[]): T2 { + let newTexShape: [number, number]; + + switch (newShape.length) { + case 0: + newTexShape = [1, 1]; + break; + case 1: + newTexShape = [newShape[0], 1]; + break; + case 2: + newTexShape = [newShape[0], newShape[1]]; + break; + case 3: + newTexShape = [newShape[0], newShape[1] * newShape[2]]; + break; + default: + throw Error( + `Reshapes into ${newShape.length}-dim ndarray is not yet ` + + `supported on GPU`); + } + + const actualTexShape = ndarray.getTextureShapeRC(newTexShape); + let clonedArray: T1; + if (!util.arraysEqual(actualTexShape, newTexShape)) { + clonedArray = this.reshapeTexture(ndarray, newTexShape); + } else { + clonedArray = this.cloneInternal(ndarray); + } + return clonedArray.reshape(newShape); + } + + protected slice2DInternal( + input: Array2D, beginRowCol: [number, number], + sizeRowCol: [number, number]): Array2D { + const result = NDArray.make(sizeRowCol, { + texture: this.textureManager.acquireTexture(sizeRowCol), + textureShapeRC: sizeRowCol + }); + this.copy2DInternal( + input, beginRowCol, sizeRowCol, result, [0, 0], sizeRowCol); + return result; + } + + protected copy2DInternal( + source: Array2D, sourceBeginRowCol: [number, number], + sourceSizeRowCol: [number, number], dest: Array2D, + destBeginRowCol: [number, number], + destSizeRowCol: [number, number]): void { + const sourceShapeRC = source.getTextureShapeRC(); + const destShapeRC = dest.getTextureShapeRC(); + const program = this.getAndSaveProgram( + makeCopyProgramName(sourceShapeRC, sourceSizeRowCol, destSizeRowCol), + () => copy_gpu.getFragmentShaderSource( + sourceShapeRC, sourceSizeRowCol, destSizeRowCol)); + + copy_gpu.copy( + this.gpgpu, program, source.getTexture(), sourceShapeRC, + sourceBeginRowCol, sourceSizeRowCol, dest.getTexture(), destShapeRC, + destBeginRowCol, destSizeRowCol); + } + + protected concat3DInternal(x1: Array3D, x2: Array3D, axis: number): Array3D { + const x1TexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(x1.shape); + const x2TexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(x2.shape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualX1TexShape = x1.getTextureShapeRC(x1TexShapeRC); + let cleanupX1 = false; + if (!util.arraysEqual(actualX1TexShape, x1TexShapeRC)) { + x1 = this.reshapeTexture(x1, x1TexShapeRC); + cleanupX1 = true; + } + const actualX2TexShape = x2.getTextureShapeRC(x2TexShapeRC); + let cleanupX2 = false; + if (!util.arraysEqual(actualX2TexShape, x2TexShapeRC)) { + x2 = this.reshapeTexture(x2, x2TexShapeRC); + cleanupX2 = true; + } + + const resultShapeRCD = + concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis); + + const program = this.getAndSaveProgram( + `${CONCAT_PROG}_${x1.shape}_${x2.shape}_${axis}`, + () => concat3d_gpu.getFragmentShaderSource( + x1.shape, x2.shape, resultShapeRCD, axis)); + + const resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + concat3d_gpu.concat3D( + this.gpgpu, program, x1.getTexture(), x2.getTexture(), resultTex, + resultTexShape); + + if (cleanupX1) { + x1.dispose(); + } + + if (cleanupX2) { + x2.dispose(); + } + + return NDArray.make( + resultShapeRCD, {texture: resultTex, textureShapeRC: resultTexShape}); + } + + protected scalarPlusArrayInternal(c: Scalar, a: T): T { + return this.addSubMulDiv( + c, a, a.shape, OperandType.SCALAR, '+', OperandType.MATRIX) as T; + } + + protected arrayMinusScalarInternal(a: T, c: Scalar): T { + return this.addSubMulDiv( + a, c, a.shape, OperandType.MATRIX, '-', OperandType.SCALAR) as T; + } + + protected scalarMinusArrayInternal(c: Scalar, a: T): T { + return this.addSubMulDiv( + c, a, a.shape, OperandType.SCALAR, '-', OperandType.MATRIX) as T; + } + + protected scaledArrayAddInternal( + c1: Scalar, a: T, c2: Scalar, b: T) { + let cleanupB = false; + if (!this.doGPUShapesMatch(a, b)) { + b = this.reshapeTexture(b, a.getTextureShapeRC()); + cleanupB = true; + } + + const program = this.getAndSaveProgram( + ADD_SCALED_MAT_PROG, () => addscaledmat_gpu.getFragmentShaderSource()); + + const textureShapeRC = a.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + addscaledmat_gpu.addScaledMatrices( + this.gpgpu, program, a.getTexture(), b.getTexture(), textureShapeRC[0], + textureShapeRC[1], c1.getTexture(), c2.getTexture(), resultTexture); + + if (cleanupB) { + b.dispose(); + } + // Bring the result back to the original shape. + return NDArray.make(a.shape, {texture: resultTexture, textureShapeRC}); + } + + protected scalarTimesArrayInternal(c: Scalar, a: T): T { + return this.addSubMulDiv( + c, a, a.shape, OperandType.SCALAR, '*', OperandType.MATRIX) as T; + } + + protected negInternal(a: T): T { + const program = this.getAndSaveProgram( + NEG_PROG, () => neg_gpu.getFragmentShaderSource()); + + const textureShapeRC = a.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + neg_gpu.neg( + this.gpgpu, program, a.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make(a.shape, {texture: resultTexture, textureShapeRC}); + } + + private reshapeTexture(a: T, newTextureShape: [ + number, number + ]): T { + const aTexShape = a.getTextureShapeRC(); + + const program = this.getAndSaveProgram( + RESHAPE_PROG, () => reshape_gpu.getFragmentShaderSource()); + + const resultTexture = this.textureManager.acquireTexture(newTextureShape); + reshape_gpu.reshape( + this.gpgpu, program, a.getTexture(), aTexShape[0], aTexShape[1], + resultTexture, newTextureShape[0], newTextureShape[1]); + + return NDArray.make( + a.shape, {texture: resultTexture, textureShapeRC: newTextureShape}); + } + + protected matMulInternal( + a: Array2D, b: Array2D, aOrientation: MatrixOrientation, + bOrientation: MatrixOrientation): Array2D { + const sharedDim = + (aOrientation === MatrixOrientation.REGULAR) ? a.shape[1] : a.shape[0]; + const outerShapeA = + (aOrientation === MatrixOrientation.REGULAR) ? a.shape[0] : a.shape[1]; + const outerShapeB = + (bOrientation === MatrixOrientation.REGULAR) ? b.shape[1] : b.shape[0]; + const outShape: [number, number] = [outerShapeA, outerShapeB]; + const outTexShape = + webgl_util.getTextureShapeFromLogicalShape(this.gpgpu.gl, outShape); + const outTexture = this.textureManager.acquireTexture(outTexShape); + const out = new Array2D( + outShape, {texture: outTexture, textureShapeRC: outTexShape}); + + const key = shader_compiler.makeShaderKey([a, b], out); + const program = this.getAndSaveProgram( + `${MATMUL_PROG}_${key}_${aOrientation}_${bOrientation}`, + () => mulmat_gpu.getFragmentShader( + a, b, out, aOrientation, bOrientation)); + + mulmat_gpu.multiplyMatrix( + this.gpgpu, program, a.getTexture(), b.getTexture(), outTexture, + outTexShape); + + return out; + } + + protected elementWiseMulInternal(a: T, b: T): T { + return this.addSubMulDiv( + a, b, a.shape, OperandType.MATRIX, '*', OperandType.MATRIX) as T; + } + + protected elementWiseMulBroadcastInternal(a: Array2D, b: Array2D): Array2D { + throw new Error('Not yet implemented!'); + } + + protected batchNormalization3DInternal( + x: Array3D, mean: Array3D|Array1D, variance: Array3D|Array1D, + varianceEpsilon: number, scale?: Array3D|Array1D, + offset?: Array3D|Array1D): Array3D { + const xTexShape = x.getTextureShapeRC(); + + let cleanupMean = false; + const preferredMeanTexShape: [number, number] = + mean.rank === 1 ? [1, mean.size] : xTexShape; + let meanTexShape = mean.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(meanTexShape, preferredMeanTexShape)) { + mean = this.reshapeTexture(mean, preferredMeanTexShape); + meanTexShape = preferredMeanTexShape; + cleanupMean = true; + } + + let cleanupVariance = false; + const preferredVarianceTexShape: [number, number] = + variance.rank === 1 ? [1, variance.size] : xTexShape; + let varianceTexShape = variance.getTextureShapeRC(preferredMeanTexShape); + if (!util.arraysEqual(varianceTexShape, preferredVarianceTexShape)) { + variance = this.reshapeTexture(variance, preferredVarianceTexShape); + varianceTexShape = preferredVarianceTexShape; + cleanupVariance = true; + } + + let scaleTexShape: [number, number]|null = null; + let cleanupScale = false; + if (scale != null) { + const preferredScaleTexShape: [number, number] = + scale.rank === 1 ? [1, scale.size] : xTexShape; + + scaleTexShape = scale.getTextureShapeRC(preferredScaleTexShape); + if (!util.arraysEqual(scaleTexShape, preferredScaleTexShape)) { + scale = this.reshapeTexture(scale, preferredScaleTexShape); + scaleTexShape = preferredScaleTexShape; + cleanupScale = true; + } + } + + let offsetTexShape: [number, number]|null = null; + let cleanupOffset = false; + if (offset != null) { + const preferredOffsetTexShape: [number, number] = + offset.rank === 1 ? [1, offset.size] : xTexShape; + + offsetTexShape = offset.getTextureShapeRC(preferredOffsetTexShape); + if (!util.arraysEqual(offsetTexShape, preferredOffsetTexShape)) { + offset = this.reshapeTexture(offset, preferredOffsetTexShape); + offsetTexShape = preferredOffsetTexShape; + cleanupOffset = true; + } + } + + const resultTexShape: [number, number] = x.getTextureShapeRC(); + + const program = this.getAndSaveProgram( + `${BATCHNORM_PROG}_${xTexShape}_${meanTexShape}_${varianceTexShape}_` + + `${scaleTexShape!}_${offsetTexShape!}_${varianceEpsilon}`, + () => batchnorm_gpu.getFragmentShaderSource( + xTexShape, meanTexShape, varianceTexShape, offsetTexShape, + scaleTexShape, varianceEpsilon)); + + const resultTexture = this.textureManager.acquireTexture(resultTexShape); + + batchnorm_gpu.batchNormalization( + this.gpgpu, program, x.getTexture(), xTexShape, mean.getTexture(), + meanTexShape, variance.getTexture(), varianceTexShape, + offset != null ? offset.getTexture() : null, + offset != null ? offsetTexShape : null, + scale != null ? scale.getTexture() : null, + scale != null ? scaleTexShape : null, resultTexture, resultTexShape); + + if (cleanupMean) { + mean.dispose(); + } + if (cleanupVariance) { + variance.dispose(); + } + if (cleanupScale) { + scale!.dispose(); + } + if (cleanupOffset) { + offset!.dispose(); + } + + return NDArray.make( + x.shape, {texture: resultTexture, textureShapeRC: resultTexShape}); + } + + protected switchDimInternal(a: T, newDim: number[]): T { + throw new Error('Not yet implemented!'); + } + + protected sumInternal(ndarray: NDArray): Scalar { + const textureShapeRC = ndarray.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${SUM_PROG}_${numRows}_${numColumns}`, + () => reducesum_gpu.getFragmentShaderSource(numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + reducesum_gpu.reduceSum( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + resultTexture); + + return new Scalar({texture: resultTexture}); + } + + protected argMinInternal(ndarray: NDArray): Scalar { + const textureShapeRC = ndarray.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${ARGMIN_PROG}_${numRows}_${numColumns}`, + () => argminmax_gpu.getArgMinFragmentShaderSource(numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + argminmax_gpu.argMinMax( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + resultTexture); + + return new Scalar({texture: resultTexture}); + } + + protected argMaxInternal(ndarray: NDArray): Scalar { + const textureShapeRC = ndarray.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${ARGMAX_PROG}_${numRows}_${numColumns}`, + () => argminmax_gpu.getArgMaxFragmentShaderSource(numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + argminmax_gpu.argMinMax( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + resultTexture); + + return new Scalar({texture: resultTexture}); + } + + protected argMaxEqualsInternal(x1: NDArray, x2: NDArray): Scalar { + // If the texture shapes doesn't match, do a physical reshape so they do. + const actualX1TexShape = x1.getTextureShapeRC(); + const actualX2TexShape = x2.getTextureShapeRC(); + let cleanupX2 = false; + if (!util.arraysEqual(actualX1TexShape, actualX2TexShape)) { + x2 = this.reshapeTexture(x2, actualX1TexShape); + cleanupX2 = true; + } + + const textureShapeRC = x1.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${ARGMAX_EQUALS_PROG}_${numRows}_${numColumns}`, + () => argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource( + numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + argmaxequals_gpu.argMaxEquals( + this.gpgpu, program, x1.getTexture(), x2.getTexture(), numRows, + numColumns, resultTexture); + + if (cleanupX2) { + x2.dispose(); + } + + return new Scalar({texture: resultTexture}); + } + + protected topKInternal(ndarray: NDArray, k: number): + {values: Array1D, indices: Array1D} { + throw new Error('topK GPU not yet implemented!'); + } + + protected minInternal(ndarray: NDArray): Scalar { + const textureShapeRC = ndarray.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${MIN_PROG}_${numRows}_${numColumns}`, + () => minmax_gpu.getMinFragmentShaderSource(numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + minmax_gpu.minMax( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + resultTexture); + + return new Scalar({texture: resultTexture}); + } + + protected maxInternal(ndarray: NDArray): Scalar { + const textureShapeRC = ndarray.getTextureShapeRC(); + const [numRows, numColumns] = textureShapeRC; + + const program = this.getAndSaveProgram( + `${MAX_PROG}_${numRows}_${numColumns}`, + () => minmax_gpu.getMaxFragmentShaderSource(numRows, numColumns)); + + const resultTexture = this.textureManager.acquireTexture([1, 1]); + + minmax_gpu.minMax( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + resultTexture); + + return new Scalar({texture: resultTexture}); + } + + protected divideInternal(a: T, b: T): T { + return this.addSubMulDiv( + a, b, a.shape, OperandType.MATRIX, '/', OperandType.MATRIX) as T; + } + + protected scalarDividedByArrayInternal(c: Scalar, a: T): + T { + return this.addSubMulDiv( + c, a, a.shape, OperandType.SCALAR, '/', OperandType.MATRIX) as T; + } + + protected arrayDividedByScalarInternal(a: T, c: Scalar): + T { + return this.addSubMulDiv( + a, c, a.shape, OperandType.MATRIX, '/', OperandType.SCALAR) as T; + } + + protected addInternal(a: T, b: T): T { + return this.addSubMulDiv( + a, b, a.shape, OperandType.MATRIX, '+', OperandType.MATRIX) as T; + } + + protected subInternal(a: T, b: T): T { + return this.addSubMulDiv( + a, b, a.shape, OperandType.MATRIX, '-', OperandType.MATRIX) as T; + } + + protected logSumExpInternal(ndarray: NDArray): Scalar { + const [numRows, numColumns] = ndarray.getTextureShapeRC(); + + const program = this.getAndSaveProgram( + `${LOGSUMEXP_PROG}_${numRows}_${numColumns}`, + () => logsumexp_gpu.getFragmentShaderSource(numRows, numColumns)); + + const result = + new Scalar({texture: this.textureManager.acquireTexture([1, 1])}); + + reducesum_gpu.reduceSum( + this.gpgpu, program, ndarray.getTexture(), numRows, numColumns, + result.getTexture()); + + return result; + } + + protected expInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + EXP_PROG, () => exp_gpu.getFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + exp_gpu.exp( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected logInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + LOG_PROG, () => log_gpu.getFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + log_gpu.log( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected reluInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + RELU_PROG, () => relu_gpu.getFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + relu_gpu.relu( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected sigmoidInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + SIGMOID_PROG, () => sigmoid_gpu.getSigmoidFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + sigmoid_gpu.sigmoid( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected tanhInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + TANH_PROG, () => trig_gpu.getTanhFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + trig_gpu.tanh( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected sinInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + SIN_PROG, () => trig_gpu.getSinFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + trig_gpu.sin( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected stepInternal(ndarray: T): T { + const program = this.getAndSaveProgram( + STEP_PROG, () => step_gpu.getFragmentShaderSource()); + + const textureShapeRC = ndarray.getTextureShapeRC(); + const resultTexture = this.textureManager.acquireTexture(textureShapeRC); + + step_gpu.step( + this.gpgpu, program, ndarray.getTexture(), textureShapeRC[0], + textureShapeRC[1], resultTexture); + + return NDArray.make( + ndarray.shape, {texture: resultTexture, textureShapeRC}); + } + + protected conv2dInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, stride: number, + zeroPad: number): Array3D { + const fieldSize = weights.shape[0]; + const inputDepth = weights.shape[2]; + const outputDepth = weights.shape[3]; + const progKey = [ + CONV2D_PROG, x.shape, outputDepth, fieldSize, stride, biases != null + ].join('_'); + const program = this.getAndSaveProgram(progKey, () => { + return conv_gpu.getFragmentShaderSource( + x.shape, outputDepth, fieldSize, stride, zeroPad, biases != null); + }); + + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + const wTexShape = + conv_util.computeWeightsTexShape(inputDepth, outputDepth, fieldSize); + const biasTexShape = conv_util.computeBiasesTexShape(outputDepth); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualXTexShape = x.getTextureShapeRC(xTexShape); + let cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + let cleanupW = false; + const actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + + let cleanupB = false; + if (biases != null) { + const actualBTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + + const resultShape = conv_util.computeOutputShape3D( + x.shape, fieldSize, outputDepth, stride, zeroPad); + const resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + conv_gpu.convolve( + this.gpgpu, program, x.getTexture(), weights.getTexture(), + biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB && biases != null) { + biases.dispose(); + } + + return NDArray.make( + resultShape, {texture: resultTex, textureShapeRC: resultTexShape}); + } + + protected conv2dBackPropInternal( + x: Array3D, dy: Array3D, weights: Array4D, stride: number, + pad: number): {dx: Array3D, dw: Array4D, db: Array1D} { + const fSize = weights.shape[0]; + const inputDepth = weights.shape[2]; + const outputDepth = weights.shape[3]; + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + const wTexShape = + conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + const yTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + let cleanupX = false; + const actualXTexShape = x.getTextureShapeRC(xTexShape); + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + let cleanupW = false; + const actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + + let cleanupY = false; + const actualYTexShape = dy.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dy = this.reshapeTexture(dy, yTexShape); + cleanupY = true; + } + + const dw = this.conv2dDerWeights(x, dy, fSize, stride, pad); + const db = this.conv2dDerBias(dy); + const dx = this.conv2dTransposeInternal( + dy, weights, null /** biases */, stride, pad); + + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupY) { + dy.dispose(); + } + return {dx, db, dw}; + } + + protected conv2dTransposeInternal( + x: Array3D, weights: Array4D, biases: Array1D|null, origStride: number, + origPad: number): Array3D { + const origInputDepth = weights.shape[2]; + const origOutputDepth = weights.shape[3]; + const fieldSize = weights.shape[0]; + + const progKey = [ + CONV2D_TRANSPOSE_PROG, x.shape, fieldSize, origInputDepth, origStride, + origPad, biases != null + ].join('_'); + const program = this.getAndSaveProgram(progKey, () => { + return conv_backprop_gpu.getFragmentShaderConvTransposeSource( + x.shape, fieldSize, origInputDepth, origStride, origPad, + biases != null); + }); + + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + const wTexShape = conv_util.computeWeightsTexShape( + origInputDepth, origOutputDepth, fieldSize); + const biasTexShape = conv_util.computeBiasesTexShape(origInputDepth); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualXTexShape = x.getTextureShapeRC(xTexShape); + let cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + let cleanupW = false; + const actualWTexShape = weights.getTextureShapeRC(wTexShape); + if (!util.arraysEqual(actualWTexShape, wTexShape)) { + weights = this.reshapeTexture(weights, wTexShape); + cleanupW = true; + } + + let cleanupB = false; + if (biases != null) { + const actualBiasTexShape = biases.getTextureShapeRC(biasTexShape); + if (!util.arraysEqual(actualBiasTexShape, biasTexShape)) { + biases = this.reshapeTexture(biases, biasTexShape); + cleanupB = true; + } + } + + // Figure out the output shape by dilating the input. + const dilatedRC = + conv_util.computeDilatedRC([x.shape[0], x.shape[1]], origStride); + const pad = fieldSize - 1 - origPad; + const resultShape = conv_util.computeOutputShape3D( + [dilatedRC[0], dilatedRC[1], origOutputDepth], fieldSize, + origInputDepth, 1, pad); + const resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + conv_backprop_gpu.convTranspose( + this.gpgpu, program, x.getTexture(), weights.getTexture(), + biases != null ? biases.getTexture() : null, resultTex, resultTexShape); + + if (cleanupX) { + x.dispose(); + } + if (cleanupW) { + weights.dispose(); + } + if (cleanupB) { + biases!.dispose(); + } + + return NDArray.make( + resultShape, {texture: resultTex, textureShapeRC: resultTexShape}); + } + + conv2dDerWeights( + x: Array3D, dY: Array3D, fSize: number, stride: number, + zeroPad: number): Array4D { + const inputDepth = x.shape[2]; + const outputDepth = dY.shape[2]; + const progKey = [ + CONV2D_DERW_PROG, x.shape, fSize, outputDepth, stride, zeroPad + ].join('_'); + const program = this.getAndSaveProgram(progKey, () => { + return conv_backprop_gpu.getFragmentShaderDerWeightsSource( + x.shape, fSize, outputDepth, stride, zeroPad); + }); + + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + const yShape = conv_util.computeOutputShape3D( + x.shape, fSize, outputDepth, stride, zeroPad); + const yTexShape = conv_util.computeTexShapeFrom3D(yShape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualXTexShape = x.getTextureShapeRC(xTexShape); + let cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + let cleanupY = false; + const actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + + const resultTexShape = + conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + conv_backprop_gpu.derWeights( + this.gpgpu, program, x.getTexture(), dY.getTexture(), resultTex, + resultTexShape); + + if (cleanupX) { + x.dispose(); + } + if (cleanupY) { + dY.dispose(); + } + + const weightsShape = + conv_util.computeWeightsShape4D(inputDepth, outputDepth, fSize); + return NDArray.make( + weightsShape, {texture: resultTex, textureShapeRC: resultTexShape}); + } + + conv2dDerBias(dY: Array3D): Array1D { + const outputDepth = dY.shape[2]; + const progKey = [CONV2D_DERB_PROG, dY.shape].join('_'); + const program = this.getAndSaveProgram(progKey, () => { + return conv_backprop_gpu.getFragmentShaderDerBiasSource(dY.shape); + }); + const yTexShape = conv_util.computeTexShapeFrom3D(dY.shape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + let cleanupY = false; + const actualYTexShape = dY.getTextureShapeRC(yTexShape); + if (!util.arraysEqual(actualYTexShape, yTexShape)) { + dY = this.reshapeTexture(dY, yTexShape); + cleanupY = true; + } + + const resultTexShape = conv_util.computeBiasesTexShape(outputDepth); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + conv_backprop_gpu.derBias( + this.gpgpu, program, dY.getTexture(), resultTex, resultTexShape); + + if (cleanupY) { + dY.dispose(); + } + + return NDArray.make( + [outputDepth], {texture: resultTex, textureShapeRC: resultTexShape}); + } + + private pool( + program: WebGLProgram, x: Array3D, fSize: number, stride: number, + pad: number): Array3D { + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualXTexShape = x.getTextureShapeRC(xTexShape); + let cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + const resultShape = + conv_util.computeOutputShape3D(x.shape, fSize, x.shape[2], stride, pad); + const resultTexShape = conv_util.computeTexShapeFrom3D(resultShape); + const poolResultTex = this.textureManager.acquireTexture(resultTexShape); + + pool_gpu.poolCommon( + this.gpgpu, program, x.getTexture(), poolResultTex, resultTexShape); + + if (cleanupX) { + x.dispose(); + } + + return NDArray.make( + resultShape, {texture: poolResultTex, textureShapeRC: resultTexShape}); + } + + protected maxPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + const maxPoolProgKey = + [MAX_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + const maxPoolProgram = this.getAndSaveProgram(maxPoolProgKey, () => { + return max_pool_gpu.getFragmentShaderMaxPoolSource( + x.shape, fSize, stride, pad); + }); + + return this.pool(maxPoolProgram, x, fSize, stride, pad); + } + + protected minPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + const minPoolProgKey = + [MIN_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + const minPoolProgram = this.getAndSaveProgram(minPoolProgKey, () => { + return min_pool_gpu.getFragmentShaderMinPoolSource( + x.shape, fSize, stride, pad); + }); + + return this.pool(minPoolProgram, x, fSize, stride, pad); + } + + protected avgPoolInternal( + x: Array3D, fSize: number, stride: number, pad: number): Array3D { + const avgPoolProgKey = + [AVG_POOL_PROG, x.shape, fSize, stride, pad].join('_'); + const avgPoolProgram = this.getAndSaveProgram(avgPoolProgKey, () => { + return avg_pool_gpu.getFragmentShaderAvgPoolSource( + x.shape, fSize, stride, pad); + }); + + return this.pool(avgPoolProgram, x, fSize, stride, pad); + } + + protected maxPoolBackpropInternal( + dy: Array3D, x: Array3D, fSize: number, origStride: number, + origPad: number): Array3D { + const maxPoolPositionsProgKey = [ + MAX_POOL_POSITIONS_PROG, x.shape, fSize, origStride, origPad + ].join('_'); + const maxPoolPositionsProgram = + this.getAndSaveProgram(maxPoolPositionsProgKey, () => { + return max_pool_gpu.getFragmentShaderMaxPoolPositionsSource( + x.shape, fSize, origStride, origPad); + }); + + const maxPoolResultShape = conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], origStride, origPad); + const maxPoolResultTexShape = + conv_util.computeTexShapeFrom3D(maxPoolResultShape); + const maxPoolPositionsResultTex = + this.textureManager.acquireTexture(maxPoolResultTexShape); + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const xTexShape = conv_util.computeTexShapeFrom3D(x.shape); + const actualXTexShape = x.getTextureShapeRC(xTexShape); + let cleanupX = false; + if (!util.arraysEqual(actualXTexShape, xTexShape)) { + x = this.reshapeTexture(x, xTexShape); + cleanupX = true; + } + + max_pool_gpu.maxPoolCommon( + this.gpgpu, maxPoolPositionsProgram, x.getTexture(), + maxPoolPositionsResultTex, maxPoolResultTexShape); + + const maxPoolBackpropProgKey = [ + MAX_POOL_BACKPROP_PROG, dy.shape, fSize, origStride, origPad + ].join('_'); + const program = this.getAndSaveProgram(maxPoolBackpropProgKey, () => { + return max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop( + dy.shape, fSize, origStride, origPad); + }); + + const dyTexShape = conv_util.computeTexShapeFrom3D(dy.shape); + + // If the texture shapes doesn't match the shapes that shaders expect, + // do physical texture reshapes on the GPU. + const actualDyTexShape = dy.getTextureShapeRC(dyTexShape); + let cleanupDy = false; + if (!util.arraysEqual(actualDyTexShape, dyTexShape)) { + dy = this.reshapeTexture(dy, dyTexShape); + cleanupDy = true; + } + + const dilatedDyRC = + conv_util.computeDilatedRC([dy.shape[0], dy.shape[1]], origStride); + const pad = fSize - 1 - origPad; + const resultShapeRCD = conv_util.computeOutputShape3D( + [dilatedDyRC[0], dilatedDyRC[1], dy.shape[2]], fSize, dy.shape[2], 1, + pad); + const resultTexShape = conv_util.computeTexShapeFrom3D(resultShapeRCD); + const resultTex = this.textureManager.acquireTexture(resultTexShape); + + max_pool_backprop_gpu.maxPoolBackprop( + this.gpgpu, program, dy.getTexture(), maxPoolPositionsResultTex, + resultTex, resultTexShape); + + if (cleanupDy) { + dy.dispose(); + } + + if (cleanupX) { + x.dispose(); + } + + this.textureManager.releaseTexture( + maxPoolPositionsResultTex, maxPoolResultTexShape); + + return NDArray.make( + resultShapeRCD, {texture: resultTex, textureShapeRC: resultTexShape}); + } + + protected resizeBilinear3DInternal( + x: Array3D, newShape2D: [number, number], + alignCorners: boolean): Array3D { + const programKey = + [RESIZE_BILINEAR_PROG, x.shape, newShape2D, alignCorners].join('_'); + + const newShapeRCD: [number, number, number] = + [newShape2D[0], newShape2D[1], x.shape[2]]; + const resultTexShape = conv_util.computeTexShapeFrom3D(newShapeRCD); + + const program = this.getAndSaveProgram( + programKey, + () => resize_bilinear_gpu.getFragmentShaderSource( + x.shape, newShape2D, alignCorners)); + + const resultTexture = this.textureManager.acquireTexture(resultTexShape); + + resize_bilinear_gpu.resizeBilinear( + this.gpgpu, program, x.getTexture(), resultTexture, resultTexShape); + + return NDArray.make( + newShapeRCD, {texture: resultTexture, textureShapeRC: resultTexShape}); + } + + private getAndSaveProgram(programKey: string, getShaderSource: () => string): + WebGLProgram { + if (!(programKey in this.programCache)) { + this.programCache[programKey] = + this.gpgpu.createProgram(getShaderSource()); + } + return this.programCache[programKey]; + } + + private addSubMulDiv( + a: NDArray, b: NDArray, resultShape: number[], + operandA: addsubmuldiv_gpu.OperandType, + opType: addsubmuldiv_gpu.Operation, + operandB: addsubmuldiv_gpu.OperandType): NDArray { + let cleanupB = false; + + const aOrientation = MatrixOrientation.REGULAR; + let bOrientation = MatrixOrientation.REGULAR; + + let logicalBTexShape: [number, number]; + + if (operandA === OperandType.MATRIX && operandB === OperandType.MATRIX) { + util.assertShapesMatch(a.shape, b.shape); + + if (a.inGPU()) { + // Prefer B to have the shape of A. + b.getTextureShapeRC(a.getTextureShapeRC()); + } else if (b.inGPU()) { + // Prefer A to have the shape of B. + a.getTextureShapeRC(b.getTextureShapeRC()); + } + + const aTexShape = a.getTextureShapeRC(); + const bTexShape = b.getTextureShapeRC(); + logicalBTexShape = bTexShape; + + if (a.rank === 1) { + // When dealing with vectors, we can sample in transposed way without + // the need to do physical reshape. + if (!util.arraysEqual(bTexShape, aTexShape)) { + bOrientation = MatrixOrientation.TRANSPOSED; + logicalBTexShape = [bTexShape[1], bTexShape[0]]; + } + } + + if (!util.arraysEqual(aTexShape, logicalBTexShape)) { + b = this.reshapeTexture(b, aTexShape); + bOrientation = MatrixOrientation.REGULAR; + logicalBTexShape = b.getTextureShapeRC(); + cleanupB = true; + } + } else { + logicalBTexShape = b.getTextureShapeRC(); + } + + const aTexShape = a.getTextureShapeRC(); + const bTexShape = b.getTextureShapeRC(); + + const programKey = [ + ADD_SUM_MUL_DIV_PROG, operandA, aOrientation, opType, operandB, + bOrientation + ].join('_'); + const program = this.getAndSaveProgram( + programKey, + () => addsubmuldiv_gpu.getFragmentShaderSource( + operandA, aOrientation, opType, operandB, bOrientation)); + + const resultTextureShape: [number, number] = [ + Math.max(aTexShape[0], logicalBTexShape[0]), + Math.max(aTexShape[1], logicalBTexShape[1]) + ]; + + const resultTexture = + this.textureManager.acquireTexture(resultTextureShape); + + addsubmuldiv_gpu.addSubMulDiv( + this.gpgpu, program, a.getTexture(), aTexShape, b.getTexture(), + bTexShape, resultTexture, resultTextureShape); + + if (cleanupB) { + b.dispose(); + } + + return NDArray.make( + resultShape, + {texture: resultTexture, textureShapeRC: resultTextureShape}); + } + + private doGPUShapesMatch(a: NDArray, b: NDArray): boolean { + util.assertShapesMatch(a.shape, b.shape); + if (a.inGPU()) { + // Prefer B to have the shape of A. + b.getTextureShapeRC(a.getTextureShapeRC()); + } else if (b.inGPU()) { + // Prefer A to have the shape of B. + a.getTextureShapeRC(b.getTextureShapeRC()); + } + return util.arraysEqual(a.getTextureShapeRC(), b.getTextureShapeRC()); + } + + getTextureManager(): TextureManager { + return this.textureManager; + } + + dispose() { + for (const programKey in this.programCache) { + if (this.programCache.hasOwnProperty(programKey)) { + this.gpgpu.deleteProgram(this.programCache[programKey]); + } + } + this.textureManager.dispose(); + + if (this.gpgpuCreatedLocally) { + this.gpgpu.dispose(); + } + } +} diff --git a/src/math/math_gpu_test.ts b/src/math/math_gpu_test.ts new file mode 100644 index 0000000000..da830c2d3a --- /dev/null +++ b/src/math/math_gpu_test.ts @@ -0,0 +1,2134 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../test_util'; +import * as util from '../util'; + +import {NDArrayMathGPU} from './math_gpu'; +import {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from './ndarray'; +import * as webgl_util from './webgl/webgl_util'; + + +describe('NDArrayMathGPU scope', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + }); + + it('basic scope usage with a return', () => { + const a = Array1D.new([1, 2, 3]); + let b = Array1D.new([0, 0, 0]); + + const numUsedTexturesBefore = math.getTextureManager().getNumUsedTextures(); + + math.scope(() => { + const result = math.scope(() => { + b = math.add(a, b); + b = math.add(a, b); + b = math.add(a, b); + return math.add(a, b); + }); + + // a, b, and result are new textures. All intermediates should be + // disposed. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 3); + expect(result.getValues()).toEqual(new Float32Array([4, 8, 12])); + }); + + // a, b are new textures, result should be disposed. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 2); + a.dispose(); + b.dispose(); + }); + + it('basic scope usage without return', () => { + const a = Array1D.new([1, 2, 3]); + let b = Array1D.new([0, 0, 0]); + + const numUsedTexturesBefore = math.getTextureManager().getNumUsedTextures(); + + math.scope(() => { + b = math.add(a, b); + b = math.add(a, b); + b = math.add(a, b); + math.add(a, b); + }); + + const numUsedTexturesAfter = math.getTextureManager().getNumUsedTextures(); + + // original a and b, all intermediates should be disposed. + expect(numUsedTexturesAfter).toEqual(numUsedTexturesBefore + 2); + }); + + it('nested scope usage', () => { + const a = Array1D.new([1, 2, 3]); + let b = Array1D.new([0, 0, 0]); + + const numUsedTexturesBefore = math.getTextureManager().getNumUsedTextures(); + + math.scope(() => { + const result = math.scope(() => { + b = math.add(a, b); + b = math.scope(() => { + b = math.scope(() => { + return math.add(a, b); + }); + // a, original b, and two intermediate textures should be the only + // textures. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 4); + + math.scope(() => { + math.add(a, b); + }); + // All the intermediates should be cleaned up. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 4); + + return math.add(a, b); + }); + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 4); + + return math.add(a, b); + }); + + // a, b, and result are new textures. All intermediates should be + // disposed. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 3); + expect(result.getValues()).toEqual(new Float32Array([4, 8, 12])); + }); + // a, b, are new textures, result should be disposed. + expect(math.getTextureManager().getNumUsedTextures()) + .toEqual(numUsedTexturesBefore + 2); + }); +}); + +describe('NDArrayMathGPU clone', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('returns a ndarray with the same shape and value', () => { + const a = Array2D.new([3, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const aPrime = math.clone(a); + expect(aPrime.shape).toEqual(a.shape); + expect(aPrime.getValues()).toEqual(a.getValues()); + a.dispose(); + }); + + it('returns a ndarray with a different texture handle', () => { + const a = Array2D.new([3, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const aPrime = math.clone(a); + expect(a.inGPU()).toEqual(true); + expect(aPrime.inGPU()).toEqual(true); + expect(aPrime.getTexture()).not.toBe(a.getTexture()); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU slice2D', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('slicing a 1x1 from a 1x1 returns a 1x1', () => { + const a = Array2D.new([1, 1], [0]); + const b = math.slice2D(a, [0, 0], [1, 1]); + expect(b.shape).toEqual([1, 1]); + a.dispose(); + }); + + it('returns a ndarray of slice size', () => { + const a = Array2D.zeros([100, 100]); + const b = math.slice2D(a, [0, 0], [12, 34]); + expect(b.shape).toEqual([12, 34]); + a.dispose(); + }); + + it('returns the upper-left submatrix when begin is [0, 0]', () => { + const a = NDArray.randUniform([10, 10], -1, 1); + const b = math.slice2D(a, [0, 0], [2, 2]); + const aValues = a.getValues(); + const expected = + new Float32Array([aValues[0], aValues[1], aValues[10], aValues[11]]); + test_util.expectArraysClose(b.getValues(), expected, 0); + a.dispose(); + }); + + it('returns the rectangle specified', () => { + const a = Array2D.new([4, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const b = math.slice2D(a, [1, 1], [3, 2]); + const expected = new Float32Array([5, 6, 8, 9, 11, 12]); + expect(b.getValues()).toEqual(expected); + a.dispose(); + }); + + it('throws when requesting out of bounds slice', () => { + const a = Array2D.new([4, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + expect(() => math.slice2D(a, [1, 1], [10, 10])).toThrowError(); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU copy2D', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('throws an error if source and dest shapes have different areas', () => { + const source = Array2D.zeros([100, 100]); + const dest = Array2D.zeros([100, 100]); + const sourceSize: [number, number] = [20, 20]; + const destSize: [number, number] = [5, 5]; + expect( + () => math.copy2D(source, [0, 0], sourceSize, dest, [0, 0], destSize)) + .toThrowError(); + source.dispose(); + dest.dispose(); + }); + + it('copies a src shape into a dst shape', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + math.copy2D(source, [1, 1], [2, 3], dest, [2, 0], [3, 2]); + expect(dest.getValues()).toEqual(new Float32Array([ + 0, 0, 0, 0, 6, 7, 8, 10, 11, 12, 0, 0 + ])); + source.dispose(); + dest.dispose(); + }); + + it('throws when requesting out of bounds source copy', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + + expect(() => math.copy2D(source, [1, 1], [10, 10], dest, [2, 0], [ + 3, 2 + ])).toThrowError(); + source.dispose(); + dest.dispose(); + }); + + it('throws when requesting out of bounds dest copy', () => { + const source = Array2D.new([3, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const dest = Array2D.zeros([6, 2]); + + expect(() => math.copy2D(source, [1, 1], [2, 3], dest, [2, 0], [ + 3, 10 + ])).toThrowError(); + source.dispose(); + dest.dispose(); + }); +}); + +describe('NDArrayMathGPU scaledNDArrayAdd', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('with 2D ndarrays', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + const expected = new Float32Array([8, 16, 24, 32, 40, 48]); + const result = math.scaledArrayAdd(c1, a, c2, b); + + expect(result.shape).toEqual([2, 3]); + expect(result.getValues()).toEqual(expected); + + a.dispose(); + b.dispose(); + c1.dispose(); + c2.dispose(); + }); + + it('with 3D ndarrays', () => { + const a = Array3D.new([2, 2, 2], [2, 4, 6, 8, 10, 12, 3, 5]); + const b = Array3D.new([2, 2, 2], [1, 2, 3, 4, 5, 6, 7, 8]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + const expected = new Float32Array([8, 16, 24, 32, 40, 48, 23, 31]); + const result = math.scaledArrayAdd(c1, a, c2, b); + + expect(result.shape).toEqual([2, 2, 2]); + expect(result.getValues()).toEqual(expected); + + a.dispose(); + b.dispose(); + c1.dispose(); + c2.dispose(); + }); + + it('throws when passed non-scalars', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c1 = Array1D.randNormal([10]); + const c2 = Scalar.new(2); + + expect(() => math.scaledArrayAdd(c1 as Scalar, a, c2, b)) + .toThrowError(); + expect(() => math.scaledArrayAdd(c2, a, c1 as Scalar, b)) + .toThrowError(); + + a.dispose(); + b.dispose(); + c1.dispose(); + c2.dispose(); + }); + + it('throws when NDArrays are different shape', () => { + const a = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + const b = Array2D.new([2, 4], [1, 2, 3, 4, 5, 6, 7, 8]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + expect(() => math.scaledArrayAdd(c1, a, c2, b)).toThrowError(); + + a.dispose(); + b.dispose(); + c1.dispose(); + c2.dispose(); + }); +}); + +describe('NDArrayMathGPU concat3D', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('concat axis=0', () => { + const axis = 0; + const x1 = Array3D.new([1, 2, 3], [1, 11, 111, 2, 22, 222]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const y = math.concat3D(x1, x2, axis); + + expect(y.shape).toEqual([3, 2, 3]); + expect(y.getValues()).toEqual(new Float32Array([ + 1, 11, 111, 2, 22, 222, 5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888 + ])); + }); + + it('concat axis=1', () => { + const axis = 1; + const x1 = Array3D.new([2, 1, 3], [1, 11, 111, 3, 33, 333]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const result = math.concat3D(x1, x2, axis); + + expect(result.shape).toEqual([2, 3, 3]); + expect(result.getValues()).toEqual(new Float32Array([ + 1, 11, 111, 5, 55, 555, 6, 66, 666, 3, 33, 333, 7, 77, 777, 8, 88, 888 + ])); + }); + + it('concat axis=2', () => { + const axis = 2; + const x1 = Array3D.new([2, 2, 2], [1, 11, 2, 22, 3, 33, 4, 44]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + const result = math.concat3D(x1, x2, axis); + + expect(result.shape).toEqual([2, 2, 5]); + expect(result.getValues()).toEqual(new Float32Array([ + 1, 11, 5, 55, 555, 2, 22, 6, 66, 666, + 3, 33, 7, 77, 777, 4, 44, 8, 88, 888 + ])); + }); + + it('concat throws when invalid non-axis shapes, axis=0', () => { + const axis = 0; + const x1 = Array3D.new([1, 1, 3], [1, 11, 111]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); + + it('concat throws when invalid non-axis shapes, axis=1', () => { + const axis = 1; + const x1 = Array3D.new([1, 1, 3], [1, 11, 111]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); + + it('concat throws when invalid non-axis shapes, axis=2', () => { + const axis = 2; + const x1 = Array3D.new([1, 2, 2], [1, 11, 2, 22]); + const x2 = Array3D.new( + [2, 2, 3], [5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + expect(() => math.concat3D(x1, x2, axis)).toThrowError(); + }); +}); + +describe('NDArrayMathGPU matMul', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('multiplies matrices', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([3, 2], [0, 1, -3, 2, 2, 1]); + const c = math.matMul(a, b); + expect(c.shape).toEqual([2, 2]); + expect(c.getValues()).toEqual(new Float32Array([0, 8, -3, 20])); + + a.dispose(); + b.dispose(); + c.dispose(); + }); + + it('with implicit texture reshaping on GPU', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + // Make the texture shape different than the logical shape on purpose. + expect(a.getTextureShapeRC([6, 1])).toEqual([6, 1]); + + const b = Array2D.new([3, 2], [1, 3, 0, 1, 2, 0]); + expect(b.getTextureShapeRC()).toEqual([3, 2]); + + // Matmul should do implicit texture reshape on ndarray A in order to + // do the right logical multiplication. + const result = math.matMul(a, b); + expect(result.shape).toEqual([2, 2]); + expect(result.getTextureShapeRC()).toEqual([2, 2]); + expect(result.getValues()).toEqual(new Float32Array([7, 5, 16, 17])); + a.dispose(); + b.dispose(); + }); + + it('matmul throws when inner dimensions dont match', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = Array2D.new([4, 2], [0, 1, -3, 2, 2, 1, 2, 2]); + expect(() => math.matMul(a, b)).toThrowError(); + + a.dispose(); + b.dispose(); + }); + + it('matmul throws when passed non matrices', () => { + // tslint:disable-next-line:no-any + const a: any = + Array3D.new([2, 3, 2], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const b = Array2D.new([4, 2], [0, 1, -3, 2, 2, 1, 2, 2]); + expect(() => math.matMul(a, b)).toThrowError(); + expect(() => math.matMul(b, a)).toThrowError(); + + a.dispose(); + b.dispose(); + }); + + it('Vector times matrix', () => { + const v = Array1D.new([2, 3]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const result = math.vectorTimesMatrix(v, matrix); + + const expected = new Float32Array([11, 16]); + expect(result.getValues()).toEqual(expected); + v.dispose(); + matrix.dispose(); + result.dispose(); + }); + + it('Vector times matrix with implicit reshape', () => { + const v = Array1D.new([2, 3]); + // Make the texture shape be column on purpose. + expect(v.getTextureShapeRC([2, 1])).toEqual([2, 1]); + + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const result = math.vectorTimesMatrix(v, matrix); + + const expected = new Float32Array([11, 16]); + expect(result.getValues()).toEqual(expected); + v.dispose(); + matrix.dispose(); + }); + + it('Vector times matrix throws when not passed a vector', () => { + // tslint:disable-next-line:no-any + const v: any = Array2D.new([2, 2], [1, 2, 3, 4]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + expect(() => math.vectorTimesMatrix(v, matrix)).toThrowError(); + }); + + it('Vector times matrix throws when not passed a matrix', () => { + const v = Array1D.new([2, 3]); + // tslint:disable-next-line:no-any + const matrix: any = Array3D.new([2, 2, 2], [1, 2, 3, 4, 5, 6, 7, 8]); + expect(() => math.vectorTimesMatrix(v, matrix)).toThrowError(); + }); + + it('Matrix times vector', () => { + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const v = Array1D.new([2, 3]); + const result = math.matrixTimesVector(matrix, v); + + const expected = new Float32Array([8, 18]); + expect(result.getValues()).toEqual(expected); + matrix.dispose(); + v.dispose(); + }); + + it('Matrix times vector, larger than max texture size', () => { + const maxTexSize = + webgl_util.queryMaxTextureSize(math.getGPGPUContext().gl); + const matrix = Array2D.zeros([1, maxTexSize + 4]); + matrix.fill(1); + const v = Array1D.zeros([maxTexSize + 4]); + v.fill(1); + const result = math.matrixTimesVector(matrix, v); + const expected = new Float32Array([maxTexSize + 4]); + expect(result.getValues()).toEqual(expected); + + matrix.dispose(); + v.dispose(); + }); + + it('Matrix * vector propagates NaNs', () => { + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const v = Array1D.new([2, NaN]); + const result = math.matrixTimesVector(matrix, v); + + const expected = new Float32Array([NaN, NaN]); + expect(result.getValues()).toEqual(expected); + + matrix.dispose(); + v.dispose(); + }); + + it('Matrix times vector with implicit reshape', () => { + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + const v = Array1D.new([2, 3]); + // Make the texture shape be row on purpose. + expect(v.getTextureShapeRC([1, 2])).toEqual([1, 2]); + const result = math.matrixTimesVector(matrix, v); + + const expected = new Float32Array([8, 18]); + expect(result.getValues()).toEqual(expected); + matrix.dispose(); + v.dispose(); + }); + + it('matrix times vector throws when not passed a vector', () => { + // tslint:disable-next-line:no-any + const v: any = Array2D.new([2, 2], [1, 2, 3, 4]); + const matrix = Array2D.new([2, 2], [1, 2, 3, 4]); + expect(() => math.matrixTimesVector(matrix, v)).toThrowError(); + }); + + it('matrix times vector throws when not passed a matrix', () => { + const v = Array1D.new([2, 3]); + // tslint:disable-next-line:no-any + const matrix: any = Array3D.new([2, 2, 2], [1, 2, 3, 4, 5, 6, 7, 8]); + expect(() => math.matrixTimesVector(matrix, v)).toThrowError(); + }); + + it('Dot product', () => { + const v1 = Array1D.new([2, 3]); + const v2 = Array1D.new([2, 1]); + const result = math.dotProduct(v1, v2); + + expect(result.get()).toEqual(7); + v1.dispose(); + v2.dispose(); + result.dispose(); + }); + + it('Dot product propagates NaNs', () => { + const v1 = Array1D.new([2, NaN]); + const v2 = Array1D.new([2, 1]); + const result = math.dotProduct(v1, v2); + expect(result.get()).toEqual(NaN); + + v1.dispose(); + v2.dispose(); + }); + + it('Dot product with implicit reshaping', () => { + const v1 = Array1D.new([2, 3]); + // Make the texture shape be column on purpose. + expect(v1.getTextureShapeRC([2, 1])).toEqual([2, 1]); + + const v2 = Array1D.new([2, 1]); + // Make the texture shape be row on purpose. + expect(v2.getTextureShapeRC([1, 2])).toEqual([1, 2]); + + const result = math.dotProduct(v1, v2); + expect(result.get()).toEqual(7); + v1.dispose(); + v2.dispose(); + }); + + it('Dot product throws when vectors are different size', () => { + const v1 = Array1D.new([2, 3, 3]); + const v2 = Array1D.new([2, 1]); + expect(() => math.dotProduct(v1, v2)).toThrowError(); + expect(() => math.dotProduct(v2, v1)).toThrowError(); + + v1.dispose(); + v2.dispose(); + }); + + it('Dot product throws when passed non vectors', () => { + // tslint:disable-next-line:no-any + const v1: any = Array2D.new([2, 2], [1, 2, 3, 3]); + const v2 = Array1D.new([2, 1]); + expect(() => math.dotProduct(v1, v2)).toThrowError(); + expect(() => math.dotProduct(v2, v1)).toThrowError(); + + v1.dispose(); + v2.dispose(); + }); + + it('Outer product', () => { + const v1 = Array1D.new([2, 3]); + const v2 = Array1D.new([2, 1]); + const result = math.outerProduct(v1, v2); + + const expected = new Float32Array([4, 2, 6, 3]); + expect(result.shape).toEqual([2, 2]); + expect(result.getValues()).toEqual(expected); + v1.dispose(); + v2.dispose(); + }); + + it('Outer product with implicit reshape', () => { + const v1 = Array1D.new([2, 3]); + // Make the texture shape be row on purpose. + expect(v1.getTextureShapeRC([1, 2])).toEqual([1, 2]); + + const v2 = Array1D.new([2, 1]); + // Make the texture shape be column on purpose. + expect(v2.getTextureShapeRC([2, 1])).toEqual([2, 1]); + + const result = math.outerProduct(v1, v2); + const expected = new Float32Array([4, 2, 6, 3]); + expect(result.shape).toEqual([2, 2]); + expect(result.getValues()).toEqual(expected); + v1.dispose(); + v2.dispose(); + }); +}); + +describe('NDArrayMathGPU element-wise mul/div', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('multiplies same-shaped ndarrays', () => { + const a = Array2D.new([2, 2], [1, 2, -3, -4]); + const b = Array2D.new([2, 2], [5, 3, 4, -7]); + const expected = new Float32Array([5, 6, -12, 28]); + const result = math.elementWiseMul(a, b); + + expect(result.shape).toEqual([2, 2]); + expect(result.inGPU()).toBe(true); + expect(result.getValues()).toEqual(expected); + expect(result.inGPU()).toBe(false); + + a.dispose(); + b.dispose(); + }); + + it('propagates NaNs', () => { + const a = Array2D.new([2, 2], [1, 3, 4, 0]); + const b = Array2D.new([2, 2], [NaN, 3, NaN, 3]); + const result = math.elementWiseMul(a, b).getValues(); + expect(result).toEqual(new Float32Array([NaN, 9, NaN, 0])); + + a.dispose(); + b.dispose(); + }); + + it('mul throws when passed ndarrays of different shapes', () => { + const a = Array2D.new([2, 3], [1, 2, -3, -4, 5, 6]); + const b = Array2D.new([2, 2], [5, 3, 4, -7]); + expect(() => math.elementWiseMul(a, b)).toThrowError(); + expect(() => math.elementWiseMul(b, a)).toThrowError(); + + a.dispose(); + b.dispose(); + }); + + it('divide', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c = Array2D.new([2, 3], [1, 2, 3, 4, 2, 5]); + const r = math.divide(a, c); + + expect(r.get(0, 0)).toBeCloseTo(1); + expect(r.get(0, 1)).toBeCloseTo(1); + expect(r.get(0, 2)).toBeCloseTo(1); + expect(r.get(1, 0)).toBeCloseTo(1); + expect(r.get(1, 1)).toBeCloseTo(2.5); + expect(r.get(1, 2)).toBeCloseTo(6 / 5); + + a.dispose(); + c.dispose(); + }); + + it('divide propagates NaNs', () => { + const a = Array2D.new([2, 1], [1, 2]); + const c = Array2D.new([2, 1], [3, NaN]); + const r = math.divide(a, c).getValues(); + expect(r[0]).toBeCloseTo(1 / 3); + expect(r[1]).toEqual(NaN); + + a.dispose(); + c.dispose(); + }); + + it('div throws when passed ndarrays of different shapes', () => { + const a = Array2D.new([2, 3], [1, 2, -3, -4, 5, 6]); + const b = Array2D.new([2, 2], [5, 3, 4, -7]); + expect(() => math.divide(a, b)).toThrowError(); + expect(() => math.divide(b, a)).toThrowError(); + + a.dispose(); + b.dispose(); + }); + + it('scalar divided by array', () => { + const c = Scalar.new(2); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + const r = math.scalarDividedByArray(c, a); + + expect(r.get(0, 0)).toBeCloseTo(2 / 1); + expect(r.get(0, 1)).toBeCloseTo(2 / 2); + expect(r.get(0, 2)).toBeCloseTo(2 / 3); + expect(r.get(1, 0)).toBeCloseTo(2 / 4); + expect(r.get(1, 1)).toBeCloseTo(2 / 5); + expect(r.get(1, 2)).toBeCloseTo(2 / 6); + + a.dispose(); + c.dispose(); + }); + + it('scalar divided by array propagates NaNs', () => { + const c = Scalar.new(NaN); + const a = Array2D.new([1, 3], [1, 2, 3]); + const r = math.scalarDividedByArray(c, a).getValues(); + expect(r).toEqual(new Float32Array([NaN, NaN, NaN])); + + a.dispose(); + c.dispose(); + }); + + it('scalar divided by array throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + expect(() => math.scalarDividedByArray(c, a)).toThrowError(); + + a.dispose(); + c.dispose(); + }); + + it('array divided by scalar', () => { + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const c = Scalar.new(2); + + const r = math.arrayDividedByScalar(a, c); + + expect(r.get(0, 0)).toBeCloseTo(1 / 2); + expect(r.get(0, 1)).toBeCloseTo(2 / 2); + expect(r.get(0, 2)).toBeCloseTo(3 / 2); + expect(r.get(1, 0)).toBeCloseTo(4 / 2); + expect(r.get(1, 1)).toBeCloseTo(5 / 2); + expect(r.get(1, 2)).toBeCloseTo(6 / 2); + + a.dispose(); + c.dispose(); + }); + + it('array divided by scalar propagates NaNs', () => { + const a = Array2D.new([1, 3], [1, 2, NaN]); + const c = Scalar.new(2); + const r = math.arrayDividedByScalar(a, c).getValues(); + expect(r[0]).toBeCloseTo(1 / 2); + expect(r[1]).toBeCloseTo(2 / 2); + expect(r[2]).toEqual(NaN); + + a.dispose(); + c.dispose(); + }); + + it('array divided by scalar throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + expect(() => math.arrayDividedByScalar(a, c)).toThrowError(); + + a.dispose(); + c.dispose(); + }); +}); + +describe('NDArrayMathGPU unary ops', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('relu', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1]); + const result = math.relu(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 3, 0])); + + a.dispose(); + }); + + it('relu propagates NaNs', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1, NaN]); + const result = math.relu(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 3, 0, NaN])); + a.dispose(); + }); + + it('step with 1d ndarray', () => { + const a = Array1D.new([1, -2, 0, 3, -0.1]); + const result = math.step(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 1, 0])); + + a.dispose(); + }); + + it('step with 2d ndarray', () => { + const a = Array2D.new([2, 2], [1, -5, -3, 4]); + const result = math.step(a); + + expect(result.shape).toEqual([2, 2]); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 1])); + + a.dispose(); + }); + + it('step propagates NaNs', () => { + const a = Array1D.new([1, -2, 0, 3, NaN]); + const result = math.step(a); + expect(result.getValues()).toEqual(new Float32Array([1, 0, 0, 1, NaN])); + a.dispose(); + }); + + it('neg', () => { + const a = Array1D.new([1, -3, 2, 7, -4]); + const result = math.neg(a); + expect(result.getValues()).toEqual(new Float32Array([-1, 3, -2, -7, 4])); + + a.dispose(); + }); + + it('neg propagate NaNs', () => { + const a = Array1D.new([1, -3, 2, 7, NaN]); + const expected = [-1, 3, -2, -7, NaN]; + expect(math.neg(a).getValues()).toEqual(new Float32Array(expected)); + a.dispose(); + }); + + it('tanh', () => { + const values = [1, -3, 2, 7, -4]; + const a = Array1D.new(values); + const result = math.tanh(a); + const expected = new Float32Array(a.size); + for (let i = 0; i < a.size; i++) { + expected[i] = util.tanh(values[i]); + } + test_util.expectArraysClose(result.getValues(), expected, 1e-6); + + a.dispose(); + }); + + it('tanh propagates NaNs', () => { + const a = Array1D.new([4, NaN, 0]); + const res = math.tanh(a).getValues(); + const expected = [util.tanh(4), NaN, util.tanh(0)]; + test_util.expectArraysClose(res, new Float32Array(expected), 1e-5); + a.dispose(); + }); + + it('sigmoid', () => { + const values = [1, -3, 2, 7, -4]; + const a = Array1D.new(values); + const result = math.sigmoid(a); + const expected = new Float32Array(a.size); + for (let i = 0; i < a.size; i++) { + expected[i] = 1 / (1 + Math.exp(-values[i])); + } + test_util.expectArraysClose(result.getValues(), expected, 1e-6); + + a.dispose(); + }); + + it('sigmoid propagates NaNs', () => { + const a = Array1D.new([3, NaN]); + const res = math.sigmoid(a).getValues(); + test_util.expectArraysClose( + res, new Float32Array([1 / (1 + Math.exp(-3)), NaN]), 1e-5); + a.dispose(); + }); + + it('sin', () => { + const values = [1, -3, 2, 7, -4]; + const a = Array1D.new(values); + const result = math.sin(a); + const expected = new Float32Array(a.size); + for (let i = 0; i < a.size; i++) { + expected[i] = Math.sin(values[i]); + } + test_util.expectArraysClose(result.getValues(), expected, 1e-3); + + a.dispose(); + }); + + it('sin propagates NaNs', () => { + const a = Array1D.new([4, NaN, 0]); + const res = math.sin(a).getValues(); + const expected = [Math.sin(4), NaN, Math.sin(0)]; + test_util.expectArraysClose(res, new Float32Array(expected), 1e-4); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU min/max', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('max with one element dominating', () => { + const a = Array1D.new([3, -1, 0, 100, -7, 2]); + const r = math.max(a); + + expect(r.get()).toBe(100); + + a.dispose(); + }); + + it('max with all elements being the same', () => { + const a = Array1D.new([3, 3, 3]); + const r = math.max(a); + expect(r.get()).toBe(3); + + a.dispose(); + }); + + it('max propagates NaNs', () => { + expect(math.max(Array1D.new([3, NaN, 2])).get()).toEqual(NaN); + }); + + it('min Array1D', () => { + const a = Array1D.new([3, -1, 0, 100, -7, 2]); + expect(math.min(a).get()).toBe(-7); + a.dispose(); + }); + + it('min propagates NaNs', () => { + const a = Array1D.new([3, NaN, 2]); + expect(math.min(a).get()).toEqual(NaN); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU scalar and element-wise', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('c + A', () => { + const c = Scalar.new(5); + const a = Array1D.new([1, 2, 3]); + const result = math.scalarPlusArray(c, a); + expect(result.getValues()).toEqual(new Float32Array([6, 7, 8])); + + a.dispose(); + c.dispose(); + }); + + it('c + A propagates NaNs', () => { + const c = Scalar.new(NaN); + const a = Array1D.new([1, 2, 3]); + const res = math.scalarPlusArray(c, a).getValues(); + expect(res).toEqual(new Float32Array([NaN, NaN, NaN])); + a.dispose(); + c.dispose(); + }); + + it('c + A throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.scalarPlusArray(c, a)).toThrowError(); + + a.dispose(); + c.dispose(); + }); + + it('c - A', () => { + const c = Scalar.new(5); + const a = Array1D.new([7, 2, 3]); + const result = math.scalarMinusArray(c, a); + expect(result.getValues()).toEqual(new Float32Array([-2, 3, 2])); + + a.dispose(); + c.dispose(); + }); + + it('c - A throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.scalarMinusArray(c, a)).toThrowError(); + + a.dispose(); + c.dispose(); + }); + + it('A - c', () => { + const a = Array1D.new([1, 2, -3]); + const c = Scalar.new(5); + const result = math.arrayMinusScalar(a, c); + expect(result.getValues()).toEqual(new Float32Array([-4, -3, -8])); + + a.dispose(); + c.dispose(); + result.dispose(); + }); + + it('A - c propagates NaNs', () => { + const a = Array1D.new([1, NaN, 3]); + const c = Scalar.new(5); + const res = math.arrayMinusScalar(a, c).getValues(); + expect(res).toEqual(new Float32Array([-4, NaN, -2])); + a.dispose(); + c.dispose(); + }); + + it('A - c throws when passed non scalar', () => { + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3]); + const a = Array1D.new([1, 2, 3]); + expect(() => math.arrayMinusScalar(a, c)).toThrowError(); + + a.dispose(); + c.dispose(); + }); + + it('A - B', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, 2, -1]); + const expected = new Float32Array([-2, 3, 2]); + const result = math.sub(a, b); + + expect(result.getValues()).toEqual(expected); + + a.dispose(); + b.dispose(); + }); + + it('A - B propagates NaNs', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, NaN, -1]); + const res = math.sub(a, b).getValues(); + expect(res).toEqual(new Float32Array([-2, NaN, 2])); + + a.dispose(); + b.dispose(); + }); + + it('A - B throws when passed ndarrays with different shape', () => { + const a = Array1D.new([2, 5, 1, 5]); + const b = Array1D.new([4, 2, -1]); + expect(() => math.sub(a, b)).toThrowError(); + expect(() => math.sub(b, a)).toThrowError(); + + a.dispose(); + b.dispose(); + }); + + it('A + B', () => { + const a = Array1D.new([2, 5, 1]); + const b = Array1D.new([4, 2, -1]); + const expected = new Float32Array([6, 7, 0]); + const result = math.add(a, b); + + expect(result.getValues()).toEqual(expected); + + a.dispose(); + b.dispose(); + }); + + it('A + B propagates NaNs', () => { + const a = Array1D.new([2, 5, NaN]); + const b = Array1D.new([4, 2, -1]); + const res = math.add(a, b).getValues(); + expect(res).toEqual(new Float32Array([6, 7, NaN])); + + a.dispose(); + b.dispose(); + }); + + it('A + B throws when passed ndarrays with different shape', () => { + const a = Array1D.new([2, 5, 1, 5]); + const b = Array1D.new([4, 2, -1]); + expect(() => math.add(a, b)).toThrowError(); + expect(() => math.add(b, a)).toThrowError(); + + a.dispose(); + b.dispose(); + }); +}); + +describe('NDArrayMathGPU scalarTimesNDArray', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('scalar times ndarray', () => { + const a = Array2D.new([3, 2], [2, -5, 1, 1, 4, 0]); + const c = Scalar.new(2); + const expected = new Float32Array([4, -10, 2, 2, 8, 0]); + const result = math.scalarTimesArray(c, a); + + expect(result.shape).toEqual([3, 2]); + expect(result.getValues()).toEqual(expected); + + a.dispose(); + c.dispose(); + }); + + it('scalar times ndarray throws when passed non-scalar', () => { + const a = Array2D.new([3, 2], [2, -5, 1, 1, 4, 0]); + // tslint:disable-next-line:no-any + const c: any = Array1D.new([1, 2, 3, 4]); + expect(() => math.scalarTimesArray(c, a)).toThrowError(); + + a.dispose(); + c.dispose(); + }); +}); + +describe('NDArrayMathGPU log/exp', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('exp', () => { + const a = Array1D.new([1, 2, 0]); + const r = math.exp(a); + + expect(r.get(0)).toBeCloseTo(Math.exp(1)); + expect(r.get(1)).toBeCloseTo(Math.exp(2)); + expect(r.get(2)).toBeCloseTo(1); + + a.dispose(); + }); + + it('exp propagates NaNs', () => { + const a = Array1D.new([1, NaN, 0]); + const r = math.exp(a).getValues(); + expect(r).toEqual(new Float32Array([Math.exp(1), NaN, 1])); + a.dispose(); + }); + + it('log', () => { + const a = Array1D.new([1, 2]); + const r = math.log(a); + + expect(r.get(0)).toBeCloseTo(Math.log(1)); + expect(r.get(1)).toBeCloseTo(Math.log(2)); + + a.dispose(); + }); + + it('log propagates NaNs', () => { + const a = Array1D.new([1, NaN]); + const r = math.log(a).getValues(); + expect(r).toEqual(new Float32Array([Math.log(1), NaN])); + a.dispose(); + }); + + it('logSumExp', () => { + const a = Array1D.new([1, 2, -3]); + const result = math.logSumExp(a); + expect(result.get()) + .toBeCloseTo(Math.log(Math.exp(1) + Math.exp(2) + Math.exp(-3))); + + a.dispose(); + result.dispose(); + }); + + it('logSumExp propagates NaNs', () => { + const a = Array1D.new([1, 2, NaN]); + const result = math.logSumExp(a); + expect(result.get()).toEqual(NaN); + a.dispose(); + }); +}); + + +describe('softmax', () => { + let math: NDArrayMathGPU; + + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('regular test', () => { + const y = math.softmax(Array1D.new([2, 1, 3])); + expect(y.get(0)).toBeCloseTo(0.24472847, 6); + expect(y.get(1)).toBeCloseTo(0.09003057, 6); + expect(y.get(2)).toBeCloseTo(0.66524095, 6); + expect(y.get(0) + y.get(1) + y.get(2)).toBeCloseTo(1, 6); + }); + + it('overflow', () => { + const y = math.softmax(Array1D.new([10000, 10000])); + expect(y.get(0)).toBeCloseTo(0.5, 3); + expect(y.get(1)).toBeCloseTo(0.5, 3); + }); + + it('underflow', () => { + const y = math.softmax(Array1D.new([-10000, -10000])); + expect(y.get(0)).toBeCloseTo(0.5, 3); + expect(y.get(1)).toBeCloseTo(0.5, 3); + }); + + it('Huge difference between probabilities', () => { + const y = math.softmax(Array1D.new([-10000, +10000])); + expect(y.get(0)).toBeCloseTo(0.0, 6); + expect(y.get(1)).toBeCloseTo(1, 6); + }); + + it('Propagates NaNs', () => { + const a = Array1D.new([2, 1, NaN]); + const y = math.softmax(a); + expect(y.getValues()).toEqual(new Float32Array([NaN, NaN, NaN])); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU sum', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('sum', () => { + const a = Array2D.new([3, 2], [1, 2, 3, 0, 0, 1]); + const result = math.sum(a); + expect(result.get()).toBe(7); + + a.dispose(); + }); + + it('propagates NaNs', () => { + const a = Array2D.new([3, 2], [1, 2, 3, NaN, 0, 1]); + expect(math.sum(a).get()).toEqual(NaN); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU argmax', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('Array1D', () => { + const a = Array1D.new([1, 0, 3, 2]); + const result = math.argMax(a); + expect(result.get()).toBe(2); + + a.dispose(); + }); + + it('propagates NaNs', () => { + const a = Array1D.new([5, 0, 3, NaN, 3]); + expect(math.argMax(a).get()).toEqual(NaN); + a.dispose(); + }); +}); + +describe('NDArrayMathGPU argmin', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('argmin', () => { + const a = Array1D.new([1, 0, 3, 2]); + const result = math.argMin(a); + expect(result.get()).toBe(1); + + a.dispose(); + }); + + it('Arg min propagates NaNs', () => { + const a = Array1D.new([5, 0, NaN, 7, 3]); + expect(math.argMin(a).get()).toEqual(NaN); + + a.dispose(); + }); +}); + +describe('NDArrayMathGPU argmax equals', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('equals', () => { + const a = Array1D.new([5, 0, 3, 7, 3]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5, -100]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toBe(1); + }); + + it('not equals', () => { + const a = Array1D.new([5, 0, 3, 1, 3]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5, 0]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toBe(0); + }); + + it('propagates NaNs', () => { + const a = Array1D.new([0, 3, 1, 3]); + const b = Array1D.new([NaN, -20.0, -10.0, -5]); + const result = math.argMaxEquals(a, b); + expect(result.get()).toEqual(NaN); + }); + + it('throws when given arrays of different shape', () => { + const a = Array1D.new([5, 0, 3, 7, 3, 10]); + const b = Array1D.new([-100.3, -20.0, -10.0, -5, -100]); + expect(() => math.argMaxEquals(a, b)).toThrowError(); + }); +}); + +describe('NDArrayMathGPU conv2d', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('input=2x2x1,d2=1,f=1,s=1,p=0', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 1; + const pad = 0; + const stride = 1; + + const x = Array3D.new(inputShape, [1, 2, 3, 4]); + const w = Array4D.new([fSize, fSize, inputDepth, outputDepth], [2]); + const bias = Array1D.new([-1]); + + const result = math.conv2d(x, w, bias, stride, pad); + const expected = new Float32Array([1, 3, 5, 7]); + + expect(result.inGPU()).toBe(true); + expect(result.getValues()).toEqual(expected); + x.dispose(); + w.dispose(); + bias.dispose(); + }); + + it('input=2x2x1,d2=1,f=2,s=1,p=0', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + + const x = Array3D.new(inputShape, [1, 2, 3, 4]); + const w = + Array4D.new([fSize, fSize, inputDepth, outputDepth], [3, 1, 5, 0]); + const bias = Array1D.new([-1]); + + const result = math.conv2d(x, w, bias, stride, pad); + const expected = new Float32Array([19]); + + expect(result.inGPU()).toBe(true); + expect(result.getValues()).toEqual(expected); + + x.dispose(); + w.dispose(); + bias.dispose(); + }); + + it('throws when x is not rank 3', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + + // tslint:disable-next-line:no-any + const x: any = Array2D.new([2, 2], [1, 2, 3, 4]); + const w = + Array4D.new([fSize, fSize, inputDepth, outputDepth], [3, 1, 5, 0]); + const bias = Array1D.new([-1]); + + expect(() => math.conv2d(x, w, bias, stride, pad)).toThrowError(); + + x.dispose(); + w.dispose(); + bias.dispose(); + }); + + it('throws when weights is not rank 4', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + + const x = Array3D.new(inputShape, [1, 2, 3, 4]); + // tslint:disable-next-line:no-any + const w: any = Array3D.new([2, 2, 1], [3, 1, 5, 0]); + const bias = Array1D.new([-1]); + + expect(() => math.conv2d(x, w, bias, stride, pad)).toThrowError(); + + x.dispose(); + w.dispose(); + bias.dispose(); + }); + + it('throws when biases is not rank 1', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + + const x = Array3D.new(inputShape, [1, 2, 3, 4]); + const w = + Array4D.new([fSize, fSize, inputDepth, outputDepth], [3, 1, 5, 0]); + // tslint:disable-next-line:no-any + const bias: any = Array2D.new([2, 2], [2, 2, 2, 2]); + + expect(() => math.conv2d(x, w, bias, stride, pad)).toThrowError(); + + x.dispose(); + w.dispose(); + bias.dispose(); + }); + + it('throws when x depth does not match weight depth', () => { + const inputDepth = 1; + const wrongInputDepth = 5; + const inputShape: [number, number, number] = [2, 2, inputDepth]; + const outputDepth = 1; + const fSize = 2; + const pad = 0; + const stride = 1; + + const x = Array3D.new(inputShape, [1, 2, 3, 4]); + const w = NDArray.randNormal( + [fSize, fSize, wrongInputDepth, outputDepth]); + const bias = Array1D.new([-1]); + + expect(() => math.conv2d(x, w, bias, stride, pad)).toThrowError(); + + x.dispose(); + w.dispose(); + bias.dispose(); + }); +}); + +describe('NDArrayMathGPU conv2dTranspose', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('input=2x2x1,d2=1,f=2,s=1,p=0', () => { + const origInputDepth = 1; + const origOutputDepth = 1; + const inputShape: [number, number, number] = [1, 1, origOutputDepth]; + const fSize = 2; + const origPad = 0; + const origStride = 1; + + const x = Array3D.new(inputShape, [2]); + const w = Array4D.new( + [fSize, fSize, origInputDepth, origOutputDepth], [3, 1, 5, 0]); + const b = Array1D.new([1]); + + const result = math.conv2dTranspose(x, w, b, origStride, origPad); + const expected = new Float32Array([7, 3, 11, 1]); + + expect(result.inGPU()).toBe(true); + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(expected); + + x.dispose(); + w.dispose(); + b.dispose(); + }); + + it('throws when x is not rank 3', () => { + const origInputDepth = 1; + const origOutputDepth = 1; + const inputShape: [number, number, number] = [1, 1, origOutputDepth]; + const fSize = 2; + const origPad = 0; + const origStride = 1; + + // tslint:disable-next-line:no-any + const x: any = Array2D.new([2, 1], [2, 2]); + const w = Array4D.new( + [fSize, fSize, origInputDepth, origOutputDepth], [3, 1, 5, 0]); + const b = Array1D.new([1]); + + expect(() => math.conv2dTranspose(x, w, b, origStride, origPad)) + .toThrowError(); + + x.dispose(); + w.dispose(); + b.dispose(); + }); + + it('throws when weights is not rank 4', () => { + const origInputDepth = 1; + const origOutputDepth = 1; + const inputShape: [number, number, number] = [1, 1, origOutputDepth]; + const fSize = 2; + const origPad = 0; + const origStride = 1; + + const x = Array3D.new(inputShape, [2]); + // tslint:disable-next-line:no-any + const w: any = Array3D.new([fSize, fSize, origInputDepth], [3, 1, 5, 0]); + const b = Array1D.new([1]); + + expect(() => math.conv2dTranspose(x, w, b, origStride, origPad)) + .toThrowError(); + + x.dispose(); + w.dispose(); + b.dispose(); + }); + + it('throws when biases is not rank 1', () => { + const origInputDepth = 1; + const origOutputDepth = 1; + const inputShape: [number, number, number] = [1, 1, origOutputDepth]; + const fSize = 2; + const origPad = 0; + const origStride = 1; + + const x = Array3D.new(inputShape, [2]); + const w = Array4D.new( + [fSize, fSize, origInputDepth, origOutputDepth], [3, 1, 5, 0]); + // tslint:disable-next-line:no-any + const b: any = Array2D.new([2, 1], [1, 2]); + + expect(() => math.conv2dTranspose(x, w, b, origStride, origPad)) + .toThrowError(); + + x.dispose(); + w.dispose(); + b.dispose(); + }); + + it('throws when x depth does not match weights original output depth', () => { + const origInputDepth = 1; + const origOutputDepth = 2; + const wrongOrigOutputDepth = 3; + const inputShape: [number, number, number] = [1, 1, origOutputDepth]; + const fSize = 2; + const origPad = 0; + const origStride = 1; + + const x = Array3D.new(inputShape, [2, 2]); + const w = NDArray.randNormal( + [fSize, fSize, origInputDepth, wrongOrigOutputDepth]); + const b = Array1D.new([1]); + + expect(() => math.conv2dTranspose(x, w, b, origStride, origPad)) + .toThrowError(); + + x.dispose(); + w.dispose(); + b.dispose(); + }); +}); + +describe('NDArrayMathGPU conv2dDerWeights', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('conv2dDerWeights input=3x3x1,d2=1,f=2,s=1,p=0', () => { + const inputDepth = 1; + const outputDepth = 1; + const inputShape: [number, number, number] = [3, 3, inputDepth]; + const fSize = 2; + const stride = 1; + const pad = 0; + + const weightsShape = [fSize, fSize, inputDepth, outputDepth]; + + const x = Array3D.new(inputShape, [1, 2, 3, 4, 5, 6, 7, 8, 9]); + const dy = Array3D.new([2, 2, 1], [3, 1, 2, 0]); + + const result = math.conv2dDerWeights(x, dy, fSize, stride, pad); + const expected = new Float32Array([13, 19, 31, 37]); + + expect(result.inGPU()).toBe(true); + expect(result.shape).toEqual(weightsShape); + expect(result.getValues()).toEqual(expected); + + x.dispose(); + dy.dispose(); + }); +}); + +describe('NDArrayMathGPU conv2dDerWeights', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('conv2dDerBias dy=2x2x2', () => { + const outputDepth = 2; + const dyShape: [number, number, number] = [2, 2, outputDepth]; + const dy = Array3D.new(dyShape, [1, 2, 3, 4, 5, 6, 7, 8]); + + const result = math.conv2dDerBias(dy); + const expected = new Float32Array([16, 20]); + + expect(result.inGPU()).toBe(true); + expect(result.shape).toEqual([outputDepth]); + expect(result.getValues()).toEqual(expected); + + dy.dispose(); + }); +}); + +describe('NDArrayMathGPU maxPool', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('3x3x2 in, 2x2 filter, 1 stride', () => { + // Feed forward. + const a = Array3D.new( + [3, 3, 2], + [1, 99, 2, 88, 3, 77, 4, 66, 5, 55, 6, 44, 7, 33, 9, 22, 8, 11]); + const result = math.maxPool(a, 2, 1, 0); + + expect(result.inGPU()).toBe(true); + expect(result.shape).toEqual([2, 2, 2]); + expect(result.getValues()).toEqual(new Float32Array([ + 5, 99, 6, 88, 9, 66, 9, 55 + ])); + a.dispose(); + }); + + it('3x3x1 in, 2x2 filter, 1 stride, propagates NaNs', () => { + const a = Array3D.new([3, 3, 1], [1, 2, 3, 4, 5, 6, 7, NaN, 9]); + const result = math.maxPool(a, 2, 1, 0); + + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([5, 6, NaN, NaN])); + a.dispose(); + }); + + it('4x4x1 in, 2x2 filter, 2 stride', () => { + // Feed forward. + const a = Array3D.new( + [4, 4, 1], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + const result = math.maxPool(a, 2, 2, 0); + + expect(result.inGPU()).toBe(true); + expect(result.shape).toEqual([2, 2, 1]); + expect(result.getValues()).toEqual(new Float32Array([5, 7, 13, 15])); + + a.dispose(); + }); + + it('throws when x is not rank 3', () => { + // tslint:disable-next-line:no-any + const a: any = Array2D.new([3, 3], [1, 2, 3, 4, 5, 6, 7, 8, 9]); + expect(() => math.maxPool(a, 2, 1, 0)).toThrowError(); + + a.dispose(); + }); +}); + +describe('NDArrayMathGPU maxPoolBackprop', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('x=2x2x1, f=2, s=2, pad=1', () => { + const dy = Array3D.new([2, 2, 1], [1, 2, 3, 4]); + const maxPositions = Array3D.new([2, 2, 1], [3, 2, 1, 0]); + const expected = new Float32Array([1, 2, 3, 4]); + const dx = math.maxPoolBackprop(dy, maxPositions, 2, 2, 1); + + expect(dx.inGPU()).toBe(true); + expect(dx.getValues()).toEqual(expected); + + dy.dispose(); + maxPositions.dispose(); + dx.dispose(); + }); + + // Max pool depth > 1. + it('x=3x3x2, f=2, s=1, no duplicate max value', () => { + const dy = Array3D.new([2, 2, 2], [1, 44, 2, 33, 3, 22, 4, 11]); + const x = Array3D.new( + [3, 3, 2], + [1, 99, 2, 55, 3, 66, 4, 66, 5, 88, 6, 44, 7, 99, 8, 55, 9, 100]); + const expected = new Float32Array( + [0, 44, 0, 0, 0, 0, 0, 0, 1, 33, 2, 0, 0, 22, 3, 0, 4, 11]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + + expect(dx.inGPU()).toBe(true); + expect(dx.getValues()).toEqual(expected); + + dy.dispose(); + x.dispose(); + dx.dispose(); + }); + + it('x=3x3x2, f=2, s=1 duplicate max value', () => { + const dy = Array3D.new([2, 2, 2], [1, 44, 2, 33, 3, 22, 4, 11]); + const x = Array3D.new( + [3, 3, 2], [0, 1, 0, 3, 0, 2, 0, 1, 5, 2, 0, 1, 0, 1, 0, 1, 0, 5]); + const expected = new Float32Array( + [0, 0, 0, 77, 0, 0, 0, 0, 10, 22, 0, 0, 0, 0, 0, 0, 0, 11]); + const dx = math.maxPoolBackprop(dy, x, 2, 1, 0); + + expect(dx.inGPU()).toBe(true); + expect(dx.getValues()).toEqual(expected); + + dy.dispose(); + x.dispose(); + dx.dispose(); + }); +}); + +describe('NDArrayMathGPU resizeBilinear', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.dispose(); + }); + + it('simple alignCorners=false', () => { + const input = Array3D.new([2, 2, 1], [2, 2, 4, 4]); + const output = math.resizeBilinear3D(input, [3, 3], false); + + test_util.expectArraysClose( + output.getValues(), + new Float32Array([2, 2, 2, 10 / 3, 10 / 3, 10 / 3, 4, 4, 4]), 1e-4); + input.dispose(); + }); + + it('simple alignCorners=true', () => { + const input = Array3D.new([2, 2, 1], [2, 2, 4, 4]); + const output = math.resizeBilinear3D(input, [3, 3], true); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([2, 2, 2, 3, 3, 3, 4, 4, 4]), + 1e-4); + input.dispose(); + }); + + it('matches tensorflow w/ random numbers alignCorners=false', () => { + const input = Array3D.new([2, 3, 2], [ + 1.19074044, 0.91373104, 2.01611669, -0.52270832, 0.38725395, 1.30809779, + 0.61835143, 3.49600659, 2.09230986, 0.56473997, 0.03823943, 1.19864896 + ]); + const output = math.resizeBilinear3D(input, [4, 5], false); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([ + 1.19074047, 0.91373104, 1.68596613, 0.05186744, 1.69034398, + -0.15654698, 0.7130264, 0.94193673, 0.38725394, 1.30809784, + 0.9045459, 2.20486879, 1.59434628, 0.89455694, 1.68591988, + 0.26748738, 0.58103991, 1.00690198, 0.21274668, 1.25337338, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893 + ]), + 1e-4); + input.dispose(); + }); + + it('matches tensorflow w/ random numbers alignCorners=true', () => { + const input = Array3D.new([2, 3, 2], [ + 1.56324531, 2.13817752, 1.44398421, 1.07632684, 0.59306785, -0.36970865, + 1.62451879, 1.8367334, 1.13944798, 2.01993218, 2.01919952, 2.67524054 + ]); + const output = math.resizeBilinear3D(input, [4, 5], true); + + test_util.expectArraysClose( + output.getValues(), new Float32Array([ + 1.5632453, 2.13817763, 1.50361478, 1.60725224, 1.44398427, + 1.07632685, 1.01852608, 0.35330909, 0.59306782, -0.36970866, + 1.58366978, 2.03769612, 1.46307099, 1.71427906, 1.3424722, + 1.39086199, 1.20545864, 1.01806819, 1.06844509, 0.6452744, + 1.60409427, 1.93721485, 1.42252707, 1.82130599, 1.24096, + 1.70539713, 1.3923912, 1.68282723, 1.54382229, 1.66025746, + 1.62451875, 1.83673346, 1.38198328, 1.92833281, 1.13944793, + 2.01993227, 1.57932377, 2.34758639, 2.01919961, 2.67524052 + ]), + 1e-4); + + input.dispose(); + }); +}); + +describe('NDArrayMathGPU batchNorm', () => { + let math: NDArrayMathGPU; + beforeEach(() => { + math = new NDArrayMathGPU(); + math.startScope(); + }); + + afterEach(() => { + math.endScope(null!); + math.startScope(); + }); + + it('simple batchnorm, no offset or scale, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, undefined, undefined); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + (x.get(0, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(0, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon), + (x.get(1, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(1, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-4); + x.dispose(); + mean.dispose(); + variance.dispose(); + }); + + it('simple batchnorm, no offset, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const scale = Array1D.new([4, 5]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, undefined); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + (x.get(0, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(0, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon), + (x.get(1, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + (x.get(1, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-4); + x.dispose(); + mean.dispose(); + variance.dispose(); + scale.dispose(); + }); + + it('simple batchnorm, no scale, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const offset = Array1D.new([4, 5]); + + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, undefined, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + offset.get(0) + + (x.get(0, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(0, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon), + offset.get(0) + + (x.get(1, 0, 0) - mean.get(0)) * 1 / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(1, 0, 1) - mean.get(1)) * 1 / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-4); + x.dispose(); + mean.dispose(); + variance.dispose(); + offset.dispose(); + }); + + it('simple batchnorm, 2x1x2', () => { + const x = Array3D.new([2, 1, 2], new Float32Array([2, 100, 4, 400])); + const mean = Array1D.new([1, 2]); + const variance = Array1D.new([2, 3]); + const offset = Array1D.new([3, 4]); + const scale = Array1D.new([4, 5]); + + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + offset.get(0) + + (x.get(0, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(0, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon), + offset.get(0) + + (x.get(1, 0, 0) - mean.get(0)) * scale.get(0) / + Math.sqrt(variance.get(0) + varianceEpsilon), + offset.get(1) + + (x.get(1, 0, 1) - mean.get(1)) * scale.get(1) / + Math.sqrt(variance.get(1) + varianceEpsilon) + ]), + 1e-4); + x.dispose(); + mean.dispose(); + variance.dispose(); + scale.dispose(); + offset.dispose(); + }); + + it('batchnorm matches tensorflow, 2x3x3', () => { + const x = + Array3D.new([2, 3, 3], new Float32Array([ + 0.49955603, 0.04158615, -1.09440524, 2.03854165, + -0.61578344, 2.87533573, 1.18105987, 0.807462, 1.87888837, + 2.26563962, -0.37040935, 1.35848753, -0.75347094, + 0.15683117, 0.91925946, 0.34121279, 0.92717143, 1.89683965 + ])); + const mean = Array1D.new([0.39745062, -0.48062894, 0.4847822]); + const variance = Array1D.new([0.32375343, 0.67117643, 1.08334653]); + const offset = Array1D.new([0.69398749, -1.29056387, 0.9429723]); + const scale = Array1D.new([-0.5607271, 0.9878457, 0.25181573]); + const varianceEpsilon = .001; + + const result = math.batchNormalization3D( + x, mean, variance, varianceEpsilon, scale, offset); + + test_util.expectArraysClose( + result.getValues(), new Float32Array([ + 0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019, + 1.52106473, -0.07704776, 0.26144429, 1.28010017, -1.14422404, + -1.15776136, 1.15425493, 1.82644104, -0.52249442, 1.04803919, + 0.74932291, 0.40568101, 1.2844412 + ]), + 1e-4); + x.dispose(); + mean.dispose(); + variance.dispose(); + scale.dispose(); + offset.dispose(); + }); +}); diff --git a/src/math/ndarray.ts b/src/math/ndarray.ts new file mode 100644 index 0000000000..d12b293b62 --- /dev/null +++ b/src/math/ndarray.ts @@ -0,0 +1,569 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../util'; + +import {GPGPUContext} from './webgl/gpgpu_context'; +import {TextureManager} from './webgl/texture_manager'; +import * as webgl_util from './webgl/webgl_util'; + +// These global variables need to be initialized to null so that closure knows +// not to seal them. +/** @hidden */ +export let GPGPU: GPGPUContext = null!; +/** @hidden */ +export let TEXTURE_MANAGER: TextureManager = null!; + +/** @hidden */ +export interface NDArrayData { + values?: Float32Array; + texture?: WebGLTexture; + /** [rows, columns] shape of the texture. */ + textureShapeRC?: [number, number]; +} + +/** @hidden */ +export function initializeGPU( + gpgpu: GPGPUContext, textureManager: TextureManager) { + GPGPU = gpgpu; + TEXTURE_MANAGER = textureManager; +} + +function throwIfGPUNotInitialized() { + if (GPGPU == null || TEXTURE_MANAGER == null) { + throw new Error('GPU not intialized.'); + } +} + +export class NDArray { + /** The shape of the ndarray. */ + shape: number[]; + /** Number of elements in the ndarray. */ + size: number; + + /** + * Number of elements to skip in each dimension when indexing. See + * https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.strides.html + */ + protected strides: number[]; + + private data: NDArrayData; + + protected constructor(shape: number[], data: NDArrayData) { + // Sanity checks. + util.assert( + data.values != null || data.texture != null, + 'Either `values` or `texture` must be defined'); + + util.assert( + data.texture == null || (data.textureShapeRC != null), + '`textureShape` must be defined when `texture` is defined'); + + this.size = util.sizeFromShape(shape); + + if (data.values != null) { + util.assert( + this.size === data.values.length, + 'Constructing ndarray of shape (' + this.size + ') should match the' + + ' length of values (' + data.values.length + ')'); + } + + this.shape = shape; + this.data = data; + const dim = this.shape.length; + + if (dim < 2) { + this.strides = []; + } else { + // Last dimension has implicit stride of 1, thus having D-1 (instead of D) + // strides. + this.strides = new Array(dim - 1); + this.strides[dim - 2] = this.shape[dim - 1]; + for (let i = dim - 3; i >= 0; --i) { + this.strides[i] = this.strides[i + 1] * this.shape[i + 1]; + } + } + } + + /** Creates a ndarray of zeros with the specified shape. */ + static zeros(shape: number[]): T { + const values = new Float32Array(util.sizeFromShape(shape)); + return NDArray.make(shape, {values}); + } + + /** Creates a ndarray of zeros with the same shape as the specified ndarray. + */ + static zerosLike(another: T): T { + return NDArray.zeros(another.shape) as T; + } + + /** Creates a ndarray with the same values/shape as the specified ndarray. */ + static like(another: T): T { + const values = another.getValues(); + return NDArray.make(another.shape, {values: new Float32Array(values)}); + } + + /** + * Makes a new ndarray with the provided shape and values. Values should be in + * a flat array. + */ + static make(shape: number[], data: NDArrayData): T { + switch (shape.length) { + case 0: + return new Scalar(data) as T; + case 1: + // tslint:disable-next-line:no-any + return new Array1D(data) as any; + case 2: + // tslint:disable-next-line:no-any + return new Array2D(shape as [number, number], data) as any; + case 3: + // tslint:disable-next-line:no-any + return new Array3D(shape as [number, number, number], data) as any; + case 4: + return new Array4D( + // tslint:disable-next-line:no-any + shape as [number, number, number, number], data) as any; + default: + // tslint:disable-next-line:no-any + return new NDArray(shape, data) as any; + } + } + + /** Reshapes the current ndarray into the provided shape. */ + reshape(newShape: number[]): T { + if (util.arraysEqual(this.shape, newShape)) { + // No-op. + // tslint:disable-next-line:no-any + return this as any; + } + + util.assert( + this.size === util.sizeFromShape(newShape), + 'new shape and old shape must have the same number of elements.'); + + return NDArray.make(newShape, this.data); + } + + asScalar(): Scalar { + util.assert(this.size === 1, 'The array must have only 1 element.'); + return this.reshape([]); + } + + as1D(): Array1D { + return this.reshape([this.size]); + } + + as2D(rows: number, columns: number): Array2D { + return this.reshape([rows, columns]); + } + + as3D(rows: number, columns: number, depth: number): Array3D { + return this.reshape([rows, columns, depth]); + } + + as4D(rows: number, columns: number, depth: number, depth2: number): Array4D { + return this.reshape([rows, columns, depth, depth2]); + } + + get rank(): number { + return this.shape.length; + } + + get(...locs: number[]) { + let index = locs[locs.length - 1]; + for (let i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return this.getValues()[index]; + } + + add(value: number, ...locs: number[]) { + this.set(this.get(...locs) + value, ...locs); + } + + set(value: number, ...locs: number[]) { + let index = locs[locs.length - 1]; + for (let i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + this.getValues()[index] = value; + } + + locToIndex(locs: number[]): number { + let index = locs[locs.length - 1]; + for (let i = 0; i < locs.length - 1; ++i) { + index += this.strides[i] * locs[i]; + } + return index; + } + + indexToLoc(index: number): number[] { + const locs: number[] = new Array(this.shape.length); + for (let i = 0; i < locs.length - 1; ++i) { + locs[i] = Math.floor(index / this.strides[i]); + index -= locs[i] * this.strides[i]; + } + locs[locs.length - 1] = index; + return locs; + } + + fill(value: number) { + this.getValues().fill(value); + } + + getData(): NDArrayData { + return this.data; + } + + getValues(): Float32Array { + if (this.data.values == null) { + throwIfGPUNotInitialized(); + this.data.values = GPGPU.downloadMatrixFromTexture( + this.data.texture!, this.data.textureShapeRC![0], + this.data.textureShapeRC![1]); + this.disposeTexture(); + } + return this.data.values; + } + + private uploadToGPU(preferredTexShape?: [number, number]) { + throwIfGPUNotInitialized(); + this.data.textureShapeRC = webgl_util.getTextureShapeFromLogicalShape( + GPGPU.gl, this.shape, preferredTexShape); + this.data.texture = + TEXTURE_MANAGER.acquireTexture(this.data.textureShapeRC); + + GPGPU.uploadMatrixToTexture( + this.data.texture, this.data.textureShapeRC[0], + this.data.textureShapeRC[1], this.data.values!); + + this.data.values = null!; + } + + getTexture(preferredShapeRC?: [number, number]): WebGLTexture { + if (this.data.texture == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.texture!; + } + + getTextureShapeRC(preferredShapeRC?: [number, number]): [number, number] { + if (this.data.textureShapeRC == null) { + this.uploadToGPU(preferredShapeRC); + } + return this.data.textureShapeRC!; + } + + dispose(): void { + this.data.values = null!; + this.shape = null!; + if (this.data.texture != null) { + this.disposeTexture(); + } + } + + private disposeTexture() { + throwIfGPUNotInitialized(); + TEXTURE_MANAGER.releaseTexture( + this.data.texture!, this.data.textureShapeRC!); + this.data.texture = null!; + this.data.textureShapeRC = null!; + } + + inGPU(): boolean { + return this.data.texture != null; + } + + equals(t: NDArray): boolean { + return util.arraysEqual(this.shape, t.shape) && + util.arraysEqual(this.getValues(), t.getValues()); + } + + static rand(shape: number[], randFunction: () => number): + T { + const size = util.sizeFromShape(shape); + const values = new Float32Array(size); + for (let i = 0; i < size; i++) { + values[i] = randFunction(); + } + + return NDArray.make(shape, {values}); + } + + static randNormal(shape: number[], mean = 0, stdDev = 1) { + return NDArray.rand(shape, () => util.randGauss(mean, stdDev)); + } + + static randTruncatedNormal( + shape: number[], mean = 0, stdDev = 1) { + return NDArray.rand(shape, () => util.randGauss(mean, stdDev, true)); + } + + static randUniform(shape: number[], a: number, b: number) { + return NDArray.rand(shape, () => util.randUniform(a, b)); + } +} + +export class Scalar extends NDArray { + constructor(data: NDArrayData) { + if (data.texture != null) { + data.textureShapeRC = [1, 1]; + } + super([], data); + } + + static new(value: number) { + return new Scalar({values: new Float32Array([value])}); + } + + static ZERO = Scalar.new(0); + static ONE = Scalar.new(1); + static TWO = Scalar.new(2); + static NEG_ONE = Scalar.new(-1); + + get(): number { + return this.getValues()[0]; + } + + set(value: number) { + this.getValues()[0] = value; + } + + add(value: number) { + this.getValues()[0] += value; + } +} + +export class Array1D extends NDArray { + shape: [number]; + + constructor(data: NDArrayData) { + const shape = (data.values != null) ? + [data.values.length] : + [util.sizeFromShape(data.textureShapeRC!)]; + super(shape, data); + } + + static new(values: Float32Array|number[]) { + if (!(values instanceof Float32Array)) { + const inferredShape = util.inferShape(values); + util.assert( + inferredShape.length === 1, + `Error constructing Array1D. Shape of values ${inferredShape} is ` + + `not 1 dimensional.`); + } + return new Array1D({values: toTypedArray(values)}); + } + + get(i: number): number { + return this.getValues()[i]; + } + + set(value: number, i: number) { + this.getValues()[i] = value; + } + + add(value: number, i: number) { + this.getValues()[i] += value; + } + + locToIndex(loc: [number]): number { + return loc[0]; + } + + indexToLoc(index: number): [number] { + return [index]; + } + + static zeros(shape: [number]): Array1D { + return NDArray.zeros(shape); + } +} + +export class Array2D extends NDArray { + shape: [number, number]; + + private stride0: number; + + constructor(shape: [number, number], data: NDArrayData) { + util.assert(shape.length === 2, 'Shape should be of length 2'); + super(shape, data); + this.stride0 = this.strides[0]; + } + + static new( + shape: [number, number], values: Float32Array|number[]|number[][]) { + if (!(values instanceof Float32Array)) { + const inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch( + shape, inferredShape, + `Error when constructing Array2D. Shape of values ` + + `${inferredShape} does not match the provided shape ` + + `${shape}. `); + } + } + return new Array2D(shape, {values: toTypedArray(values)}); + } + + get(i: number, j: number) { + return this.getValues()[this.stride0 * i + j]; + } + + set(value: number, i: number, j: number) { + this.getValues()[this.stride0 * i + j] = value; + } + + add(value: number, i: number, j: number) { + this.getValues()[this.stride0 * i + j] += value; + } + + locToIndex(locs: [number, number]): number { + return this.stride0 * locs[0] + locs[1]; + } + + indexToLoc(index: number): [number, number] { + return [Math.floor(index / this.stride0), index % this.stride0]; + } + + static zeros(shape: [number, number]): Array2D { + return NDArray.zeros(shape); + } +} + +export class Array3D extends NDArray { + shape: [number, number, number]; + private stride0: number; + private stride1: number; + + constructor(shape: [number, number, number], data: NDArrayData) { + util.assert(shape.length === 3, 'Shape should be of length 3'); + super(shape, data); + this.stride0 = this.strides[0]; + this.stride1 = this.strides[1]; + } + + static new( + shape: [number, number, number], + values: Float32Array|number[]|number[][][]) { + if (!(values instanceof Float32Array)) { + const inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch( + shape, inferredShape, + `Error when constructing Array3D. Shape of values ` + + `${inferredShape} does not match the provided shape ` + + `${shape}. `); + } + } + return new Array3D(shape, {values: toTypedArray(values)}); + } + + get(i: number, j: number, k: number) { + return this.getValues()[this.stride0 * i + this.stride1 * j + k]; + } + + set(value: number, i: number, j: number, k: number) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] = value; + } + + add(value: number, i: number, j: number, k: number) { + this.getValues()[this.stride0 * i + this.stride1 * j + k] += value; + } + + locToIndex(locs: [number, number, number]): number { + return this.stride0 * locs[0] + this.stride1 * locs[1] + locs[2]; + } + + indexToLoc(index: number): [number, number, number] { + const i = Math.floor(index / this.stride0); + index -= i * this.stride0; + return [i, Math.floor(index / this.stride1), index % this.stride1]; + } + + static zeros(shape: [number, number, number]): Array3D { + return NDArray.zeros(shape); + } +} + +export class Array4D extends NDArray { + shape: [number, number, number, number]; + private stride0: number; + private stride1: number; + private stride2: number; + + constructor(shape: [number, number, number, number], data: NDArrayData) { + util.assert(shape.length === 4, 'Shape should be of length 4'); + super(shape, data); + this.stride0 = this.strides[0]; + this.stride1 = this.strides[1]; + this.stride2 = this.strides[2]; + } + + static new( + shape: [number, number, number, number], + values: Float32Array|number[]|number[][][][]) { + if (!(values instanceof Float32Array)) { + const inferredShape = util.inferShape(values); + if (inferredShape.length > 1) { + util.assertShapesMatch( + shape, inferredShape, + `Error when constructing Array4D. Shape of values ` + + `${inferredShape} does not match the provided shape ` + + `${shape}. `); + } + } + return new Array4D(shape, {values: toTypedArray(values)}); + } + + get(i: number, j: number, k: number, l: number) { + return this.getValues() + [this.stride0 * i + this.stride1 * j + this.stride2 * k + l]; + } + + set(value: number, i: number, j: number, k: number, l: number) { + this.getValues() + [this.stride0 * i + this.stride1 * j + this.stride2 * k + l] = value; + } + + add(value: number, i: number, j: number, k: number, l: number) { + this.getValues() + [this.stride0 * i + this.stride1 * j + this.stride2 * k + l] += value; + } + + locToIndex(locs: [number, number, number, number]): number { + return this.stride0 * locs[0] + this.stride1 * locs[1] + + this.stride2 * locs[2] + locs[3]; + } + + indexToLoc(index: number): [number, number, number, number] { + const i = Math.floor(index / this.stride0); + index -= i * this.stride0; + const j = Math.floor(index / this.stride1); + index -= j * this.stride1; + return [i, j, Math.floor(index / this.stride2), index % this.stride2]; + } + + static zeros(shape: [number, number, number, number]): Array4D { + return NDArray.zeros(shape); + } +} + +type ArrayData = Float32Array|number[]|number[][]|number[][][]|number[][][][]; + +function toTypedArray(a: ArrayData): Float32Array { + return (a instanceof Float32Array) ? a : new Float32Array(util.flatten(a)); +} diff --git a/src/math/ndarray_test.ts b/src/math/ndarray_test.ts new file mode 100644 index 0000000000..b8f8604fce --- /dev/null +++ b/src/math/ndarray_test.ts @@ -0,0 +1,338 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as ndarray from './ndarray'; +import {Array1D, Array2D, Array3D, Array4D, GPGPU, NDArray, Scalar, TEXTURE_MANAGER} from './ndarray'; +import {GPGPUContext} from './webgl/gpgpu_context'; +import * as gpgpu_util from './webgl/gpgpu_util'; +import {TextureManager} from './webgl/texture_manager'; + +describe('NDArray', () => { + let gl: WebGLRenderingContext; + let gpgpu: GPGPUContext; + let textureManager: TextureManager; + + beforeEach(() => { + gl = gpgpu_util.createWebGLContext(); + gpgpu = new GPGPUContext(gl); + textureManager = new TextureManager(gpgpu); + ndarray.initializeGPU(gpgpu, textureManager); + }); + + afterEach(() => { + textureManager.dispose(); + gpgpu.dispose(); + }); + + it('NDArrays of arbitrary size', () => { + // [1, 2, 3] + let t: NDArray = Array1D.new([1, 2, 3]); + expect(t instanceof Array1D).toBe(true); + expect(t.rank).toBe(1); + expect(t.size).toBe(3); + expect(t.getValues()).toEqual(new Float32Array([1, 2, 3])); + expect(t.get(0)).toBe(1); + expect(t.get(1)).toBe(2); + expect(t.get(2)).toBe(3); + // Out of bounds indexing. + expect(t.get(4)).toBeUndefined(); + + // [[1, 2, 3]] + t = Array2D.new([1, 3], [1, 2, 3]); + expect(t instanceof Array2D).toBe(true); + expect(t.rank).toBe(2); + expect(t.size).toBe(3); + expect(t.get(0, 0)).toBe(1); + expect(t.get(0, 1)).toBe(2); + expect(t.get(0, 2)).toBe(3); + // Out of bounds indexing. + expect(t.get(4)).toBeUndefined(); + + // [[1, 2, 3], + // [4, 5, 6]] + t = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + expect(t instanceof Array2D).toBe(true); + expect(t.rank).toBe(2); + expect(t.size).toBe(6); + expect(t.get(0, 0)).toBe(1); + expect(t.get(0, 1)).toBe(2); + expect(t.get(0, 2)).toBe(3); + expect(t.get(1, 0)).toBe(4); + expect(t.get(1, 1)).toBe(5); + expect(t.get(1, 2)).toBe(6); + // Out of bounds indexing. + expect(t.get(5, 3)).toBeUndefined(); + + // Shape mismatch with the values. + expect(() => Array2D.new([1, 2], [1])).toThrowError(); + }); + + it('NDArrays of explicit size', () => { + const t = Array1D.new([5, 3, 2]); + expect(t.rank).toBe(1); + expect(t.shape).toEqual([3]); + expect(t.get(1)).toBe(3); + + expect(() => Array3D.new([1, 2, 3, 5], [ + 1, 2 + ])).toThrowError('Shape should be of length 3'); + + const t4 = Array4D.new([1, 2, 1, 2], [1, 2, 3, 4]); + expect(t4.get(0, 0, 0, 0)).toBe(1); + expect(t4.get(0, 0, 0, 1)).toBe(2); + expect(t4.get(0, 1, 0, 0)).toBe(3); + expect(t4.get(0, 1, 0, 1)).toBe(4); + + const t4Like = NDArray.like(t4); + // Change t4. + t4.set(10, 0, 0, 0, 1); + expect(t4.get(0, 0, 0, 1)).toBe(10); + // Make suree t4_like hasn't changed. + expect(t4Like.get(0, 0, 0, 1)).toBe(2); + + // NDArray of zeros. + const z = NDArray.zeros([3, 4, 2]) as Array3D; + expect(z.rank).toBe(3); + expect(z.size).toBe(24); + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 4; j++) { + for (let k = 0; k < 2; k++) { + expect(z.get(i, j, k)).toBe(0); + } + } + } + + // Reshaping ndarrays. + const a = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const b = a.reshape([3, 2, 1]); + expect(a.get(1, 2)).toBe(6); + + // Modify the reshaped ndarray. + b.set(10, 2, 1, 0); + // Make sure the original ndarray is also modified. + expect(a.get(1, 2)).toBe(10); + }); + + it('NDArray CPU --> GPU', () => { + const a = Array2D.new([3, 2], [1, 2, 3, 4, 5, 6]); + + expect(a.inGPU()).toBe(false); + + expect(a.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + + expect(a.inGPU()).toBe(false); + + // Upload to GPU. + expect(a.getTexture() != null).toBe(true); + + expect(a.inGPU()).toBe(true); + a.dispose(); + }); + + it('NDArray GPU --> CPU', () => { + const texture = textureManager.acquireTexture([3, 2]); + gpgpu.uploadMatrixToTexture( + texture, 3, 2, new Float32Array([1, 2, 3, 4, 5, 6])); + + const a = new Array2D([3, 2], {texture, textureShapeRC: [3, 2]}); + expect(a.inGPU()).toBe(true); + + expect(a.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + expect(a.inGPU()).toBe(false); + }); + + it('Scalar basic methods', () => { + const a = Scalar.new(5); + expect(a.get()).toBe(5); + expect(a.getValues()).toEqual(new Float32Array([5])); + expect(a.rank).toBe(0); + expect(a.size).toBe(1); + expect(a.shape).toEqual([]); + }); + + it('Scalar in GPU', () => { + const texture = textureManager.acquireTexture([1, 1]); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([10])); + + const a = new Scalar({texture}); + expect(a.inGPU()).toBe(true); + expect(a.getValues()).toEqual(new Float32Array([10])); + expect(a.inGPU()).toBe(false); + }); + + it('Array1D in GPU', () => { + const texture = textureManager.acquireTexture([1, 3]); + gpgpu.uploadMatrixToTexture(texture, 1, 3, new Float32Array([10, 7, 3])); + + const a = new Array1D({texture, textureShapeRC: [1, 3]}); + expect(a.inGPU()).toBe(true); + expect(a.getValues()).toEqual(new Float32Array([10, 7, 3])); + expect(a.inGPU()).toBe(false); + }); + + it('Array1D in GPU, but incorrect c-tor (missing textureShape)', () => { + const texture = textureManager.acquireTexture([1, 3]); + gpgpu.uploadMatrixToTexture(texture, 1, 3, new Float32Array([10, 7, 3])); + + const f = () => { + return new Array1D({texture}); + }; + + expect(f).toThrowError(); + textureManager.releaseTexture(texture, [1, 3]); + }); + + it('NDArray.make() constructs a Scalar', () => { + const a = NDArray.make([], {values: new Float32Array([3])}); + expect(a instanceof Scalar).toBe(true); + }); + + it('Array2D in GPU, reshaped to Array1D', () => { + const texture = textureManager.acquireTexture([2, 2]); + gpgpu.uploadMatrixToTexture(texture, 2, 2, new Float32Array([10, 7, 3, 5])); + + const a = new Array2D([2, 2], {texture, textureShapeRC: [2, 2]}); + const a1d = a.as1D(); + + expect(a1d.getValues()).toEqual(new Float32Array([10, 7, 3, 5])); + }); + + it('Array1D in GPU, reshaped to Array2D', () => { + const texture = textureManager.acquireTexture([1, 4]); + gpgpu.uploadMatrixToTexture(texture, 1, 4, new Float32Array([10, 7, 3, 5])); + + const a = new Array1D({texture, textureShapeRC: [1, 4]}); + const a2d = a.as2D(2, 2); + + expect(a2d.getValues()).toEqual(new Float32Array([10, 7, 3, 5])); + }); + + it('Array2D in GPU with custom texture shape', () => { + const texture = textureManager.acquireTexture([4, 1]); + gpgpu.uploadMatrixToTexture(texture, 4, 1, new Float32Array([10, 7, 3, 5])); + + const a = new Array2D([2, 2], {texture, textureShapeRC: [4, 1]}); + + expect(a.getValues()).toEqual(new Float32Array([10, 7, 3, 5])); + }); + + it('index2Loc Array1D', () => { + const t = Array1D.zeros([3]); + expect(t.indexToLoc(0)).toEqual([0]); + expect(t.indexToLoc(1)).toEqual([1]); + expect(t.indexToLoc(2)).toEqual([2]); + }); + + it('index2Loc Array2D', () => { + const t = Array2D.zeros([3, 2]); + expect(t.indexToLoc(0)).toEqual([0, 0]); + expect(t.indexToLoc(1)).toEqual([0, 1]); + expect(t.indexToLoc(2)).toEqual([1, 0]); + expect(t.indexToLoc(3)).toEqual([1, 1]); + expect(t.indexToLoc(4)).toEqual([2, 0]); + expect(t.indexToLoc(5)).toEqual([2, 1]); + }); + + it('index2Loc Array3D', () => { + const t = Array2D.zeros([3, 2, 2]); + expect(t.indexToLoc(0)).toEqual([0, 0, 0]); + expect(t.indexToLoc(1)).toEqual([0, 0, 1]); + expect(t.indexToLoc(2)).toEqual([0, 1, 0]); + expect(t.indexToLoc(3)).toEqual([0, 1, 1]); + expect(t.indexToLoc(4)).toEqual([1, 0, 0]); + expect(t.indexToLoc(5)).toEqual([1, 0, 1]); + expect(t.indexToLoc(11)).toEqual([2, 1, 1]); + }); + + it('index2Loc NDArray 5D', () => { + const values = new Float32Array([1, 2, 3, 4]); + const t = NDArray.make([2, 1, 1, 1, 2], {values}); + expect(t.indexToLoc(0)).toEqual([0, 0, 0, 0, 0]); + expect(t.indexToLoc(1)).toEqual([0, 0, 0, 0, 1]); + expect(t.indexToLoc(2)).toEqual([1, 0, 0, 0, 0]); + expect(t.indexToLoc(3)).toEqual([1, 0, 0, 0, 1]); + }); + + it('preferred texture shape, Scalar', () => { + const t = Scalar.new(1); + expect(t.getTextureShapeRC()).toEqual([1, 1]); + }); + + it('preferred texture shape, Array1D column vector', () => { + const t = Array1D.zeros([4]); + expect(t.getTextureShapeRC()).toEqual([4, 1]); + }); + + it('preferred texture shape, Array2D same shape', () => { + const t = Array2D.zeros([5, 2]); + expect(t.getTextureShapeRC()).toEqual([5, 2]); + }); + + it('preferred texture shape, Array3D depth strided along columns', () => { + const t = Array3D.zeros([2, 2, 2]); + expect(t.getTextureShapeRC()).toEqual([2, 4]); + }); + + it('preferred texture shape, Array4D is squareish', () => { + const t = Array4D.zeros([8, 2, 4, 4]); + expect(t.getTextureShapeRC()).toEqual([16, 16]); + }); +}); // Close describe. + +describe('NDArray.new method', () => { + it('Array1D.new() from number[]', () => { + const a = Array1D.new([1, 2, 3]); + expect(a.getValues()).toEqual(new Float32Array([1, 2, 3])); + }); + + it('Array1D.new() from number[][], shape mismatch', () => { + // tslint:disable-next-line:no-any + expect(() => Array1D.new([[1], [2], [3]] as any)).toThrowError(); + }); + + it('Array2D.new() from number[][]', () => { + const a = Array2D.new([2, 3], [[1, 2, 3], [4, 5, 6]]); + expect(a.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('Array2D.new() from number[][], but shape does not match', () => { + // Actual shape is [2, 3]. + expect(() => Array2D.new([3, 2], [[1, 2, 3], [4, 5, 6]])).toThrowError(); + }); + + it('Array3D.new() from number[][][]', () => { + const a = Array3D.new([2, 3, 1], [[[1], [2], [3]], [[4], [5], [6]]]); + expect(a.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('Array3D.new() from number[][][], but shape does not match', () => { + const values = [[[1], [2], [3]], [[4], [5], [6]]]; + // Actual shape is [2, 3, 1]. + expect(() => Array3D.new([3, 2, 1], values)).toThrowError(); + }); + + it('Array4D.new() from number[][][][]', () => { + const a = Array4D.new([2, 2, 1, 1], [[[[1]], [[2]]], [[[4]], [[5]]]]); + expect(a.getValues()).toEqual(new Float32Array([1, 2, 4, 5])); + }); + + it('Array4D.new() from number[][][][], but shape does not match', () => { + const f = () => { + // Actual shape is [2, 2, 1, 1]. + Array4D.new([2, 1, 2, 1], [[[[1]], [[2]]], [[[4]], [[5]]]]); + }; + expect(f).toThrowError(); + }); +}); diff --git a/src/math/webgl/addscaledmat_gpu.ts b/src/math/webgl/addscaledmat_gpu.ts new file mode 100644 index 0000000000..57dee24ad6 --- /dev/null +++ b/src/math/webgl/addscaledmat_gpu.ts @@ -0,0 +1,84 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource(): string { + return ` + precision highp float; + uniform sampler2D matrixA; + uniform sampler2D matrixB; + uniform sampler2D matrixAScalar; + uniform sampler2D matrixBScalar; + varying vec2 resultUV; + + const vec2 halfTexel = vec2(0.5, 0.5); + + void main() { + float a = texture2D(matrixA, resultUV).r; + float b = texture2D(matrixB, resultUV).r; + float aScalar = texture2D(matrixAScalar, halfTexel).r; + float bScalar = texture2D(matrixBScalar, halfTexel).r; + vec2 abScaled = vec2(a, b) * vec2(aScalar, bScalar); + gl_FragColor = vec4(abScaled.x + abScaled.y, 0, 0, 0); + }`; +} + +export function addScaledMatrices( + gpgpu: GPGPUContext, addScaledMatricesProgram: WebGLProgram, + a: WebGLTexture, b: WebGLTexture, rows: number, columns: number, + aScalar: WebGLTexture, bScalar: WebGLTexture, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(addScaledMatricesProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.setInputMatrixTexture(aScalar, 'matrixAScalar', 2); + gpgpu.setInputMatrixTexture(bScalar, 'matrixBScalar', 3); + gpgpu.executeProgram(); +} + +export function uploadAddScaledMatricesDownload( + a: Float32Array, b: Float32Array, rows: number, columns: number, + aScalar: number, bScalar: number): Float32Array { + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = gpgpu.createProgram(getFragmentShaderSource()); + + const aTex = gpgpu.createMatrixTexture(rows, columns); + const bTex = gpgpu.createMatrixTexture(rows, columns); + const aScalarTex = gpgpu.createMatrixTexture(1, 1); + const bScalarTex = gpgpu.createMatrixTexture(1, 1); + const resultTex = gpgpu.createMatrixTexture(rows, columns); + + gpgpu.uploadMatrixToTexture(aTex, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTex, rows, columns, b); + gpgpu.uploadMatrixToTexture(aScalarTex, 1, 1, new Float32Array([aScalar])); + gpgpu.uploadMatrixToTexture(bScalarTex, 1, 1, new Float32Array([bScalar])); + + addScaledMatrices( + gpgpu, program, aTex, bTex, rows, columns, aScalarTex, bScalarTex, + resultTex); + + const result = gpgpu.downloadMatrixFromTexture(resultTex, rows, columns); + + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteMatrixTexture(bTex); + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aScalarTex); + gpgpu.deleteMatrixTexture(bScalarTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return result; +} diff --git a/src/math/webgl/addscaledmat_gpu_test.ts b/src/math/webgl/addscaledmat_gpu_test.ts new file mode 100644 index 0000000000..617bb17383 --- /dev/null +++ b/src/math/webgl/addscaledmat_gpu_test.ts @@ -0,0 +1,75 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as addscaledmat_gpu from './addscaledmat_gpu'; + +function cpuAddScaledMatrices( + a: Float32Array, aScalar: number, b: Float32Array, + bScalar: number): Float32Array { + const result = new Float32Array(a.length); + for (let i = 0; i < result.length; ++i) { + result[i] = (a[i] * aScalar) + (b[i] * bScalar); + } + return result; +} + +describe('addscaledmat_gpu', () => { + it('returns a matrix with the same shape as the input matrices', () => { + const a = new Float32Array(9 * 14); + const b = new Float32Array(a.length); + const result = + addscaledmat_gpu.uploadAddScaledMatricesDownload(a, b, 9, 14, 0, 0); + expect(result.length).toEqual(9 * 14); + }); + + it('returns A + B when scalars are 1', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6]); + const result = + addscaledmat_gpu.uploadAddScaledMatricesDownload(a, b, 3, 2, 1, 1); + test_util.expectArraysClose( + result, new Float32Array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6]), 0.0001); + }); + + it('returns A * aScalar when B and bScalar are 0', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array(a.length); + const result = + addscaledmat_gpu.uploadAddScaledMatricesDownload(a, b, 3, 2, 1.1, 0); + test_util.expectArraysClose( + result, new Float32Array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6]), 0.0001); + }); + + it('returns B * bScalar when A and aScalar are 0', () => { + const b = new Float32Array([1, 2, 3, 4, 5, 6]); + const a = new Float32Array(b.length); + const result = + addscaledmat_gpu.uploadAddScaledMatricesDownload(a, b, 3, 2, 0, 1.1); + test_util.expectArraysClose( + result, new Float32Array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6]), 0.0001); + }); + + it('returns (A * aScalar) + (B * bScalar)', () => { + const a = test_util.randomArrayInRange(12 * 12, -2, 2); + const b = test_util.randomArrayInRange(a.length, -10, 10); + const aScalar = 0.5; + const bScalar = 0.25; + const result = addscaledmat_gpu.uploadAddScaledMatricesDownload( + a, b, 12, 12, aScalar, bScalar); + test_util.expectArraysClose( + result, cpuAddScaledMatrices(a, aScalar, b, bScalar), 0.001); + }); +}); diff --git a/src/math/webgl/addsubmuldiv_gpu.ts b/src/math/webgl/addsubmuldiv_gpu.ts new file mode 100644 index 0000000000..3ea00a7d92 --- /dev/null +++ b/src/math/webgl/addsubmuldiv_gpu.ts @@ -0,0 +1,116 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MatrixOrientation} from '../math'; + +import * as binaryop_gpu from './binaryop_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +export type Operation = '+' | '-' | '*' | '/'; + +export enum OperandType { + MATRIX, + SCALAR +} + +export function getFragmentShaderSource( + aType: OperandType, aOrientation: MatrixOrientation, op: Operation, + bType: OperandType, bOrientation: MatrixOrientation): string { + const aUV = operandToShaderSnippet(aType, aOrientation); + const bUV = operandToShaderSnippet(bType, bOrientation); + const resultOp = `gl_FragColor = vec4(a ${op} b, 0, 0, 0);`; + return binaryop_gpu.getFragmentShaderSource(aUV, bUV, resultOp); +} + +function operandToShaderSnippet( + operand: OperandType, orientation: MatrixOrientation): string { + switch (operand) { + case OperandType.MATRIX: + return 'resultUV' + + (orientation === MatrixOrientation.REGULAR ? '.st' : '.ts'); + case OperandType.SCALAR: + return 'vec2(0.5, 0.5)'; + default: + throw new Error('Unknown operand type'); + } +} + +export function addSubMulDiv( + gpgpu: GPGPUContext, program: WebGLProgram, a: WebGLTexture, + aShapeRowCol: [number, number], b: WebGLTexture, + bShapeRowCol: [number, number], result: WebGLTexture, + resultShapeRowCol: [number, number]) { + return binaryop_gpu.binaryOp( + gpgpu, program, a, aShapeRowCol, b, bShapeRowCol, result, + resultShapeRowCol); +} + +export function uploadScalarPlusMatrixDownload( + a: number, b: Float32Array, bShape: [number, number], + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.SCALAR, MatrixOrientation.REGULAR, '+', OperandType.MATRIX, + bOrientation); + return binaryop_gpu.uploadBinaryOpDownload( + new Float32Array([a]), [1, 1], b, bShape, src); +} + +export function uploadMatrixMinusScalarDownload( + a: Float32Array, aShape: [number, number], b: number, + aOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.MATRIX, aOrientation, '-', OperandType.SCALAR, + MatrixOrientation.REGULAR); + return binaryop_gpu.uploadBinaryOpDownload( + a, aShape, new Float32Array([b]), [1, 1], src); +} + +export function uploadScalarMinusMatrixDownload( + a: number, b: Float32Array, bShape: [number, number], + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.SCALAR, MatrixOrientation.REGULAR, '-', OperandType.MATRIX, + bOrientation); + return binaryop_gpu.uploadBinaryOpDownload( + new Float32Array([a]), [1, 1], b, bShape, src); +} + +export function uploadScalarTimesMatrixDownload( + a: number, b: Float32Array, bShape: [number, number], + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.SCALAR, MatrixOrientation.REGULAR, '*', OperandType.MATRIX, + bOrientation); + return binaryop_gpu.uploadBinaryOpDownload( + new Float32Array([a]), [1, 1], b, bShape, src); +} + +export function uploadMatrixTimesMatrixDownload( + a: Float32Array, b: Float32Array, shape: [number, number], + aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.MATRIX, aOrientation, '*', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} + +export function uploadMatrixPlusMatrixDownload( + a: Float32Array, b: Float32Array, shape: [number, number], + aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const src = getFragmentShaderSource( + OperandType.MATRIX, aOrientation, '+', OperandType.MATRIX, bOrientation); + return binaryop_gpu.uploadBinaryOpDownload(a, shape, b, shape, src); +} diff --git a/src/math/webgl/addsubmuldiv_gpu_test.ts b/src/math/webgl/addsubmuldiv_gpu_test.ts new file mode 100644 index 0000000000..5087ce05c6 --- /dev/null +++ b/src/math/webgl/addsubmuldiv_gpu_test.ts @@ -0,0 +1,214 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import {MatrixOrientation} from '../math'; + +import * as addsubmuldiv_gpu from './addsubmuldiv_gpu'; + +describe('addsubmuldiv_gpu ScalarPlusMatrix', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(12 * 513); + const result = + addsubmuldiv_gpu.uploadScalarPlusMatrixDownload(0, a, [12, 513]); + expect(result.length).toEqual(a.length); + }); + + it('preserves the matrix when the scalar is 0', () => { + const a = new Float32Array([1, 2, 3]); + const result = + addsubmuldiv_gpu.uploadScalarPlusMatrixDownload(0, a, [1, 3]); + test_util.expectArraysClose(result, a, 0); + }); + + it('adds the scalar to every element in the matrix', () => { + const a = new Float32Array([1, 2, 3, 4]); + const result = + addsubmuldiv_gpu.uploadScalarPlusMatrixDownload(0.5, a, [2, 2]); + test_util.expectArraysClose( + result, new Float32Array([1.5, 2.5, 3.5, 4.5]), 0.0001); + }); +}); + +describe('addsubmuldiv_gpu MatrixMinusScalar', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(12 * 513); + const result = + addsubmuldiv_gpu.uploadMatrixMinusScalarDownload(a, [12, 513], 0); + expect(result.length).toEqual(a.length); + }); + + it('preserves the matrix when the scalar is 0', () => { + const a = new Float32Array([1, 2, 3]); + const result = + addsubmuldiv_gpu.uploadMatrixMinusScalarDownload(a, [1, 3], 0); + test_util.expectArraysClose(result, a, 0); + }); + + it('subtracts the scalar from every element in the matrix', () => { + const a = new Float32Array([1, 2, 3, 4]); + const result = + addsubmuldiv_gpu.uploadMatrixMinusScalarDownload(a, [2, 2], 0.5); + test_util.expectArraysClose( + result, new Float32Array([0.5, 1.5, 2.5, 3.5]), 0.0001); + }); +}); + +describe('addsubmuldiv_gpu ScalarMinusMatrix', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(12 * 513); + const result = + addsubmuldiv_gpu.uploadScalarMinusMatrixDownload(0, a, [12, 513]); + expect(result.length).toEqual(a.length); + }); + + it('negates the matrix when the scalar is 0', () => { + const a = new Float32Array([1, 2, 3]); + const result = + addsubmuldiv_gpu.uploadScalarMinusMatrixDownload(0, a, [1, 3]); + test_util.expectArraysClose(result, new Float32Array([-1, -2, -3]), 0); + }); + + it('subtracts the matrix value from the scalar for every element', () => { + const a = new Float32Array([1, 2, 3, 4]); + const result = + addsubmuldiv_gpu.uploadScalarMinusMatrixDownload(0.5, a, [2, 2]); + test_util.expectArraysClose( + result, new Float32Array([-0.5, -1.5, -2.5, -3.5]), 0.0001); + }); +}); + +describe('addsubmuldiv_gpu ScalarTimesMatrix', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(12 * 513); + const result = + addsubmuldiv_gpu.uploadScalarTimesMatrixDownload(0, a, [12, 513]); + expect(result.length).toEqual(a.length); + }); + + it('zeros out the matrix when the scalar is 0', () => { + const a = new Float32Array([1, 2, 3]); + const result = + addsubmuldiv_gpu.uploadScalarTimesMatrixDownload(0, a, [1, 3]); + test_util.expectArraysClose(result, new Float32Array([0, 0, 0]), 0); + }); + + it('triples the matrix when the scalar is 3', () => { + const a = new Float32Array([1, 2, 3]); + const result = + addsubmuldiv_gpu.uploadScalarTimesMatrixDownload(3, a, [1, 3]); + test_util.expectArraysClose(result, new Float32Array([3, 6, 9]), 0); + }); +}); + +describe('addsubmuldiv_gpu element-wise matrix product', () => { + function cpuMultiply(a: Float32Array, b: Float32Array): Float32Array { + const result = new Float32Array(a.length); + for (let i = 0; i < result.length; ++i) { + result[i] = a[i] * b[i]; + } + return result; + } + + it('returns a matrix the size of A (and B)', () => { + const a = new Float32Array(1234); + const b = new Float32Array(1234); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [1234 / 2, 2]); + expect(result.length).toEqual(a.length); + }); + + it('sets all result entries to 0 if A is 0', () => { + const a = new Float32Array(257 * 257); + const b = new Float32Array(a.length); + b.fill(1.0); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [257, 257]); + expect(result).toEqual(a); + }); + + it('sets all result entries to 0 if B is 0', () => { + const a = new Float32Array(257 * 257); + const b = new Float32Array(a.length); + a.fill(1.0); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [257, 257]); + expect(result).toEqual(b); + }); + + it('sets all result entries to A if B is [1]', () => { + const a = test_util.randomArrayInRange(16, -10, 10); + const b = new Float32Array(16); + b.fill(1.0); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [4, 4]); + test_util.expectArraysClose(a, result, 0.0001); + }); + + it('sets all result entries to B if A is [1]', () => { + const a = new Float32Array(16); + a.fill(1.0); + const b = test_util.randomArrayInRange(16, -10, 10); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [4, 4]); + test_util.expectArraysClose(b, result, 0.0001); + }); + + it('writes the element-wise product of A and B to result', () => { + const a = test_util.randomArrayInRange(64, -10, 10); + const b = test_util.randomArrayInRange(64, -10, 10); + const result = + addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload(a, b, [8, 8]); + const expected = cpuMultiply(a, b); + test_util.expectArraysClose(result, expected, 0.0001); + }); + + it('writes the element-wise product A * B^T to result', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array([3, 1, 0, 2]); + + const result = addsubmuldiv_gpu.uploadMatrixTimesMatrixDownload( + a, b, [2, 2], MatrixOrientation.REGULAR, MatrixOrientation.TRANSPOSED); + + const bTransposed = new Float32Array([3, 0, 1, 2]); + const expected = cpuMultiply(a, bTransposed); + test_util.expectArraysClose(result, expected, 0.0001); + }); +}); + +describe('addsubmuldiv_gpu element-wise matrix addition', () => { + it('writes the element-wise A + B^T to result', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array([3, 1, 0, 2]); + + const result = addsubmuldiv_gpu.uploadMatrixPlusMatrixDownload( + a, b, [2, 2], MatrixOrientation.REGULAR, MatrixOrientation.TRANSPOSED); + + const expected = new Float32Array([4, 2, 4, 6]); + test_util.expectArraysClose(result, expected, 0.0001); + }); + + it('writes the element-wise A^T + B^T to result', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array([3, 1, 0, 2]); + + const result = addsubmuldiv_gpu.uploadMatrixPlusMatrixDownload( + a, b, [2, 2], MatrixOrientation.TRANSPOSED, + MatrixOrientation.TRANSPOSED); + + const expected = new Float32Array([4, 3, 3, 6]); + test_util.expectArraysClose(result, expected, 0.0001); + }); +}); diff --git a/src/math/webgl/argmaxequals_gpu.ts b/src/math/webgl/argmaxequals_gpu.ts new file mode 100644 index 0000000000..3f5b7e72a7 --- /dev/null +++ b/src/math/webgl/argmaxequals_gpu.ts @@ -0,0 +1,62 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as argminmax_gpu from './argminmax_gpu'; +import {GPGPUContext} from './gpgpu_context'; +import {IS_NAN_SHADER_FUNC} from './webgl_util'; + +function getFragmentShaderPrologueSource(): string { + return ` + precision highp float; + uniform sampler2D matrixA; + uniform sampler2D matrixB; + varying vec2 resultUV;`; +} + +function getFragmentShaderMainSource(): string { + return ` + void main() { + float argMaxA = getArgMinMax(matrixA); + float argMaxB = getArgMinMax(matrixB); + float value; + if (isNaN(argMaxA)) { + value = argMaxA; + } else if (isNaN(argMaxB)) { + value = argMaxB; + } else { + value = float(argMaxA == argMaxB); + } + gl_FragColor = vec4(value, 0, 0, 0); + }`; +} + +export function getArgMaxEqualsFragmentShaderSource( + rows: number, columns: number): string { + return [ + getFragmentShaderPrologueSource(), + argminmax_gpu.getFragmentShaderGetArgMinMaxSource('>', rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} + +export function argMaxEquals( + gpgpu: GPGPUContext, maxEqualsProgram: WebGLProgram, a: WebGLTexture, + b: WebGLTexture, numRows: number, numCols: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(maxEqualsProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/argmaxequals_gpu_test.ts b/src/math/webgl/argmaxequals_gpu_test.ts new file mode 100644 index 0000000000..f74fbccd54 --- /dev/null +++ b/src/math/webgl/argmaxequals_gpu_test.ts @@ -0,0 +1,63 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as argmaxequals_gpu from './argmaxequals_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +function uploadArgMaxEqualsDownload( + a: Float32Array, b: Float32Array, rows: number, columns: number): number { + const src = + argmaxequals_gpu.getArgMaxEqualsFragmentShaderSource(rows, columns); + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram(src); + const aTexture = gpgpu.createMatrixTexture(rows, columns); + const bTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + gpgpu.uploadMatrixToTexture(bTexture, rows, columns, b); + argmaxequals_gpu.argMaxEquals( + gpgpu, program, aTexture, bTexture, rows, columns, resultTexture); + const result = new Float32Array(4); + gpgpu.gl.readPixels(0, 0, 1, 1, gpgpu.gl.RGBA, gpgpu.gl.FLOAT, result); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} + +describe('argmaxequals_gpu ArgMin', () => { + it('one value in each array', () => { + const a = new Float32Array([3]); + const b = new Float32Array([3]); + const equals = uploadArgMaxEqualsDownload(a, b, 1, 1); + expect(equals).toEqual(1); + }); + + it('different argmax values', () => { + const a = new Float32Array([2, 3]); + const b = new Float32Array([3, 2]); + const equals = uploadArgMaxEqualsDownload(a, b, 1, 2); + expect(equals).toEqual(0); + }); + + it('same argmax values', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 4, 3, 2, 1]); + const b = new Float32Array([10, 2, 30, 4, 50, 4, 30, 2, 10]); + const equals = uploadArgMaxEqualsDownload(a, b, 1, 9); + expect(equals).toEqual(1); + }); +}); \ No newline at end of file diff --git a/src/math/webgl/argminmax_gpu.ts b/src/math/webgl/argminmax_gpu.ts new file mode 100644 index 0000000000..2d0874cf0b --- /dev/null +++ b/src/math/webgl/argminmax_gpu.ts @@ -0,0 +1,90 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import {IS_NAN_SHADER_FUNC} from './webgl_util'; + +export function getFragmentShaderPrologueSource(): string { + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 resultUV;`; +} + +function getFragmentShaderMainSource(): string { + return ` + void main() { + gl_FragColor = vec4(getArgMinMax(matrixA), 0, 0, 0); + }`; +} + +function getArgMinMaxFragmentShaderSource( + rows: number, columns: number, compOp: string): string { + return [ + getFragmentShaderPrologueSource(), + getFragmentShaderGetArgMinMaxSource(compOp, rows, columns), + getFragmentShaderMainSource() + ].join('\n'); +} + +export function getArgMinFragmentShaderSource( + rows: number, columns: number): string { + return getArgMinMaxFragmentShaderSource(rows, columns, '<'); +} + +export function getArgMaxFragmentShaderSource( + rows: number, columns: number): string { + return getArgMinMaxFragmentShaderSource(rows, columns, '>'); +} + +export function getFragmentShaderGetArgMinMaxSource( + compOp: string, rows: number, columns: number) { + return ` + const vec2 dimCR = vec2(${columns}.0, ${rows}.0); + const vec2 halfCR = vec2(0.5, 0.5); + + ${IS_NAN_SHADER_FUNC} + + float getArgMinMax(in sampler2D matrix) { + vec2 bestCR = vec2(0, 0); + float bestValue = texture2D(matrix, bestCR).r; + + for (float c = 0.0; c < dimCR.x; c += 1.0) { + for (float r = 0.0; r < dimCR.y; r += 1.0) { + vec2 cr = vec2(c, r); + vec2 uv = (cr + halfCR) / dimCR; + float value = texture2D(matrix, uv).r; + if (isNaN(value)) { + return value; + } + if (value ${compOp} bestValue) { + bestValue = value; + bestCR = cr; + } + } + } + return bestCR.x + (bestCR.y * dimCR.x); + } + `; +} + +export function argMinMax( + gpgpu: GPGPUContext, minMaxProgram: WebGLProgram, a: WebGLTexture, + aNumRows: number, aNumCols: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/argminmax_gpu_test.ts b/src/math/webgl/argminmax_gpu_test.ts new file mode 100644 index 0000000000..077efe1f3a --- /dev/null +++ b/src/math/webgl/argminmax_gpu_test.ts @@ -0,0 +1,119 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as argminmax_gpu from './argminmax_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +function uploadArgMinMaxDownloadDriver( + a: Float32Array, rows: number, columns: number, + fragmentShaderSource: string): number { + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram(fragmentShaderSource); + const aTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + argminmax_gpu.argMinMax( + gpgpu, program, aTexture, rows, columns, resultTexture); + const result = new Float32Array(4); + gpgpu.gl.readPixels(0, 0, 1, 1, gpgpu.gl.RGBA, gpgpu.gl.FLOAT, result); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} + +function uploadArgMinDownload( + a: Float32Array, rows: number, columns: number): number { + const src = argminmax_gpu.getArgMinFragmentShaderSource(rows, columns); + return uploadArgMinMaxDownloadDriver(a, rows, columns, src); +} + +function uploadArgMaxDownload( + a: Float32Array, rows: number, columns: number): number { + const src = argminmax_gpu.getArgMaxFragmentShaderSource(rows, columns); + return uploadArgMinMaxDownloadDriver(a, rows, columns, src); +} + +describe('argminmax_gpu ArgMin', () => { + it('returns the only value in a 1x1 input matrix', () => { + const a = new Float32Array([3]); + const argMin = uploadArgMinDownload(a, 1, 1); + expect(argMin).toEqual(0); + }); + + it('returns min indices when not in first cell', () => { + const a = new Float32Array([0, 100, -12, 0]); // row-major + const argMin = uploadArgMinDownload(a, 2, 2); + expect(argMin).toEqual(2); + }); + + it('finds the min value of a large array', () => { + const a = new Float32Array(1024 * 1024); + test_util.setValue(a, 1024, 1024, -100, 17, 913); + const argMin = uploadArgMinDownload(a, 1024, 1024); + expect(argMin).toEqual((17 * 1024) + 913); + }); + + it('returns the correct column and row when matrix is non-square', () => { + const a = new Float32Array(19 * 254); + test_util.setValue(a, 19, 254, -1, 13, 200); + const argMin = uploadArgMinDownload(a, 19, 254); + expect(argMin).toEqual((13 * 254) + 200); + }); + + it('works when the min element is the bottom/right cell in matrix', () => { + const a = new Float32Array(129 * 129); + test_util.setValue(a, 129, 129, -19, 128, 128); + const argMin = uploadArgMinDownload(a, 129, 129); + expect(argMin).toEqual((128 * 129) + 128); + }); +}); + +describe('argminmax_gpu ArgMax', () => { + it('returns the only value in a 1x1 input matrix', () => { + const a = new Float32Array([3]); + const argMax = uploadArgMaxDownload(a, 1, 1); + expect(argMax).toEqual(0); + }); + + it('returns min indices when not in first cell', () => { + const a = new Float32Array([0, -12, 100, 0]); // row-major + const argMax = uploadArgMaxDownload(a, 2, 2); + expect(argMax).toEqual(2); + }); + + it('finds the max value of a large array', () => { + const a = new Float32Array(1024 * 1024); + test_util.setValue(a, 1024, 1024, 100, 17, 913); + const argMax = uploadArgMaxDownload(a, 1024, 1024); + expect(argMax).toEqual((17 * 1024) + 913); + }); + + it('returns the correct column and row when matrix is non-square', () => { + const a = new Float32Array(19 * 254); + test_util.setValue(a, 19, 254, 109, 13, 200); + const argMax = uploadArgMaxDownload(a, 19, 254); + expect(argMax).toEqual((13 * 254) + 200); + }); + + it('works when the min element is the bottom/right cell in matrix', () => { + const a = new Float32Array(129 * 129); + test_util.setValue(a, 129, 129, 19, 128, 128); + const argMax = uploadArgMaxDownload(a, 129, 129); + expect(argMax).toEqual((128 * 129) + 128); + }); +}); diff --git a/src/math/webgl/avg_pool_gpu.ts b/src/math/webgl/avg_pool_gpu.ts new file mode 100644 index 0000000000..eb59aaf4e9 --- /dev/null +++ b/src/math/webgl/avg_pool_gpu.ts @@ -0,0 +1,30 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as pool_gpu from './pool_gpu'; + +export function getFragmentShaderAvgPoolSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number) { + return pool_gpu.getFragmentShaderPoolCommonSource( + xShapeRCD, fSize, stride, pad, 'avg', false); +} + +export function avgPool( + gpgpu: GPGPUContext, program: WebGLProgram, x: WebGLTexture, + result: WebGLTexture, resultShapeRowCol: [number, number]) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} \ No newline at end of file diff --git a/src/math/webgl/avg_pool_gpu_test.ts b/src/math/webgl/avg_pool_gpu_test.ts new file mode 100644 index 0000000000..d74c98a38e --- /dev/null +++ b/src/math/webgl/avg_pool_gpu_test.ts @@ -0,0 +1,112 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import * as avg_pool_gpu from './avg_pool_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('avg_pool_gpu', () => { + function uploadAvgPoolDownload( + a: Float32Array, aShapeRowColDepth: [number, number, number], + fieldSize: number, stride: number, zeroPad: number): Float32Array { + const aTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + aShapeRowColDepth, fieldSize, aShapeRowColDepth[2], stride, + zeroPad); + + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = avg_pool_gpu.getFragmentShaderAvgPoolSource( + aShapeRowColDepth, fieldSize, stride, zeroPad); + const program = gpgpu.createProgram(shaderSource); + + const aTex = gpgpu.createMatrixTexture(aTexShapeRC[0], aTexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(aTex, aTexShapeRC[0], aTexShapeRC[1], a); + + avg_pool_gpu.avgPool(gpgpu, program, aTex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + function compareToCPU( + xShape: [number, number, number], fSize: number, stride: number, + pad: number) { + const x = NDArray.randNormal(xShape); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = mathCPU.avgPool(x, fSize, stride, pad); + const yGPU = + uploadAvgPoolDownload(x.getValues(), x.shape, fSize, stride, pad); + + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const depth = 1; + const dyShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(dyShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=1,f=3,s=2,p=1', () => { + const depth = 1; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=4,f=2,s=1,p=0', () => { + const depth = 4; + const inputShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=3,f=3,s=3,p=1', () => { + const depth = 3; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); +}); \ No newline at end of file diff --git a/src/math/webgl/batchnorm_gpu.ts b/src/math/webgl/batchnorm_gpu.ts new file mode 100644 index 0000000000..6a93267a97 --- /dev/null +++ b/src/math/webgl/batchnorm_gpu.ts @@ -0,0 +1,131 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + xTexShapeRC: [number, number], meanTexShapeRC: [number, number], + varianceTexShapeRC: [number, number], + offsetTexShapeRC: [number, number]|null, + scaleTexShapeRC?: [number, number]|null, varianceEpsilon = 0.001): string { + let offsetSamplerSnippet = ''; + let offsetShapeInitializationSnippet = ''; + let offsetCoordsSnippet = ''; + let offsetUVSnippet = ''; + let offsetValueSnippet = ''; + let offsetOperationSnippet = '0.0'; + + let scaleSamplerSnippet = ''; + let scaleShapeInitializationSnippet = ''; + let scaleCoordsSnippet = ''; + let scaleUVSnippet = ''; + let scaleValueSnippet = ''; + let scaleOperationSnippet = ''; + + if (offsetTexShapeRC != null) { + offsetSamplerSnippet = 'uniform sampler2D offset;'; + offsetShapeInitializationSnippet = `const vec2 offsetShapeCR = vec2( + ${offsetTexShapeRC[1]}, ${offsetTexShapeRC[0]});`; + offsetCoordsSnippet = 'vec2 offsetCoordsCR = mod(yTexCR, offsetShapeCR);'; + offsetUVSnippet = + 'vec2 offsetUV = (offsetCoordsCR + halfCR) / offsetShapeCR;'; + offsetValueSnippet = 'float offsetValue = texture2D(offset, offsetUV).r;'; + offsetOperationSnippet = 'offsetValue'; + } + + if (scaleTexShapeRC != null) { + scaleSamplerSnippet = 'uniform sampler2D scale;'; + scaleShapeInitializationSnippet = `const vec2 scaleShapeCR = vec2( + ${scaleTexShapeRC[1]}, ${scaleTexShapeRC[0]});`; + scaleCoordsSnippet = 'vec2 scaleCoordsCR = mod(yTexCR, scaleShapeCR);'; + scaleUVSnippet = 'vec2 scaleUV = (scaleCoordsCR + halfCR) / scaleShapeCR;'; + scaleValueSnippet = 'float scaleValue = texture2D(scale, scaleUV).r;'; + scaleOperationSnippet = 'inv *= scaleValue;'; + } + + return ` + precision highp float; + uniform sampler2D x; + uniform sampler2D mean; + uniform sampler2D variance; + ${offsetSamplerSnippet} + ${scaleSamplerSnippet} + + varying vec2 resultUV; + + const vec2 xShapeCR = vec2(${xTexShapeRC[1]}, ${xTexShapeRC[0]}); + const vec2 meanShapeCR = vec2(${meanTexShapeRC[1]}, ${meanTexShapeRC[0]}); + const vec2 varianceShapeCR = vec2( + ${varianceTexShapeRC[1]}, ${varianceTexShapeRC[0]}); + + ${offsetShapeInitializationSnippet} + ${scaleShapeInitializationSnippet} + + const vec2 halfCR = vec2(0.5, 0.5); + const float varianceEpsilon = ${varianceEpsilon}; + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + vec2 meanCoordsCR = mod(yTexCR, meanShapeCR); + vec2 varianceCoordsCR = mod(yTexCR, varianceShapeCR); + ${offsetCoordsSnippet} + ${scaleCoordsSnippet} + + vec2 meanUV = (meanCoordsCR + halfCR) / meanShapeCR; + vec2 varianceUV = (varianceCoordsCR + halfCR) / varianceShapeCR; + ${offsetUVSnippet} + ${scaleUVSnippet} + + float xValue = texture2D(x, resultUV).r; + float meanValue = texture2D(mean, meanUV).r; + float varianceValue = texture2D(variance, varianceUV).r; + ${offsetValueSnippet} + ${scaleValueSnippet} + + float inv = 1.0 / sqrt(varianceValue + varianceEpsilon); + ${scaleOperationSnippet} + float xTimesInv = xValue * inv; + float meanTimesInvWithOffset = ${offsetOperationSnippet} + - meanValue * inv; + + gl_FragColor = vec4(xTimesInv + meanTimesInvWithOffset, 0, 0, 0); + }`; +} + +export function batchNormalization( + gpgpu: GPGPUContext, program: WebGLProgram, x: WebGLTexture, + xShapeRowCol: [number, number], mean: WebGLTexture, + meanShapeRowCol: [number, number], variance: WebGLTexture, + varianceShapeRowCol: [number, number], offset: WebGLTexture|null, + offsetShapeRowCol: [number, number]|null, scale: WebGLTexture|null, + scaleShapeRowCol: [number, number]|null, result: WebGLTexture, + resultShapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.setInputMatrixTexture(mean, 'mean', 1); + gpgpu.setInputMatrixTexture(variance, 'variance', 2); + let nextIndex = 3; + if (offset != null) { + gpgpu.setInputMatrixTexture(offset, 'offset', nextIndex); + nextIndex++; + } + if (scale != null) { + gpgpu.setInputMatrixTexture(scale, 'scale', nextIndex); + } + gpgpu.executeProgram(); +} \ No newline at end of file diff --git a/src/math/webgl/batchnorm_gpu_test.ts b/src/math/webgl/batchnorm_gpu_test.ts new file mode 100644 index 0000000000..6dcbaead0c --- /dev/null +++ b/src/math/webgl/batchnorm_gpu_test.ts @@ -0,0 +1,217 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import * as batchnorm_gpu from './batchnorm_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('batchnorm gpu test', () => { + function uploadBatchNormDownload( + x: Float32Array, xTexShapeRowCol: [number, number], mean: Float32Array, + meanTexShapeRowCol: [number, number], variance: Float32Array, + varianceTexShapeRowCol: [number, number], offset: Float32Array|null, + offsetTexShapeRowCol: [number, number]|null, scale: Float32Array|null, + scaleTexShapeRowCol: [number, number]|null, + varianceEpsilon: number): Float32Array { + const resultTexShapeRC: [number, number] = xTexShapeRowCol; + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = batchnorm_gpu.getFragmentShaderSource( + xTexShapeRowCol, meanTexShapeRowCol, varianceTexShapeRowCol, + offsetTexShapeRowCol, scaleTexShapeRowCol, varianceEpsilon); + + const program = gpgpu.createProgram(shaderSource); + + const xTex = + gpgpu.createMatrixTexture(xTexShapeRowCol[0], xTexShapeRowCol[1]); + const meanTex = + gpgpu.createMatrixTexture(meanTexShapeRowCol[0], meanTexShapeRowCol[1]); + const varianceTex = gpgpu.createMatrixTexture( + varianceTexShapeRowCol[0], varianceTexShapeRowCol[1]); + + let offsetTex = null; + if (offset != null) { + offsetTex = gpgpu.createMatrixTexture( + offsetTexShapeRowCol![0], offsetTexShapeRowCol![1]); + } + let scaleTex = null; + if (scale != null) { + scaleTex = gpgpu.createMatrixTexture( + scaleTexShapeRowCol![0], scaleTexShapeRowCol![1]); + } + + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture( + xTex, xTexShapeRowCol[0], xTexShapeRowCol[1], x); + gpgpu.uploadMatrixToTexture( + meanTex, meanTexShapeRowCol[0], meanTexShapeRowCol[1], mean); + gpgpu.uploadMatrixToTexture( + varianceTex, varianceTexShapeRowCol[0], varianceTexShapeRowCol[1], + variance); + if (offset != null) { + gpgpu.uploadMatrixToTexture( + offsetTex!, offsetTexShapeRowCol![0], offsetTexShapeRowCol![1], + offset); + } + if (scale != null) { + gpgpu.uploadMatrixToTexture( + scaleTex!, scaleTexShapeRowCol![0], scaleTexShapeRowCol![1], scale); + } + + batchnorm_gpu.batchNormalization( + gpgpu, program, xTex, xTexShapeRowCol, meanTex, meanTexShapeRowCol, + varianceTex, varianceTexShapeRowCol, offsetTex, offsetTexShapeRowCol, + scaleTex, scaleTexShapeRowCol, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteMatrixTexture(meanTex); + gpgpu.deleteMatrixTexture(varianceTex); + if (offsetTex != null) { + gpgpu.deleteMatrixTexture(offsetTex); + } + if (scaleTex != null) { + gpgpu.deleteMatrixTexture(scaleTex); + } + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + it('simple batchnorm, no offset or scale, 2x1x2', () => { + const x = new Float32Array([2, 100, 4, 400]); + const mean = new Float32Array([1, 2]); + const variance = new Float32Array([2, 3]); + const varianceEpsilon = .001; + + const result = uploadBatchNormDownload( + x, [2, 2], mean, [1, 2], variance, [1, 2], null, null, null, null, + varianceEpsilon); + + const expectedResult = new Float32Array([ + (x[0] - mean[0]) * 1 / Math.sqrt(variance[0] + varianceEpsilon), + (x[1] - mean[1]) * 1 / Math.sqrt(variance[1] + varianceEpsilon), + (x[2] - mean[0]) * 1 / Math.sqrt(variance[0] + varianceEpsilon), + (x[3] - mean[1]) * 1 / Math.sqrt(variance[1] + varianceEpsilon) + ]); + test_util.expectArraysClose(result, expectedResult, 1e-5); + }); + + it('simple batchnorm, no offset, 2x1x2', () => { + const x = new Float32Array([2, 100, 4, 400]); + const mean = new Float32Array([1, 2]); + const variance = new Float32Array([2, 3]); + const scale = new Float32Array([4, 5]); + const varianceEpsilon = .001; + + const result = uploadBatchNormDownload( + x, [2, 2], mean, [1, 2], variance, [1, 2], null, null, scale, [1, 2], + varianceEpsilon); + + const expectedResult = new Float32Array([ + (x[0] - mean[0]) * scale[0] / Math.sqrt(variance[0] + varianceEpsilon), + (x[1] - mean[1]) * scale[1] / Math.sqrt(variance[1] + varianceEpsilon), + (x[2] - mean[0]) * scale[0] / Math.sqrt(variance[0] + varianceEpsilon), + (x[3] - mean[1]) * scale[1] / Math.sqrt(variance[1] + varianceEpsilon) + ]); + test_util.expectArraysClose(result, expectedResult, 1e-5); + }); + + it('simple batchnorm, no scale, 2x1x2', () => { + const x = new Float32Array([2, 100, 4, 400]); + const mean = new Float32Array([1, 2]); + const variance = new Float32Array([2, 3]); + const offset = new Float32Array([4, 5]); + const varianceEpsilon = .001; + + const result = uploadBatchNormDownload( + x, [2, 2], mean, [1, 2], variance, [1, 2], offset, [1, 2], null, null, + varianceEpsilon); + + const expectedResult = new Float32Array([ + offset[0] + + (x[0] - mean[0]) * 1 / Math.sqrt(variance[0] + varianceEpsilon), + offset[1] + + (x[1] - mean[1]) * 1 / Math.sqrt(variance[1] + varianceEpsilon), + offset[0] + + (x[2] - mean[0]) * 1 / Math.sqrt(variance[0] + varianceEpsilon), + offset[1] + + (x[3] - mean[1]) * 1 / Math.sqrt(variance[1] + varianceEpsilon) + ]); + test_util.expectArraysClose(result, expectedResult, 1e-5); + }); + + it('simple batchnorm, 2x1x2', () => { + const x = new Float32Array([2, 100, 4, 400]); + const mean = new Float32Array([1, 2]); + const variance = new Float32Array([2, 3]); + const offset = new Float32Array([3, 4]); + const scale = new Float32Array([4, 5]); + const varianceEpsilon = .001; + + const result = uploadBatchNormDownload( + x, [2, 2], mean, [1, 2], variance, [1, 2], offset, [1, 2], scale, + [1, 2], varianceEpsilon); + + const expectedResult = new Float32Array([ + offset[0] + + (x[0] - mean[0]) * scale[0] / + Math.sqrt(variance[0] + varianceEpsilon), + offset[1] + + (x[1] - mean[1]) * scale[1] / + Math.sqrt(variance[1] + varianceEpsilon), + offset[0] + + (x[2] - mean[0]) * scale[0] / + Math.sqrt(variance[0] + varianceEpsilon), + offset[1] + + (x[3] - mean[1]) * scale[1] / Math.sqrt(variance[1] + varianceEpsilon) + ]); + test_util.expectArraysClose(result, expectedResult, 1e-5); + }); + + it('batchnorm matches tensorflow, 2x3x3', () => { + const x = new Float32Array([ + 0.49955603, 0.04158615, -1.09440524, 2.03854165, -0.61578344, 2.87533573, + 1.18105987, 0.807462, 1.87888837, 2.26563962, -0.37040935, 1.35848753, + -0.75347094, 0.15683117, 0.91925946, 0.34121279, 0.92717143, 1.89683965 + ]); + const mean = new Float32Array([0.39745062, -0.48062894, 0.4847822]); + const variance = new Float32Array([0.32375343, 0.67117643, 1.08334653]); + const offset = new Float32Array([0.69398749, -1.29056387, 0.9429723]); + const scale = new Float32Array([-0.5607271, 0.9878457, 0.25181573]); + const varianceEpsilon = .001; + + const result = uploadBatchNormDownload( + x, [2, 9], mean, [1, 3], variance, [1, 3], offset, [1, 3], scale, + [1, 3], varianceEpsilon); + + const expectedResult = new Float32Array([ + 0.59352049, -0.66135202, 0.5610874, -0.92077015, -1.45341019, 1.52106473, + -0.07704776, 0.26144429, 1.28010017, -1.14422404, -1.15776136, 1.15425493, + 1.82644104, -0.52249442, 1.04803919, 0.74932291, 0.40568101, 1.2844412 + ]); + test_util.expectArraysClose(result, expectedResult, 1e-5); + }); +}); \ No newline at end of file diff --git a/src/math/webgl/binaryop_gpu.ts b/src/math/webgl/binaryop_gpu.ts new file mode 100644 index 0000000000..4a793d9342 --- /dev/null +++ b/src/math/webgl/binaryop_gpu.ts @@ -0,0 +1,78 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + aResultUV: string, bResultUV: string, op: string): string { + return ` + precision highp float; + uniform sampler2D matrixA; + uniform sampler2D matrixB; + varying vec2 resultUV; + + void main() { + float a = texture2D(matrixA, ${aResultUV}).r; + float b = texture2D(matrixB, ${bResultUV}).r; + ${op} + }`; +} + +export function binaryOp( + gpgpu: GPGPUContext, program: WebGLProgram, a: WebGLTexture, + aShapeRowCol: [number, number], b: WebGLTexture, + bShapeRowCol: [number, number], result: WebGLTexture, + resultShapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} + +export function uploadBinaryOpDownload( + a: Float32Array, aShape: [number, number], b: Float32Array, + bShape: [number, number], fragmentShaderSource: string): Float32Array { + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram(fragmentShaderSource); + + const aTexture: WebGLTexture = + gpgpu.createMatrixTexture(aShape[0], aShape[1]); + const bTexture: WebGLTexture = + gpgpu.createMatrixTexture(bShape[0], bShape[1]); + + const resultShape: [number, number] = + [Math.max(aShape[0], bShape[0]), Math.max(aShape[1], bShape[1])]; + + const resultTexture: WebGLTexture = + gpgpu.createMatrixTexture(resultShape[0], resultShape[1]); + + gpgpu.uploadMatrixToTexture(aTexture, aShape[0], aShape[1], a); + gpgpu.uploadMatrixToTexture(bTexture, bShape[0], bShape[1], b); + + binaryOp( + gpgpu, program, aTexture, aShape, bTexture, bShape, resultTexture, + resultShape); + const result = gpgpu.downloadMatrixFromTexture( + resultTexture, resultShape[0], resultShape[1]); + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} diff --git a/src/math/webgl/concat3d_gpu.ts b/src/math/webgl/concat3d_gpu.ts new file mode 100644 index 0000000000..ebe37d7ab3 --- /dev/null +++ b/src/math/webgl/concat3d_gpu.ts @@ -0,0 +1,74 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + x1ShapeRCD: [number, number, number], x2ShapeRCD: [number, number, number], + resultShapeRCD: [number, number, number], axis: number): string { + const x1TexShapeRC = conv_util.computeTexShapeFrom3D(x1ShapeRCD); + const x2TexShapeRC = conv_util.computeTexShapeFrom3D(x2ShapeRCD); + + const yAxes = ['yR', 'yC', 'yD']; + const concatAxis = yAxes[axis]; + + return ` + precision highp float; + uniform sampler2D x1; + uniform sampler2D x2; + + const vec2 x1ShapeCR = vec2(${x1TexShapeRC[1]}, ${x1TexShapeRC[0]}); + const vec2 x2ShapeCR = vec2(${x2TexShapeRC[1]}.0, ${x2TexShapeRC[0]}.0); + + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (yTexR, yTexC) to 3D (yR, yC, yD). + float yR = yTexCR.y; + float yC = floor(yTexCR.x / ${resultShapeRCD[2]}.0); + float yD = mod(yTexCR.x, ${resultShapeRCD[2]}.0); + + float value = 0.0; + + if (${concatAxis} < ${x1ShapeRCD[axis]}.0) { + // Map yR, yC, yD back to x1 coordinates. + vec2 x1CR = vec2(yC * ${x1ShapeRCD[2]}.0 + yD, yR); + vec2 x1UV = (x1CR + halfCR) / x1ShapeCR; + value = texture2D(x1, x1UV).r; + } else { + ${concatAxis} = ${concatAxis} - ${x1ShapeRCD[axis]}.0; + + // Map yR, yC, yD back to x2 coordinates. + vec2 x2CR = vec2(yC * ${x2ShapeRCD[2]}.0 + yD, yR); + vec2 x2UV = (x2CR + halfCR) / x2ShapeCR; + value = texture2D(x2, x2UV).r; + } + + gl_FragColor = vec4(value, 0.0, 0.0, 0.0); + }`; +} + +export function concat3D( + gpgpu: GPGPUContext, program: WebGLProgram, x1: WebGLTexture, + x2: WebGLTexture, result: WebGLTexture, resultShapeRC: [number, number]) { + gpgpu.setOutputMatrixTexture(result, resultShapeRC[0], resultShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x1, 'x1', 0); + gpgpu.setInputMatrixTexture(x2, 'x2', 1); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/concat3d_gpu_test.ts b/src/math/webgl/concat3d_gpu_test.ts new file mode 100644 index 0000000000..00ee4b14d0 --- /dev/null +++ b/src/math/webgl/concat3d_gpu_test.ts @@ -0,0 +1,105 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; + +import * as concat3d_gpu from './concat3d_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('concat3d_gpu', () => { + + function uploadConcat3dDownload( + x1: Float32Array, x2: Float32Array, x1ShapeRCD: [number, number, number], + x2ShapeRCD: [number, number, number], axis: number): Float32Array { + const x1TexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(x1ShapeRCD); + const x2TexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(x2ShapeRCD); + + const resultShapeRCD = x1ShapeRCD.slice() as [number, number, number]; + resultShapeRCD[axis] += x2ShapeRCD[axis]; + const resultTexShapeRC = conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = concat3d_gpu.getFragmentShaderSource( + x1ShapeRCD, x2ShapeRCD, resultShapeRCD, axis); + const program = gpgpu.createProgram(shaderSource); + + const x1Tex = gpgpu.createMatrixTexture(x1TexShapeRC[0], x1TexShapeRC[1]); + const x2Tex = gpgpu.createMatrixTexture(x2TexShapeRC[0], x2TexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(x1Tex, x1TexShapeRC[0], x1TexShapeRC[1], x1); + gpgpu.uploadMatrixToTexture(x2Tex, x2TexShapeRC[0], x2TexShapeRC[1], x2); + + concat3d_gpu.concat3D( + gpgpu, program, x1Tex, x2Tex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(x1Tex); + gpgpu.deleteMatrixTexture(x2Tex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + it('concat axis=0', () => { + const x1 = new Float32Array([1, 11, 111, 2, 22, 222]); + const x2 = + new Float32Array([5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + + const result = uploadConcat3dDownload(x1, x2, [1, 2, 3], [2, 2, 3], 0); + test_util.expectArraysClose( + result, new Float32Array([ + 1, 11, 111, 2, 22, 222, 5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888 + ]), + 1e-6); + + }); + + it('concat axis=1', () => { + const x1 = new Float32Array([1, 11, 111, 3, 33, 333]); + const x2 = + new Float32Array([5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + + const result = uploadConcat3dDownload(x1, x2, [2, 1, 3], [2, 2, 3], 1); + test_util.expectArraysClose( + result, new Float32Array([ + 1, 11, 111, 5, 55, 555, 6, 66, 666, 3, 33, 333, 7, 77, 777, 8, 88, 888 + ]), + 1e-6); + }); + + it('concat axis=2', () => { + const x1 = new Float32Array([1, 11, 2, 22, 3, 33, 4, 44]); + const x2 = + new Float32Array([5, 55, 555, 6, 66, 666, 7, 77, 777, 8, 88, 888]); + + const result = uploadConcat3dDownload(x1, x2, [2, 2, 2], [2, 2, 3], 2); + test_util.expectArraysClose( + result, new Float32Array([ + 1, 11, 5, 55, 555, 2, 22, 6, 66, 666, + 3, 33, 7, 77, 777, 4, 44, 8, 88, 888 + ]), + 1e-6); + }); +}); diff --git a/src/math/webgl/conv_backprop_gpu.ts b/src/math/webgl/conv_backprop_gpu.ts new file mode 100644 index 0000000000..15aca3485e --- /dev/null +++ b/src/math/webgl/conv_backprop_gpu.ts @@ -0,0 +1,252 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; + +import * as conv_gpu from './conv_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderDerWeightsSource( + xShapeRowColDepth: [number, number, number], fSize: number, + outputDepth: number, stride: number, zeroPad: number) { + const getMatrixValueOrZeroPad = + conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + const inputDepth = xShapeRowColDepth[2]; + + const xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + + const yShape = conv_util.computeOutputShape3D( + xShapeRowColDepth, fSize, outputDepth, stride, zeroPad); + const yNumRows = yShape[0]; + const yNumCols = yShape[1]; + const yTexShapeRC = conv_util.computeTexShapeFrom3D(yShape); + + const fSizeTimesInputDepth = fSize * inputDepth; + + const prologue = ` + precision highp float; + uniform sampler2D x; + uniform sampler2D dy; + `; + + return prologue + '\n' + getMatrixValueOrZeroPad + '\n' + + ` + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 xShapeCR = vec2(${xTexShapeRC[1]}, ${xTexShapeRC[0]}); + const vec2 dyShapeCR = vec2(${yTexShapeRC[1]}, ${yTexShapeRC[0]}); + + void main() { + vec2 wTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (wTexR, wTexC) to 4D (wR, wC, d1, d2). + float wR = floor(wTexCR.y / ${fSizeTimesInputDepth}.0); + float wTexRLeftover = wTexCR.y - wR * ${fSizeTimesInputDepth}.0; + float wC = floor(wTexRLeftover / ${inputDepth}.0); + float d1 = mod(wTexRLeftover, ${inputDepth}.0); + float d2 = wTexCR.x; + + // Convolve x(?, ?, d1) with dy(:, :, d2) to get dw(wR, wC, d1, d2). + // ? = to be determined. : = across all values in that axis. + float dotProd = 0.0; + for (float yR = 0.0; yR < ${yNumRows}.0; yR += 1.0) { + float xR = wR + yR * ${stride}.0 - ${zeroPad}.0; + float xTexR = xR; + float yTexR = yR; + for (float yC = 0.0; yC < ${yNumCols}.0; yC += 1.0) { + float xC = wC + yC * ${stride}.0 - ${zeroPad}.0; + + // Map from 3D (xR, xC, d1) to 2D (xTexR, xTexC). + // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC). + vec2 xyTexC = vec2(xC, yC) * vec2(${inputDepth}.0, ${outputDepth}.0) + + vec2(d1, d2); + float xTexC = xyTexC.x; + float yTexC = xyTexC.y; + + // Read dy(yR, yC, d2). + vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR; + float dyValue = texture2D(dy, dyUV).r; + + // Read x(xR, xC, d1) (potentially zero-padded). + float xValue = + getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR)); + + dotProd += (xValue * dyValue); + } + } + gl_FragColor = vec4(dotProd, 0, 0, 0); + }`; +} + +export function getFragmentShaderConvTransposeSource( + xShapeRCD: [number, number, number], fSize: number, origInputDepth: number, + origStride: number, origPad: number, hasBias: boolean) { + const pad = fSize - 1 - origPad; + const [xRows, xCols, origOutputDepth] = xShapeRCD; + + const xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + const wTexShapeRC = + conv_util.computeWeightsTexShape(origInputDepth, origOutputDepth, fSize); + + const getBiasValue = hasBias ? + conv_gpu.getFragmentShaderGetBiasValueSource(origInputDepth) : + ''; + const biasPrologue = hasBias ? 'uniform sampler2D biases;' : ''; + const biasOperation = hasBias ? 'dotProd += getBiasValue(biases, d2);' : ''; + + const prologue = ` + precision highp float; + uniform sampler2D x; + uniform sampler2D weights; + ${biasPrologue} + `; + + return prologue + '\n' + getBiasValue + '\n' + + ` + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 xShapeCR = vec2(${xTexShapeRC[1]}, ${xTexShapeRC[0]}); + const vec2 wShapeCR = vec2(${wTexShapeRC[1]}, ${wTexShapeRC[0]}); + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2). + float yR = yTexCR.y; + float yC = floor(yTexCR.x / ${origInputDepth}.0); + float d2 = mod(yTexCR.x, ${origInputDepth}.0); + + vec2 xRCCorner = vec2(yR, yC) - vec2(${pad}.0, ${pad}.0); + float xRCorner = xRCCorner.x; + float xCCorner = xRCCorner.y; + + // Convolve x(?, ?, d1) with w(:, :, d2, d1) to get y(yR, yC, d2). + // ? = to be determined. : = across all values in that axis. + float dotProd = 0.0; + for (float wR = 0.0; wR < ${fSize}.0; wR += 1.0) { + + float xR = (xRCorner + wR) / ${origStride}.0; + // TODO(smilkov): Splice this with another version where you call + // getMatrixValueOrZeroPad(). Here and below. + if (xR < 0.0 || xR >= ${xRows}.0 || fract(xR) > 0.0) { + continue; + } + + float wRPerm = ${fSize}.0 - 1.0 - wR; + float xTexR = xR; + + for (float wC = 0.0; wC < ${fSize}.0; wC += 1.0) { + + float xC = (xCCorner + wC) / ${origStride}.0; + if (xC < 0.0 || xC >= ${xCols}.0 || fract(xC) > 0.0) { + continue; + } + + float wCPerm = ${fSize}.0 - 1.0 - wC; + float wTexR = wRPerm * ${fSize}.0 * ${origInputDepth}.0 + + wCPerm * ${origInputDepth}.0 + d2; + + for (float d1 = 0.0; d1 < ${origOutputDepth}.0; d1 += 1.0) { + float xTexC = xC * ${origOutputDepth}.0 + d1; + float wTexC = d1; + + // Read x(xR, xC, d1). + vec2 xUV = (vec2(xTexC, xTexR) + halfCR) / xShapeCR; + float xValue = texture2D(x, xUV).r; + + // Read w(wRPerm, wCPerm, d2, d1). + vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR; + float wValue = texture2D(weights, wUV).r; + + dotProd += xValue * wValue; + } + } + } + ${biasOperation} + gl_FragColor = vec4(dotProd, 0, 0, 0); + }`; +} + +export function getFragmentShaderDerBiasSource( + dyShapeRCD: [number, number, number]) { + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + const [yNumRows, yNumCols, outputDepth] = dyShapeRCD; + + return ` + precision highp float; + uniform sampler2D dy; + + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 dyShapeCR = vec2(${dyTexShapeRC[1]}, ${dyTexShapeRC[0]}); + + void main() { + vec2 biasTexCR = floor(gl_FragCoord.xy); + + // The bias texture RC shape is [1, d2]. + float d2 = biasTexCR.x; + + float derBias = 0.0; + for (float yR = 0.0; yR < ${yNumRows}.0; yR += 1.0) { + float yTexR = yR; + + for (float yC = 0.0; yC < ${yNumCols}.0; yC += 1.0) { + // Map from 3D (yR, yC, d2) to 2D (yTexR, yTexC). + float yTexC = yC * ${outputDepth}.0 + d2; + + // Read dy(yR, yC, d2). + vec2 dyUV = (vec2(yTexC, yTexR) + halfCR) / dyShapeCR; + float dyValue = texture2D(dy, dyUV).r; + + derBias += dyValue; + } + } + gl_FragColor = vec4(derBias, 0, 0, 0); + }`; +} + +export function derBias( + gpgpu: GPGPUContext, program: WebGLProgram, dyTex: WebGLTexture, + result: WebGLTexture, resultTexShapeRC: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.executeProgram(); +} + +export function derWeights( + gpgpu: GPGPUContext, program: WebGLProgram, xTex: WebGLTexture, + dyTex: WebGLTexture, result: WebGLTexture, + resultTexShapeRC: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 1); + gpgpu.executeProgram(); +} + +export function convTranspose( + gpgpu: GPGPUContext, program: WebGLProgram, xTex: WebGLTexture, + weightsTex: WebGLTexture, biasesTex: WebGLTexture|null, + resultTex: WebGLTexture, resultTexShapeRC: [number, number]) { + gpgpu.setOutputMatrixTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(xTex, 'x', 0); + gpgpu.setInputMatrixTexture(weightsTex, 'weights', 1); + if (biasesTex != null) { + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + } + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/conv_backprop_gpu_derbias_test.ts b/src/math/webgl/conv_backprop_gpu_derbias_test.ts new file mode 100644 index 0000000000..efd9c0cb92 --- /dev/null +++ b/src/math/webgl/conv_backprop_gpu_derbias_test.ts @@ -0,0 +1,74 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import * as conv_backprop_gpu from './conv_backprop_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu derBias', () => { + + function uploadDerBiasDownload(dy: Array3D): Float32Array { + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + const src = conv_backprop_gpu.getFragmentShaderDerBiasSource(dy.shape); + const program = gpgpu.createProgram(src); + + // Upload dy. + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dy.shape); + const dyTex = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + dyTex, dyTexShapeRC[0], dyTexShapeRC[1], dy.getValues()); + + const outputDepth = dy.shape[2]; + const resultTexRC = conv_util.computeBiasesTexShape(outputDepth); + const resultTex = gpgpu.createMatrixTexture(resultTexRC[0], resultTexRC[1]); + conv_backprop_gpu.derBias(gpgpu, program, dyTex, resultTex, resultTexRC); + const db = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexRC[0], resultTexRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(dyTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return db; + } + + function compareToCPU(dyShapeRCD: [number, number, number]) { + const dy = NDArray.randNormal(dyShapeRCD); + + const mathCPU = new NDArrayMathCPU(); + const dBiasCPU = mathCPU.conv2dDerBias(dy); + + const dBiasGPU = uploadDerBiasDownload(dy); + test_util.expectArraysClose(dBiasGPU, dBiasCPU.getValues(), 1e-5); + } + + it('matches CPU on random input. dy shape [3, 3, 2]', () => { + compareToCPU([3, 3, 2]); + }); + + it('matches CPU on random input. dy shape [5, 5, 1]', () => { + compareToCPU([5, 5, 1]); + }); + + it('matches CPU on random input. dy shape [1, 1, 8]', () => { + compareToCPU([1, 1, 8]); + }); +}); diff --git a/src/math/webgl/conv_backprop_gpu_derweights_test.ts b/src/math/webgl/conv_backprop_gpu_derweights_test.ts new file mode 100644 index 0000000000..03129d77ac --- /dev/null +++ b/src/math/webgl/conv_backprop_gpu_derweights_test.ts @@ -0,0 +1,120 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import * as conv_backprop_gpu from './conv_backprop_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu derWeights', () => { + + function uploadDerWeightsDownload( + x: Array3D, dy: Array3D, fSize: number, stride: number, + zeroPad: number): Float32Array { + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + const outputDepth = dy.shape[2]; + const src = conv_backprop_gpu.getFragmentShaderDerWeightsSource( + x.shape, fSize, outputDepth, stride, zeroPad); + const program = gpgpu.createProgram(src); + const inputDepth = x.shape[2]; + + // Upload x. + const xTexShapeRC = conv_util.computeTexShapeFrom3D(x.shape); + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + xTex, xTexShapeRC[0], xTexShapeRC[1], x.getValues()); + + // Upload dy. + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dy.shape); + const dyTex = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + dyTex, dyTexShapeRC[0], dyTexShapeRC[1], dy.getValues()); + + const resultTexRC = + conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + const resultTex = gpgpu.createMatrixTexture(resultTexRC[0], resultTexRC[1]); + conv_backprop_gpu.derWeights( + gpgpu, program, xTex, dyTex, resultTex, resultTexRC); + const dw = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexRC[0], resultTexRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteMatrixTexture(dyTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return dw; + } + + function compareToCPU( + inputShape: [number, number, number], fSize: number, outputDepth: number, + stride: number, zeroPad: number) { + const x = NDArray.randNormal(inputShape); + const outputShape = conv_util.computeOutputShape3D( + x.shape, fSize, outputDepth, stride, zeroPad); + const dy = NDArray.randNormal(outputShape); + + const mathCPU = new NDArrayMathCPU(); + const dwCPU = mathCPU.conv2dDerWeights(x, dy, fSize, stride, zeroPad); + + const dwGPU = uploadDerWeightsDownload(x, dy, fSize, stride, zeroPad); + test_util.expectArraysClose(dwGPU, dwCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=3,d2=4,f=2,s=1,p=0', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 2; + const outputDepth = 4; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=3,d2=4,f=3,s=1,p=1', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 3; + const outputDepth = 4; + const stride = 1; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=3,d2=4,f=3,s=2,p=1', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 4; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=3,d2=4,f=3,s=3,p=1', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 4; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); +}); diff --git a/src/math/webgl/conv_backprop_transpose_gpu_test.ts b/src/math/webgl/conv_backprop_transpose_gpu_test.ts new file mode 100644 index 0000000000..9cf4ba3c9a --- /dev/null +++ b/src/math/webgl/conv_backprop_transpose_gpu_test.ts @@ -0,0 +1,144 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array1D, Array3D, Array4D, NDArray} from '../ndarray'; + +import * as conv_backprop_gpu from './conv_backprop_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu transpose', () => { + + function uploadConvTransposeDownload( + x: Array3D, weights: Array4D, biases: Array1D|null, fSize: number, + origStride: number, origPad: number): Float32Array { + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + const origInputDepth = weights.shape[2]; + const origOutputDepth = weights.shape[3]; + const src = conv_backprop_gpu.getFragmentShaderConvTransposeSource( + x.shape, fSize, origInputDepth, origStride, origPad, biases != null); + const program = gpgpu.createProgram(src); + + // Upload x. + const xTexShapeRC = conv_util.computeTexShapeFrom3D(x.shape); + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + xTex, xTexShapeRC[0], xTexShapeRC[1], x.getValues()); + + // Upload weights. + const wTexShapeRC = conv_util.computeWeightsTexShape( + origInputDepth, origOutputDepth, fSize); + const wTex = gpgpu.createMatrixTexture(wTexShapeRC[0], wTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + wTex, wTexShapeRC[0], wTexShapeRC[1], weights.getValues()); + + const biasTexShapeRC = conv_util.computeBiasesTexShape(origInputDepth); + const biasTex = biases != null ? + gpgpu.createMatrixTexture(biasTexShapeRC[0], biasTexShapeRC[1]) : + null; + if (biasTex != null) { + gpgpu.uploadMatrixToTexture( + biasTex, biasTexShapeRC[0], biasTexShapeRC[1], biases!.getValues()); + } + + // Figure out the output shape by dilating the input. + const xRowsDilated = (x.shape[0] - 1) * origStride + 1; + const xColsDilated = (x.shape[1] - 1) * origStride + 1; + const pad = fSize - 1 - origPad; + const resultShapeRCD = conv_util.computeOutputShape3D( + [xRowsDilated, xColsDilated, origOutputDepth], fSize, origInputDepth, 1, + pad); + const resultTexRC = conv_util.computeTexShapeFrom3D(resultShapeRCD); + const resultTex = gpgpu.createMatrixTexture(resultTexRC[0], resultTexRC[1]); + conv_backprop_gpu.convTranspose( + gpgpu, program, xTex, wTex, biasTex, resultTex, resultTexRC); + const y = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexRC[0], resultTexRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteMatrixTexture(wTex); + if (biasTex != null) { + gpgpu.deleteMatrixTexture(biasTex); + } + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return y; + } + + function compareToCPU( + origInputShape: [number, number, number], fSize: number, + origOutputDepth: number, origStride: number, origPad: number) { + const [xNumRows, xNumCols, origInputDepth] = origInputShape; + + const x = + NDArray.randNormal([xNumRows, xNumCols, origOutputDepth]); + + const weights = NDArray.randNormal( + [fSize, fSize, origInputDepth, origOutputDepth]); + const biases = NDArray.randNormal([origInputDepth]); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = + mathCPU.conv2dTranspose(x, weights, biases, origStride, origPad); + const yGPU = uploadConvTransposeDownload( + x, weights, biases, fSize, origStride, origPad); + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 2; + const outputDepth = 1; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=1,d2=1,f=3,s=2,p=1', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 1; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=4,d2=3,f=2,s=1,p=0', () => { + const inputDepth = 4; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 2; + const outputDepth = 3; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=3,d2=4,f=3,s=3,p=1', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 4; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); +}); diff --git a/src/math/webgl/conv_gpu.ts b/src/math/webgl/conv_gpu.ts new file mode 100644 index 0000000000..21f9685041 --- /dev/null +++ b/src/math/webgl/conv_gpu.ts @@ -0,0 +1,151 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderPrologueSource(): string { + return ` + precision highp float; + uniform sampler2D x; + uniform sampler2D weights; + uniform sampler2D biases; + varying vec2 resultUV;`; +} + +export function getFragmentShaderGetMatrixValueOrZeroPadSource(): string { + return ` + float getMatrixValueOrZeroPad(in sampler2D matrix, vec2 matrixShapeCR, + vec2 requestedCR) { + vec2 uv = (requestedCR + vec2(0.5, 0.5)) / matrixShapeCR; + float value = texture2D(matrix, uv).r; + bool lessThanZero = any(lessThan(uv, vec2(0, 0))); + bool greaterThanOne = any(greaterThan(uv, vec2(1, 1))); + bool outside = lessThanZero || greaterThanOne; + return mix(value, 0.0, float(outside)); + }`; +} + +export function getFragmentShaderConvolveSource( + xShapeRCD: [number, number, number], fSize: number, outputDepth: number, + stride: number, pad: number, hasBias: boolean) { + const [xRows, xCols, inputDepth] = xShapeRCD; + + const xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + const wTexShapeRC = + conv_util.computeWeightsTexShape(inputDepth, outputDepth, fSize); + + return ` + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 xShapeCR = vec2(${xTexShapeRC[1]}, ${xTexShapeRC[0]}); + const vec2 wShapeCR = vec2(${wTexShapeRC[1]}, ${wTexShapeRC[0]}); + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2). + float yR = yTexCR.y; + float yC = floor(yTexCR.x / ${outputDepth}.0); + float d2 = mod(yTexCR.x, ${outputDepth}.0); + float wTexC = d2; + + vec2 xRCCorner = vec2(yR, yC) * vec2(${stride}, ${stride}) - + vec2(${pad}.0, ${pad}.0); + float xRCorner = xRCCorner.x; + float xCCorner = xRCCorner.y; + + // Convolve x(?, ?, d1) with w(:, :, d1, d2) to get y(yR, yC, d2). + // ? = to be determined. : = across all values in that axis. + float dotProd = 0.0; + for (float wR = 0.0; wR < ${fSize}.0; wR += 1.0) { + float xR = xRCorner + wR; + float xTexR = xR; + + for (float wC = 0.0; wC < ${fSize}.0; wC += 1.0) { + float xC = xCCorner + wC; + + for (float d1 = 0.0; d1 < ${inputDepth}.0; d1 += 1.0) { + float xTexC = xC * ${inputDepth}.0 + d1; + float wTexR = wR * ${fSize * inputDepth}.0 + + wC * ${inputDepth}.0 + d1; + + float xValue = + getMatrixValueOrZeroPad(x, xShapeCR, vec2(xTexC, xTexR)); + + // Read w(wR, wC, d1, d2). + vec2 wUV = (vec2(wTexC, wTexR) + halfCR) / wShapeCR; + float wValue = texture2D(weights, wUV).r; + + dotProd += xValue * wValue; + } + } + } + if (${hasBias}) { + dotProd += getBiasValue(biases, d2); + } + gl_FragColor = vec4(dotProd, 0, 0, 0); + }`; +} + +export function getFragmentShaderGetBiasValueSource(outputDepth: number): + string { + return ` + float getBiasValue(in sampler2D bias, float biasC) { + const vec2 biasShapeCR = vec2(${outputDepth}, 1); + vec2 biasCR = vec2(mod(biasC, ${outputDepth}.0), 0); + vec2 biasUV = (biasCR + vec2(0.5, 0.5)) / biasShapeCR; + return texture2D(bias, biasUV).r; + }`; +} + +export function getFragmentShaderSource( + aShapeRowColDepth: [number, number, number], resultDepth: number, + fieldSize: number, stride: number, zeroPad: number, + hasBias: boolean): string { + const aShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const weightShapeRC: [number, number] = conv_util.computeWeightsTexShape( + aShapeRowColDepth[2], resultDepth, fieldSize); + + const prologue = getFragmentShaderPrologueSource(); + const getMatrixValueOrZeroPad = + getFragmentShaderGetMatrixValueOrZeroPadSource(); + const convolve = getFragmentShaderConvolveSource( + aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad, hasBias); + const getBiasValue = getFragmentShaderGetBiasValueSource(resultDepth); + + return [ + prologue, + getMatrixValueOrZeroPad, + getBiasValue, + convolve, + ].join('\n'); +} + +export function convolve( + gpgpu: GPGPUContext, program: WebGLProgram, a: WebGLTexture, + weights: WebGLTexture, biases: WebGLTexture|null, result: WebGLTexture, + resultShapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(a, 'x', 0); + gpgpu.setInputMatrixTexture(weights, 'weights', 1); + if (biases != null) { + gpgpu.setInputMatrixTexture(biases, 'biases', 2); + } + gpgpu.executeProgram(); +} \ No newline at end of file diff --git a/src/math/webgl/conv_gpu_getbiasvalue_test.ts b/src/math/webgl/conv_gpu_getbiasvalue_test.ts new file mode 100644 index 0000000000..62046c36c7 --- /dev/null +++ b/src/math/webgl/conv_gpu_getbiasvalue_test.ts @@ -0,0 +1,85 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_gpu from './conv_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu getBiasValue', () => { + function createGetBiasValueProgram( + gpgpu: GPGPUContext, outputDepth: number): WebGLProgram { + const prologue = conv_gpu.getFragmentShaderPrologueSource(); + const uniforms = 'uniform float biasC;'; + const getBiasValue = + conv_gpu.getFragmentShaderGetBiasValueSource(outputDepth); + const main = ` + void main() { + gl_FragColor = vec4(getBiasValue(biases, biasC), 0, 0, 0); + }`; + + const src = [prologue, uniforms, getBiasValue, main].join('\n'); + return gpgpu.createProgram(src); + } + + function uploadGetBiasValueDownload( + biases: Float32Array, biasCol: number, outputDepth: number): number { + const gpgpu = new GPGPUContext(); + const program = createGetBiasValueProgram(gpgpu, outputDepth); + const biasesTex = gpgpu.createMatrixTexture(1, outputDepth); + const resultTex = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(biasesTex, 1, outputDepth, biases); + gpgpu.setOutputMatrixTexture(resultTex, 1, 1); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(biasesTex, 'biases', 2); + gpgpu.gl.uniform1f(gpgpu.getUniformLocation('biasC'), biasCol); + gpgpu.executeProgram(); + const result = gpgpu.downloadMatrixFromTexture(resultTex, 1, 1)[0]; + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(biasesTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + it('returns the only bias value if output depth is 1', () => { + const biases = new Float32Array([4]); + const result = uploadGetBiasValueDownload(biases, 0, 1); + expect(result).toEqual(4); + }); + + it('returns the requested column if < output depth', () => { + const biases = new Float32Array([1, 2, 3, 4, 5]); + const result = + uploadGetBiasValueDownload(biases, biases.length - 1, biases.length); + expect(result).toEqual(5); + }); + + it('wraps around to column 0 if column == output depth', () => { + const biases = new Float32Array([6, 0, 0]); + const result = uploadGetBiasValueDownload(biases, 3, 3); + expect(result).toEqual(6); + }); + + it('wraps around twice if column == 2*output depth', () => { + const biases = new Float32Array([7, 0, 0]); + const result = uploadGetBiasValueDownload(biases, 6, 3); + expect(result).toEqual(7); + }); + + it('selects value from column mod(biasC, outputDepth)', () => { + const biases = new Float32Array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]); + const result = uploadGetBiasValueDownload(biases, 2017, biases.length); + expect(result).toEqual(biases[2017 % biases.length]); + }); +}); diff --git a/src/math/webgl/conv_gpu_getmatrixvalueorzeropad_test.ts b/src/math/webgl/conv_gpu_getmatrixvalueorzeropad_test.ts new file mode 100644 index 0000000000..48d8c3d687 --- /dev/null +++ b/src/math/webgl/conv_gpu_getmatrixvalueorzeropad_test.ts @@ -0,0 +1,139 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_gpu from './conv_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu getMatrixValueOrZeroPad', () => { + function createGetMatrixValueOrZeroPadProgram( + gpgpu: GPGPUContext, shapeRowCol: [number, number]): WebGLProgram { + const prologue = conv_gpu.getFragmentShaderPrologueSource(); + + const uniformColRow = 'uniform vec2 colRow;'; + + const getMatrixValueOrZeroPad = + conv_gpu.getFragmentShaderGetMatrixValueOrZeroPadSource(); + + const main = ` + void main() { + const vec2 aShapeCR = vec2(${shapeRowCol[1]}, ${shapeRowCol[0]}); + float value = getMatrixValueOrZeroPad(x, aShapeCR, colRow); + gl_FragColor = vec4(value, 0, 0, 0); + }`; + + const src = + [prologue, uniformColRow, getMatrixValueOrZeroPad, main].join('\n'); + return gpgpu.createProgram(src); + } + + function uploadGetMatrixValueOrZeroPadDownload( + matrix: Float32Array, shapeRowCol: [number, number], + paramRowCol: [number, number]): number { + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const program: WebGLProgram = + createGetMatrixValueOrZeroPadProgram(gpgpu, shapeRowCol); + + const matrixTexture = + gpgpu.createMatrixTexture(shapeRowCol[0], shapeRowCol[1]); + const resultTexture = gpgpu.createMatrixTexture(1, 1); + + gpgpu.uploadMatrixToTexture( + matrixTexture, shapeRowCol[0], shapeRowCol[1], matrix); + + gpgpu.setOutputMatrixTexture(resultTexture, 1, 1); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(matrixTexture, 'x', 0); + const loc = gpgpu.getUniformLocation('colRow'); + gpgpu.gl.uniform2f(loc, paramRowCol[1], paramRowCol[0]); + gpgpu.executeProgram(); + const result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteMatrixTexture(matrixTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; + } + + it('returns only value of a 1x1 matrix when row and column are 0', () => { + const a = new Float32Array([1.23]); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [1, 1], [0, 0]); + expect(result).toBeCloseTo(a[0]); + }); + + it('returns value of matrix cell at specified row and column', () => { + const a = new Float32Array(32 * 64); + a[5 + (30 * 64)] = Math.PI; + const result = uploadGetMatrixValueOrZeroPadDownload(a, [32, 64], [30, 5]); + expect(result).toBeCloseTo(Math.PI); + }); + + it('returns zero if sampling out-of-bounds left', () => { + const a = new Float32Array(4 * 4); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [4, 4], [0, -1]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds right', () => { + const a = new Float32Array(4 * 4); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [4, 4], [0, 15]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds top', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [-1, 0]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds bottom', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [20, 0]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds upper-left', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [-1, -1]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds upper-right', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [-1, 36]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds lower-left', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [20, -1]); + expect(result).toEqual(0); + }); + + it('returns zero if sampling out-of-bounds lower-right', () => { + const a = new Float32Array(19 * 35); + a.fill(1); + const result = uploadGetMatrixValueOrZeroPadDownload(a, [19, 35], [20, 36]); + expect(result).toEqual(0); + }); +}); diff --git a/src/math/webgl/conv_gpu_test.ts b/src/math/webgl/conv_gpu_test.ts new file mode 100644 index 0000000000..ac41c6a4ea --- /dev/null +++ b/src/math/webgl/conv_gpu_test.ts @@ -0,0 +1,413 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array1D, Array3D, Array4D, NDArray} from '../ndarray'; + +import * as conv_gpu from './conv_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +describe('conv_gpu', () => { + + function uploadConvolveDownload( + x: Float32Array, aShapeRowColDepth: [number, number, number], + weights: Float32Array, biases: Float32Array|null, resultDepth: number, + fieldSize: number, stride: number, zeroPad?: number): Float32Array { + zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad(aShapeRowColDepth, fieldSize, stride); + + const xTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + aShapeRowColDepth, fieldSize, resultDepth, stride, zeroPad); + + const weightsTexShapeRC: [number, number] = + conv_util.computeWeightsTexShape( + aShapeRowColDepth[2], resultDepth, fieldSize); + + const biasesTexShapeRC: [number, number] = [1, resultDepth]; + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = conv_gpu.getFragmentShaderSource( + aShapeRowColDepth, resultDepth, fieldSize, stride, zeroPad, + biases != null); + const program = gpgpu.createProgram(shaderSource); + + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + const weightsTex = + gpgpu.createMatrixTexture(weightsTexShapeRC[0], weightsTexShapeRC[1]); + const biasesTex = biases != null ? + gpgpu.createMatrixTexture(biasesTexShapeRC[0], biasesTexShapeRC[1]) : + null; + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(xTex, xTexShapeRC[0], xTexShapeRC[1], x); + gpgpu.uploadMatrixToTexture( + weightsTex, weightsTexShapeRC[0], weightsTexShapeRC[1], weights); + + if (biases != null) { + gpgpu.uploadMatrixToTexture( + biasesTex!, biasesTexShapeRC[0], biasesTexShapeRC[1], biases); + } + + conv_gpu.convolve( + gpgpu, program, xTex, weightsTex, biasesTex, resultTex, + resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + if (biasesTex != null) { + gpgpu.deleteMatrixTexture(biasesTex); + } + gpgpu.deleteMatrixTexture(weightsTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + function compareToCPU( + xShape: [number, number, number], fSize: number, resultDepth: number, + stride: number, pad: number) { + const x = NDArray.randNormal(xShape); + const weightsShape: [number, number, number, number] = + [fSize, fSize, xShape[2], resultDepth]; + const weights = NDArray.randNormal(weightsShape); + const biases = NDArray.randNormal([weightsShape[3]]); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = mathCPU.conv2d(x, weights, biases, stride, pad); + const yGPU = uploadConvolveDownload( + x.getValues(), xShape, weights.getValues(), biases.getValues(), + resultDepth, fSize, stride, pad); + + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('1x1x1 in, 1d out, 1x1 filter, 1 stride: [0] => [0]', () => { + const a = new Float32Array([0]); + const weights = new Float32Array([1]); + const biases = new Float32Array([0]); + const result = + uploadConvolveDownload(a, [1, 1, 1], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(0); + }); + + it('1x1x1 in, 1d out, 1x1 filter, 1 stride: [1] => [1]', () => { + const a = new Float32Array([1]); + const weights = new Float32Array([1]); + const biases = new Float32Array([0]); + const result = + uploadConvolveDownload(a, [1, 1, 1], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(1); + }); + + it('1x1x1 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([2]); + const weights = new Float32Array([3]); + const biases = new Float32Array([0]); + const result = + uploadConvolveDownload(a, [1, 1, 1], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(6); + }); + + it('1x1x1 in, 1d out, 1x1 filter, 1 stride, null bias', () => { + const a = new Float32Array([2]); + const weights = new Float32Array([3]); + const biases: Float32Array|null = null; + const result = + uploadConvolveDownload(a, [1, 1, 1], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(6); + }); + + it('1x1x1 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([2]); + const weights = new Float32Array([3]); + const biases = new Float32Array([Math.PI]); + const result = + uploadConvolveDownload(a, [1, 1, 1], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(6 + Math.PI); + }); + + it('1x1x2 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 1]); + const weights = new Float32Array([3, 5]); + const biases = new Float32Array([0, 0]); + const result = + uploadConvolveDownload(a, [1, 1, 2], weights, biases, 1, 1, 1); + expect(result).toBeCloseTo(8); + }); + + it('2x1x1 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2]); + const weights = new Float32Array([5]); + const biases = new Float32Array([0]); + const result = + uploadConvolveDownload(a, [2, 1, 1], weights, biases, 1, 1, 1); + expect(result.length).toEqual(2); + expect(result[0]).toBeCloseTo(5); + expect(result[1]).toBeCloseTo(10); + }); + + it('2x1x1 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2]); + const weights = new Float32Array([5]); + const biases = new Float32Array([Math.PI]); + const result = + uploadConvolveDownload(a, [2, 1, 1], weights, biases, 1, 1, 1); + expect(result.length).toEqual(2); + expect(result[0]).toBeCloseTo(5 + Math.PI); + expect(result[1]).toBeCloseTo(10 + Math.PI); + }); + + it('2x1x1 in, 2d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2]); + const weights = new Float32Array([5, 6]); + const biases = new Float32Array([0, 0]); + const result = + uploadConvolveDownload(a, [2, 1, 1], weights, biases, 2, 1, 1); + expect(result.length).toEqual(4); + expect(result[0]).toBeCloseTo(a[0] * weights[0]); + expect(result[1]).toBeCloseTo(a[0] * weights[1]); + expect(result[2]).toBeCloseTo(a[1] * weights[0]); + expect(result[3]).toBeCloseTo(a[1] * weights[1]); + }); + + it('2x1x1 in, 2d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2]); + const weights = new Float32Array([5, 6]); + const biases = new Float32Array([100, 200]); + const result = + uploadConvolveDownload(a, [2, 1, 1], weights, biases, 2, 1, 1); + expect(result.length).toEqual(4); + expect(result[0]).toBeCloseTo((a[0] * weights[0]) + biases[0]); + expect(result[1]).toBeCloseTo((a[0] * weights[1]) + biases[1]); + expect(result[2]).toBeCloseTo((a[1] * weights[0]) + biases[0]); + expect(result[3]).toBeCloseTo((a[1] * weights[1]) + biases[1]); + }); + + it('2x1x1 in, 3d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([2, 4]); + const weights = new Float32Array([3, 5, 7]); + const biases = new Float32Array([0, 0, 0]); + const result = + uploadConvolveDownload(a, [2, 1, 1], weights, biases, 3, 1, 1, 0); + expect(result.length).toEqual(2 * 3); + expect(result[0]).toBeCloseTo(a[0] * weights[0]); + expect(result[1]).toBeCloseTo(a[0] * weights[1]); + expect(result[2]).toBeCloseTo(a[0] * weights[2]); + expect(result[3]).toBeCloseTo(a[1] * weights[0]); + expect(result[4]).toBeCloseTo(a[1] * weights[1]); + expect(result[5]).toBeCloseTo(a[1] * weights[2]); + }); + + it('1x2x1 in, 1d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2]); + const weights = new Float32Array([5]); + const biases = new Float32Array([0]); + const result = + uploadConvolveDownload(a, [1, 2, 1], weights, biases, 1, 1, 1); + expect(result.length).toEqual(2); + expect(result[0]).toBeCloseTo(5); + expect(result[1]).toBeCloseTo(10); + }); + + it('2x1x2 in, 3d out, 1x1 filter, 1 stride', () => { + const a = new Float32Array([1, 2, 3, 4]); + const weights = new Float32Array([10, 11, 12, 13, 14, 15]); + const biases = new Float32Array([0, 0, 0]); + const result = + uploadConvolveDownload(a, [2, 1, 2], weights, biases, 3, 1, 1); + expect(result.length).toEqual(6); + expect(result[0]).toBeCloseTo(a[0] * weights[0] + a[1] * weights[3]); + expect(result[1]).toBeCloseTo(a[0] * weights[1] + a[1] * weights[4]); + expect(result[2]).toBeCloseTo(a[0] * weights[2] + a[1] * weights[5]); + expect(result[3]).toBeCloseTo(a[2] * weights[0] + a[3] * weights[3]); + expect(result[4]).toBeCloseTo(a[2] * weights[1] + a[3] * weights[4]); + expect(result[5]).toBeCloseTo(a[2] * weights[2] + a[3] * weights[5]); + }); + + it('2x2x1 in, 1d out, 2x2 filter, 1 stride', () => { + const x = new Float32Array([1, 2, 3, 4]); + const w = new Float32Array([3, 1, 5, 0]); + const bias = new Float32Array([0]); + const result = uploadConvolveDownload(x, [2, 2, 1], w, bias, 1, 2, 2, 1); + expect(result.length).toEqual(4); + expect(result[0]).toBe(0); + expect(result[1]).toBe(10); + expect(result[2]).toBe(3); + expect(result[3]).toBe(12); + }); + + it('2x2x1 in, 1d out, 2x2 filter, 1 stride', () => { + const x = new Float32Array([1, 2, 3, 4]); + const w = new Float32Array([3, 1, 5, 0]); + const bias = new Float32Array([-1]); + const result = uploadConvolveDownload(x, [2, 2, 1], w, bias, 1, 2, 1, 0); + expect(result.length).toEqual(1); + expect(result[0]).toBe(19); + }); + + it('2x2x1 in, 1d out, 2x2 filter, 1 stride, null bias', () => { + const x = new Float32Array([1, 2, 3, 4]); + const w = new Float32Array([3, 1, 5, 0]); + const bias: Float32Array|null = null; + const result = uploadConvolveDownload(x, [2, 2, 1], w, bias, 1, 2, 1, 0); + expect(result.length).toEqual(1); + expect(result[0]).toBe(20); + }); + + it('2x2x1 in, 1d out, 2x2 filter, 1 stride, zeropad = 1', () => { + const x = new Float32Array([1, 2, 3, 4]); + const w = new Float32Array([3, 1, 5, 0]); + const bias = new Float32Array([0]); + const result = uploadConvolveDownload(x, [2, 2, 1], w, bias, 1, 2, 2, 1); + expect(result.length).toEqual(4); + expect(result[0]).toBe(0); + expect(result[1]).toBe(10); + expect(result[2]).toBe(3); + expect(result[3]).toBe(12); + }); + + it('5x5x3 in, 2d out, 3x3 filter, 2 stride', () => { + /* + weights: input: + [ 1, -1, [1, 2, 2, 0, 0, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, + 1, 0, 1, 2, 2, 0, 2, 2, 1, 1, 0, 0, 2, 1, 1, 0, 1, + -1, 1, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, + -1, 0, 1, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 1, 0, 1, 2, + -1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 0, 2, 0, 0, 0] + 0, 1, + -1, 1, biases: + 1, 1, [1, 0] + 1, 1, + 0, 1, + 0, 0, + 0, 1, + -1, -1, + 1, 0, + 1, -1, + 1, 1, + 1, 1, + 1, -1, + -1, 0, + 1, 0, + 0, 0, + 1, -1, + -1, -1, + 1, 0, + -1, 1, + 0, -1, + 0, 1] + */ + + const input = new Float32Array([ + 1, 2, 2, 0, 0, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 0, + 2, 2, 1, 1, 0, 0, 2, 1, 1, 0, 1, 2, 2, 0, 0, 2, 2, 1, 2, + 2, 2, 1, 2, 2, 2, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 1, + 0, 1, 2, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 0, 2, 0, 0, 0 + ]); + + const weights = new Float32Array([ + 1, -1, 1, 0, -1, 1, -1, 0, -1, 0, 0, 1, -1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, -1, -1, 1, 0, 1, -1, 1, 1, 1, 1, 1, -1, + -1, 0, 1, 0, 0, 0, 1, -1, -1, -1, 1, 0, -1, 1, 0, -1, 0, 1 + ]); + + const biases = new Float32Array([1, 0]); + + const result = + uploadConvolveDownload(input, [5, 5, 3], weights, biases, 2, 3, 2, 1); + /* + Filter centered at [0,0], zero-pad 1 column and 1 row + 0 0 0 0 0 0 0 0 0 + 0 0 0 1 2 2 0 0 2 + 0 0 0 1 2 2 0 2 2 + + Weights, column [0] + 1 1 -1 -1 -1 0 -1 1 1 + 0 0 0 -1 1 1 1 1 1 + -1 1 0 1 -1 1 -1 0 0 + + Element-wise product (dot product before summation) + 0 0 0 0 0 0 0 0 0 + 0 0 0 -1 2 2 0 0 2 + 0 0 0 1 -2 2 0 0 0 + + Sum of elements, plus bias of 1 + (-1 + 2 + 2 + 2 + 1 + -2 + 2) + 1 == 7 + */ + + expect(result[0]).toBeCloseTo(7); + + test_util.expectArraysClose( + result, + new Float32Array( + [7, -8, 8, -2, 7, -2, 5, 5, 4, 6, 1, 2, -1, 3, 7, -2, 1, 4]), + 0.00001); + }); + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 2; + const outputDepth = 1; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=1,d2=1,f=3,s=2,p=1', () => { + const inputDepth = 1; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 1; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=4,d2=3,f=2,s=1,p=0', () => { + const inputDepth = 4; + const inputShape: [number, number, number] = [8, 8, inputDepth]; + const fSize = 2; + const outputDepth = 3; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); + + it('matches CPU on random input, d1=3,d2=4,f=3,s=3,p=1', () => { + const inputDepth = 3; + const inputShape: [number, number, number] = [7, 7, inputDepth]; + const fSize = 3; + const outputDepth = 4; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, outputDepth, stride, zeroPad); + }); +}); diff --git a/src/math/webgl/copy_gpu.ts b/src/math/webgl/copy_gpu.ts new file mode 100644 index 0000000000..3210644704 --- /dev/null +++ b/src/math/webgl/copy_gpu.ts @@ -0,0 +1,63 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + sourceShapeRowCol: [number, number], sourceSizeRowCol: [number, number], + destSizeRowCol: [number, number]): string { + return ` + precision highp float; + uniform sampler2D source; + uniform vec2 sourceStartCR; + uniform vec2 destStartCR; + + const vec2 sourceShapeCR = + vec2(${sourceShapeRowCol[1]}, ${sourceShapeRowCol[0]}); + const vec2 sourceSizeCR = + vec2(${sourceSizeRowCol[1]}, ${sourceSizeRowCol[0]}); + const vec2 destSizeCR = + vec2(${destSizeRowCol[1]}, ${destSizeRowCol[0]}); + + void main() { + vec2 destOffsetCR = floor(gl_FragCoord.xy) - destStartCR; + float destOffsetFlat = (destOffsetCR.y * destSizeCR.x) + destOffsetCR.x; + vec2 sourceOffsetCR = vec2(mod(destOffsetFlat, sourceSizeCR.x), + floor(destOffsetFlat / sourceSizeCR.x)); + vec2 sourceCR = sourceStartCR + sourceOffsetCR; + vec2 sourceUV = (sourceCR + vec2(0.5, 0.5)) / sourceShapeCR; + gl_FragColor = texture2D(source, sourceUV); + }`; +} + +export function copy( + gpgpu: GPGPUContext, program: WebGLProgram, source: WebGLTexture, + sourceShapeRowCol: [number, number], sourceStartRowCol: [number, number], + sourceSizeRowCol: [number, number], dest: WebGLTexture, + destShapeRowCol: [number, number], destStartRowCol: [number, number], + destSizeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture(dest, destShapeRowCol[0], destShapeRowCol[1]); + gpgpu.setOutputMatrixWriteRegion( + destStartRowCol[0], destSizeRowCol[0], destStartRowCol[1], + destSizeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(source, 'source', 0); + const sourceStartCRLoc = gpgpu.getUniformLocation('sourceStartCR'); + gpgpu.gl.uniform2f( + sourceStartCRLoc, sourceStartRowCol[1], sourceStartRowCol[0]); + const destStartCRLoc = gpgpu.getUniformLocation('destStartCR'); + gpgpu.gl.uniform2f(destStartCRLoc, destStartRowCol[1], destStartRowCol[0]); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/copy_gpu_test.ts b/src/math/webgl/copy_gpu_test.ts new file mode 100644 index 0000000000..6600995f3b --- /dev/null +++ b/src/math/webgl/copy_gpu_test.ts @@ -0,0 +1,189 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as copy_gpu from './copy_gpu'; +import {GPGPUContext} from './gpgpu_context'; + +function uploadCopyDownload( + source: Float32Array, sourceShapeRowCol: [number, number], + sourceStartRowCol: [number, number], sourceSizeRowCol: [number, number], + destStartRowCol: [number, number], destSizeRowCol: [number, number], + dest: Float32Array, destShapeRowCol: [number, number]): Float32Array { + const gpgpu = new GPGPUContext(); + const fragmentShaderSource = copy_gpu.getFragmentShaderSource( + sourceShapeRowCol, sourceSizeRowCol, destSizeRowCol); + const program = gpgpu.createProgram(fragmentShaderSource); + + const sourceTex = + gpgpu.createMatrixTexture(sourceShapeRowCol[0], sourceShapeRowCol[1]); + const destTex = + gpgpu.createMatrixTexture(destShapeRowCol[0], destShapeRowCol[1]); + + gpgpu.uploadMatrixToTexture( + sourceTex, sourceShapeRowCol[0], sourceShapeRowCol[1], source); + gpgpu.uploadMatrixToTexture( + destTex, destShapeRowCol[0], destShapeRowCol[1], dest); + + copy_gpu.copy( + gpgpu, program, sourceTex, sourceShapeRowCol, sourceStartRowCol, + sourceSizeRowCol, destTex, destShapeRowCol, destStartRowCol, + destSizeRowCol); + + const result = gpgpu.downloadMatrixFromTexture( + destTex, destShapeRowCol[0], destShapeRowCol[1]); + + gpgpu.deleteMatrixTexture(sourceTex); + gpgpu.deleteMatrixTexture(destTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return result; +} + +describe('copy_gpu', () => { + it('copies a 1x1 source to a 1x1 dest', () => { + const source = new Float32Array([Math.PI]); + const dest = new Float32Array([0]); + const result = uploadCopyDownload( + source, [1, 1], [0, 0], [1, 1], [0, 0], [1, 1], dest, [1, 1]); + expect(result.length).toEqual(1); + expect(result[0]).toBeCloseTo(Math.PI); + }); + + it('copies a 1x2 source to a 1x2 dest', () => { + const source = new Float32Array([1, 2]); + const dest = new Float32Array([0, 0]); + const result = uploadCopyDownload( + source, [1, 2], [0, 0], [1, 2], [0, 0], [1, 2], dest, [1, 2]); + expect(result.length).toEqual(2); + expect(result[0]).toEqual(1); + expect(result[1]).toEqual(2); + }); + + it('copies a 2x1 source to a 2x1 dest', () => { + const source = new Float32Array([1, 2]); + const dest = new Float32Array([0, 0]); + const result = uploadCopyDownload( + source, [2, 1], [0, 0], [2, 1], [0, 0], [2, 1], dest, [2, 1]); + expect(result.length).toEqual(2); + expect(result[0]).toEqual(1); + expect(result[1]).toEqual(2); + }); + + it('copies a 2x2 source to a 2x2 dest', () => { + const source = new Float32Array([1, 2, 3, 4]); + const dest = new Float32Array([0, 0, 0, 0]); + const result = uploadCopyDownload( + source, [2, 2], [0, 0], [2, 2], [0, 0], [2, 2], dest, [2, 2]); + expect(result.length).toEqual(4); + expect(result[0]).toEqual(1); + expect(result[1]).toEqual(2); + expect(result[2]).toEqual(3); + expect(result[3]).toEqual(4); + }); + + it('copies inner 2x2 from a 4x4 source to a 2x2 dest', () => { + const source = new Float32Array(16); + source[5] = 10; + source[6] = 11; + source[9] = 12; + source[10] = 13; + const dest = new Float32Array(4); + const result = uploadCopyDownload( + source, [4, 4], [1, 1], [2, 2], [0, 0], [2, 2], dest, [2, 2]); + expect(result.length).toEqual(4); + expect(result[0]).toEqual(10); + expect(result[1]).toEqual(11); + expect(result[2]).toEqual(12); + expect(result[3]).toEqual(13); + }); + + it('copies a 1x4 row from source into a 2x2 dest', () => { + const source = new Float32Array([1, 2, 3, 4]); + const dest = new Float32Array(4); + const result = uploadCopyDownload( + source, [1, 4], [0, 0], [1, 4], [0, 0], [2, 2], dest, [2, 2]); + expect(result.length).toEqual(4); + expect(result[0]).toEqual(1); + expect(result[1]).toEqual(2); + expect(result[2]).toEqual(3); + expect(result[3]).toEqual(4); + }); + + it('copies a 1x4 row from source into a 4x1 dest', () => { + const source = new Float32Array([1, 2, 3, 4]); + const dest = new Float32Array(4); + const result = uploadCopyDownload( + source, [1, 4], [0, 0], [1, 4], [0, 0], [4, 1], dest, [4, 1]); + expect(result.length).toEqual(4); + expect(result[0]).toEqual(1); + expect(result[1]).toEqual(2); + expect(result[2]).toEqual(3); + expect(result[3]).toEqual(4); + }); + + it('copies a column from source into a dest row vector', () => { + const source = new Float32Array(10 * 10); + for (let i = 0; i < 10; ++i) { + source[3 + (i * 10)] = i + 1; + } + const dest = new Float32Array(10); + const result = uploadCopyDownload( + source, [10, 10], [0, 3], [10, 1], [0, 0], [1, 10], dest, [1, 10]); + test_util.expectArraysClose( + result, new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 0); + }); + + it('doesn\'t touch destination pixels outside of the source box', () => { + const source = new Float32Array([1]); + const dest = new Float32Array([Math.PI, 0]); + const result = uploadCopyDownload( + source, [1, 1], [0, 0], [1, 1], [0, 1], [1, 1], dest, [1, 2]); + expect(result[0]).toBeCloseTo(Math.PI); + expect(result[1]).toEqual(1); + }); + + it('accumulates results from previous copies into dest texture', () => { + const shapeRC: [number, number] = [10, 10]; + const sizeRC: [number, number] = [10, 1]; + const source = new Float32Array(100); + for (let i = 0; i < 100; ++i) { + source[i] = i; + } + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram( + copy_gpu.getFragmentShaderSource(shapeRC, sizeRC, sizeRC)); + const sourceTex = gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + const destTex = gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + gpgpu.uploadMatrixToTexture(sourceTex, shapeRC[0], shapeRC[1], source); + + for (let i = 0; i < 10; ++i) { + copy_gpu.copy( + gpgpu, program, sourceTex, shapeRC, [0, i], sizeRC, destTex, shapeRC, + [0, i], sizeRC); + } + + const dest = + gpgpu.downloadMatrixFromTexture(destTex, shapeRC[0], shapeRC[1]); + + gpgpu.deleteMatrixTexture(sourceTex); + gpgpu.deleteMatrixTexture(destTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + test_util.expectArraysClose(dest, source, 0); + }); +}); diff --git a/src/math/webgl/exp_gpu.ts b/src/math/webgl/exp_gpu.ts new file mode 100644 index 0000000000..2f10f909eb --- /dev/null +++ b/src/math/webgl/exp_gpu.ts @@ -0,0 +1,36 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getExpUnaryOp(): string { + return 'gl_FragColor = vec4(exp(value), 0, 0, 0);'; +} + +export function getFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getExpUnaryOp()); +} + +export function exp( + gpgpu: GPGPUContext, expProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, expProgram, a, rows, columns, result); +} + +export function uploadExpDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getExpUnaryOp()); +} diff --git a/src/math/webgl/exp_gpu_test.ts b/src/math/webgl/exp_gpu_test.ts new file mode 100644 index 0000000000..60048f7299 --- /dev/null +++ b/src/math/webgl/exp_gpu_test.ts @@ -0,0 +1,49 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as exp_gpu from './exp_gpu'; + +describe('exp_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(23 * 32); + const result = exp_gpu.uploadExpDownload(a, 23, 32); + expect(result.length).toEqual(a.length); + }); + + it('returns e when the only value in a 1x1 matrix is 1.0', () => { + const a = new Float32Array([1]); + const result = exp_gpu.uploadExpDownload(a, 1, 1); + expect(result[0]).toBeCloseTo(Math.E); + }); + + it('operates on every value in a matrix', () => { + const a = new Float32Array([1, 1, 1, 1, 1, 1]); + const result = exp_gpu.uploadExpDownload(a, 1, a.length); + const expected = new Float32Array(a.length); + expected.fill(Math.E); + test_util.expectArraysClose(result, expected, 0.0001); + }); + + it('calculates f(x)=e^x for every value in the matrix', () => { + const a = new Float32Array([0.5, 1, 2, -1]); + const result = exp_gpu.uploadExpDownload(a, 1, a.length); + const expected = new Float32Array(a.length); + for (let i = 0; i < a.length; ++i) { + expected[i] = Math.exp(a[i]); + } + test_util.expectArraysClose(result, expected, 0.0001); + }); +}); diff --git a/src/math/webgl/gpgpu_context.ts b/src/math/webgl/gpgpu_context.ts new file mode 100644 index 0000000000..fda586fa5b --- /dev/null +++ b/src/math/webgl/gpgpu_context.ts @@ -0,0 +1,310 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as gpgpu_util from './gpgpu_util'; +import * as tex_util from './tex_util'; +import * as webgl_util from './webgl_util'; + +import {WebGLLoseContextExtension} from './webgl_util'; + +export class GPGPUContext { + gl: WebGLRenderingContext; + textureFloatExtension: {}; + colorBufferFloatExtension: {}; + loseContextExtension: WebGLLoseContextExtension; + vertexBuffer: WebGLBuffer; + indexBuffer: WebGLBuffer; + framebuffer: WebGLFramebuffer; + outputTexture: WebGLTexture|null = null; + program: WebGLProgram|null = null; + private disposed = false; + private autoDebugValidate = false; + + constructor(gl?: WebGLRenderingContext) { + if (gl != null) { + this.gl = gl; + } else { + this.gl = gpgpu_util.createWebGLContext(); + } + + // WebGL 2.0 enables texture floats without an extension. + if (!webgl_util.isWebGL2Enabled()) { + this.textureFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'OES_texture_float'); + } else { + this.colorBufferFloatExtension = + webgl_util.getExtensionOrThrow(this.gl, 'EXT_color_buffer_float'); + } + + this.loseContextExtension = + webgl_util.getExtensionOrThrow(this.gl, 'WEBGL_lose_context') as + WebGLLoseContextExtension; + this.vertexBuffer = gpgpu_util.createVertexBuffer(this.gl); + this.indexBuffer = gpgpu_util.createIndexBuffer(this.gl); + this.framebuffer = webgl_util.createFramebuffer(this.gl); + } + + public dispose() { + this.throwIfDisposed(); + if (this.program != null) { + console.warn( + 'Disposing a GPGPUContext that still has a bound WebGLProgram.' + + ' This is probably a resource leak, delete the program with ' + + 'GPGPUContext.deleteProgram before disposing.'); + } + if (this.outputTexture != null) { + console.warn( + 'Disposing a GPGPUContext that still has a bound output matrix ' + + 'texture. This is probably a resource leak, delete the output ' + + 'matrix texture with GPGPUContext.deleteMatrixTexture before ' + + 'disposing.'); + } + const gl = this.gl; + webgl_util.callAndCheck(gl, () => gl.finish()); + webgl_util.callAndCheck(gl, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); + webgl_util.callAndCheck(gl, () => gl.deleteFramebuffer(this.framebuffer)); + webgl_util.callAndCheck(gl, () => gl.bindBuffer(gl.ARRAY_BUFFER, null)); + webgl_util.callAndCheck(gl, () => gl.deleteBuffer(this.vertexBuffer)); + webgl_util.callAndCheck( + gl, () => gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null)); + webgl_util.callAndCheck(gl, () => gl.deleteBuffer(this.indexBuffer)); + this.loseContextExtension.loseContext(); + this.disposed = true; + } + + public enableAutomaticDebugValidation(enabled: boolean) { + this.autoDebugValidate = enabled; + webgl_util.enableDebugWebGLErrorChecking(enabled); + } + + public createMatrixTexture(rows: number, columns: number): WebGLTexture { + this.throwIfDisposed(); + return gpgpu_util.createMatrixTexture(this.gl, rows, columns); + } + + public uploadPixelDataToTexture( + texture: WebGLTexture, + pixels: ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement) { + this.throwIfDisposed(); + gpgpu_util.uploadPixelDataToTexture(this.gl, texture, pixels); + } + + public createPackedMatrixTexture(rows: number, columns: number): + WebGLTexture { + this.throwIfDisposed(); + return gpgpu_util.createPackedMatrixTexture(this.gl, rows, columns); + } + + public deleteMatrixTexture(texture: WebGLTexture) { + this.throwIfDisposed(); + if (this.outputTexture === texture) { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + this.outputTexture = null; + } + webgl_util.callAndCheck(this.gl, () => this.gl.deleteTexture(texture)); + } + + public uploadMatrixToTexture( + texture: WebGLTexture, rows: number, columns: number, + matrix: Float32Array) { + this.throwIfDisposed(); + const numChannels = 1; + return gpgpu_util.uploadMatrixToTexture( + this.gl, texture, rows, columns, matrix, numChannels); + } + + public uploadMatrixToPackedTexture( + texture: WebGLTexture, rows: number, columns: number, + matrix: Float32Array) { + this.throwIfDisposed(); + return gpgpu_util.uploadMatrixToPackedTexture( + this.gl, texture, rows, columns, matrix); + } + + public downloadMatrixFromTexture( + texture: WebGLTexture, rows: number, columns: number): Float32Array { + return this.downloadMatrixDriver( + texture, + () => + gpgpu_util.downloadMatrixFromOutputTexture(this.gl, rows, columns)); + } + + public downloadMatrixFromPackedTexture( + texture: WebGLTexture, rows: number, columns: number): Float32Array { + return this.downloadMatrixDriver( + texture, + () => gpgpu_util.downloadMatrixFromPackedOutputTexture( + this.gl, rows, columns)); + } + + public createProgram(fragmentShaderSource: string): WebGLProgram { + this.throwIfDisposed(); + const gl = this.gl; + const fragmentShader: WebGLShader = + webgl_util.createFragmentShader(gl, fragmentShaderSource); + const vertexShader: WebGLShader = gpgpu_util.createVertexShader(gl); + const program: WebGLProgram = webgl_util.createProgram(gl); + webgl_util.callAndCheck(gl, () => gl.attachShader(program, vertexShader)); + webgl_util.callAndCheck(gl, () => gl.attachShader(program, fragmentShader)); + webgl_util.linkProgram(gl, program); + if (this.autoDebugValidate) { + webgl_util.validateProgram(gl, program); + } + webgl_util.callAndCheck(gl, () => gl.detachShader(program, vertexShader)); + webgl_util.callAndCheck(gl, () => gl.deleteShader(vertexShader)); + webgl_util.callAndCheck(gl, () => gl.detachShader(program, fragmentShader)); + webgl_util.callAndCheck(gl, () => gl.deleteShader(fragmentShader)); + return program; + } + + public deleteProgram(program: WebGLProgram) { + this.throwIfDisposed(); + if (program === this.program) { + this.program = null; + } + if (program != null) { + webgl_util.callAndCheck(this.gl, () => this.gl.deleteProgram(program)); + } + } + + public setProgram(program: WebGLProgram|null) { + this.throwIfDisposed(); + this.program = program; + if ((this.program != null) && this.autoDebugValidate) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.callAndCheck(this.gl, () => this.gl.useProgram(program)); + } + + public getUniformLocation(uniformName: string): WebGLUniformLocation { + this.throwIfDisposed(); + this.throwIfNoProgram(); + return webgl_util.getProgramUniformLocationOrThrow( + this.gl, this.program!, uniformName); + } + + public setInputMatrixTexture( + inputMatrixTexture: WebGLTexture, uniformName: string, + textureUnit: number) { + this.throwIfDisposed(); + this.throwIfNoProgram(); + webgl_util.bindTextureToProgramUniformSampler( + this.gl, this.program!, inputMatrixTexture, uniformName, textureUnit); + } + + public setOutputMatrixTexture( + outputMatrixTexture: WebGLTexture, rows: number, columns: number) { + this.setOutputMatrixTextureDriver(outputMatrixTexture, columns, rows); + } + + public setOutputPackedMatrixTexture( + outputPackedMatrixTexture: WebGLTexture, rows: number, columns: number) { + this.throwIfDisposed(); + const [width, height] = + tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); + this.setOutputMatrixTextureDriver(outputPackedMatrixTexture, width, height); + } + + public setOutputMatrixWriteRegion( + startRow: number, numRows: number, startColumn: number, + numColumns: number) { + this.setOutputMatrixWriteRegionDriver( + startColumn, startRow, numColumns, numRows); + } + + public setOutputPackedMatrixWriteRegion( + startRow: number, numRows: number, startColumn: number, + numColumns: number) { + throw new Error('setOutputPackedMatrixWriteRegion not implemented.'); + } + + public debugValidate() { + if (this.program != null) { + webgl_util.validateProgram(this.gl, this.program); + } + webgl_util.validateFramebuffer(this.gl); + } + + public executeProgram() { + this.throwIfDisposed(); + this.throwIfNoProgram(); + const gl = this.gl; + gpgpu_util.bindVertexProgramAttributeStreams( + gl, this.program!, this.vertexBuffer); + if (this.autoDebugValidate) { + this.debugValidate(); + } + webgl_util.callAndCheck( + gl, () => gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)); + } + + public blockUntilAllProgramsCompleted() { + this.throwIfDisposed(); + webgl_util.callAndCheck(this.gl, () => this.gl.finish()); + } + + private downloadMatrixDriver( + texture: WebGLTexture, + downloadAndDecode: () => Float32Array): Float32Array { + this.throwIfDisposed(); + webgl_util.bindColorTextureToFramebuffer( + this.gl, texture, this.framebuffer); + const result = downloadAndDecode(); + if (this.outputTexture != null) { + webgl_util.bindColorTextureToFramebuffer( + this.gl, this.outputTexture, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(this.gl); + } + } else { + webgl_util.unbindColorTextureFromFramebuffer(this.gl, this.framebuffer); + } + return result; + } + + private setOutputMatrixTextureDriver( + outputMatrixTextureMaybePacked: WebGLTexture, width: number, + height: number) { + this.throwIfDisposed(); + const gl = this.gl; + webgl_util.bindColorTextureToFramebuffer( + gl, outputMatrixTextureMaybePacked, this.framebuffer); + if (this.autoDebugValidate) { + webgl_util.validateFramebuffer(gl); + } + this.outputTexture = outputMatrixTextureMaybePacked; + webgl_util.callAndCheck(gl, () => gl.viewport(0, 0, width, height)); + webgl_util.callAndCheck(gl, () => gl.scissor(0, 0, width, height)); + } + + private setOutputMatrixWriteRegionDriver( + x: number, y: number, width: number, height: number) { + this.throwIfDisposed(); + webgl_util.callAndCheck( + this.gl, () => this.gl.scissor(x, y, width, height)); + } + + private throwIfDisposed() { + if (this.disposed) { + throw new Error('Attempted to use disposed GPGPUContext.'); + } + } + + private throwIfNoProgram() { + if (this.program == null) { + throw new Error('No GPU program is currently set.'); + } + } +} diff --git a/src/math/webgl/gpgpu_context_test.ts b/src/math/webgl/gpgpu_context_test.ts new file mode 100644 index 0000000000..efc802fba4 --- /dev/null +++ b/src/math/webgl/gpgpu_context_test.ts @@ -0,0 +1,421 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import {GPGPUContext} from './gpgpu_context'; +import * as tex_util from './tex_util'; +import * as webgl_util from './webgl_util'; + +describe('GPGPUContext downloadMatrixFromTexture WebGL 2.0', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + texture = gpgpu.createMatrixTexture(1, 1); + }); + + afterEach(() => { + gpgpu.deleteMatrixTexture(texture); + gpgpu.dispose(); + }); + + it('returns clear color from the output texture', () => { + gpgpu.setOutputMatrixTexture(texture, 1, 1); + gpgpu.gl.clearColor(0.123, 0, 0, 0); + gpgpu.gl.clear(gpgpu.gl.COLOR_BUFFER_BIT); + const result = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(result[0]).toBeCloseTo(0.123); + }); + + it('returns matrix that was uploaded', () => { + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([1.234])); + const result = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(result[0]).toBeCloseTo(1.234); + }); + + it('uses texture parameter', () => { + const texture2: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([1])); + gpgpu.uploadMatrixToTexture(texture2, 1, 1, new Float32Array([2])); + const read1 = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + const read2 = gpgpu.downloadMatrixFromTexture(texture2, 1, 1); + expect(read1[0]).toBeCloseTo(1); + expect(read2[0]).toBeCloseTo(2); + gpgpu.deleteMatrixTexture(texture2); + }); +}); + +describe('GPGPUContext downloadMatrixFromTexture WebGL 1.0', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + webgl_util.preferWebGL1(); + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + texture = gpgpu.createMatrixTexture(1, 1); + }); + + afterEach(() => { + gpgpu.deleteMatrixTexture(texture); + gpgpu.dispose(); + webgl_util.preferWebGL2(); + }); + + it('returns clear color from the output texture', () => { + gpgpu.setOutputMatrixTexture(texture, 1, 1); + gpgpu.gl.clearColor(0.123, 0, 0, 0); + gpgpu.gl.clear(gpgpu.gl.COLOR_BUFFER_BIT); + const result = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(result[0]).toBeCloseTo(0.123); + }); + + it('returns matrix that was uploaded', () => { + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([1.234])); + const result = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(result[0]).toBeCloseTo(1.234); + }); + + it('uses texture parameter', () => { + const texture2: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([1])); + gpgpu.uploadMatrixToTexture(texture2, 1, 1, new Float32Array([2])); + const read1 = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + const read2 = gpgpu.downloadMatrixFromTexture(texture2, 1, 1); + expect(read1[0]).toBeCloseTo(1); + expect(read2[0]).toBeCloseTo(2); + gpgpu.deleteMatrixTexture(texture2); + }); +}); + +describe('GPGPUContext setOutputMatrixTexture WebGL 2.0', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + texture = gpgpu.createMatrixTexture(1, 1); + }); + + afterEach(() => { + gpgpu.deleteMatrixTexture(texture); + gpgpu.dispose(); + }); + + it('sets the output texture property to the output texture', () => { + gpgpu.setOutputMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + }); + + it('rebinds the output texture to the color buffer target', () => { + const output: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([10])); + gpgpu.setOutputMatrixTexture(output, 1, 1); + const tBeforeClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tBeforeClear[0]).toBeCloseTo(10); + gpgpu.gl.clearColor(1, 0, 0, 0); + gpgpu.gl.clear(gpgpu.gl.COLOR_BUFFER_BIT); + const tAfterClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tAfterClear[0]).toBeCloseTo(10); + gpgpu.deleteMatrixTexture(output); + }); + + it('resets output texture to null if nothing was previously bound', () => { + expect(gpgpu.outputTexture).toBeNull(); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBeNull(); + }); + + it('sets the gl viewport to the output texture dimensions', () => { + const columns = 456; + const rows = 123; + const output = gpgpu.createMatrixTexture(rows, columns); + gpgpu.setOutputMatrixTexture(output, rows, columns); + const expected = new Int32Array([0, 0, columns, rows]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); + + it('doesn\'t change gl viewport when downloading a non-output tex', () => { + const output = gpgpu.createMatrixTexture(128, 128); + gpgpu.setOutputMatrixTexture(output, 128, 128); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + const expected = new Int32Array([0, 0, 128, 128]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); +}); + +describe('GPGPUContext setOutputMatrixTexture WebGL 1.0', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + webgl_util.preferWebGL1(); + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + texture = gpgpu.createMatrixTexture(1, 1); + }); + + afterEach(() => { + webgl_util.preferWebGL2(); + gpgpu.deleteMatrixTexture(texture); + gpgpu.dispose(); + }); + + it('sets the output texture property to the output texture', () => { + gpgpu.setOutputMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + }); + + it('rebinds the output texture to the color buffer target', () => { + const output: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([10])); + gpgpu.setOutputMatrixTexture(output, 1, 1); + const tBeforeClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tBeforeClear[0]).toBeCloseTo(10); + gpgpu.gl.clearColor(1, 0, 0, 0); + gpgpu.gl.clear(gpgpu.gl.COLOR_BUFFER_BIT); + const tAfterClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tAfterClear[0]).toBeCloseTo(10); + gpgpu.deleteMatrixTexture(output); + }); + + it('resets output texture to null if nothing was previously bound', () => { + expect(gpgpu.outputTexture).toBeNull(); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBeNull(); + }); + + it('sets the gl viewport to the output texture dimensions', () => { + const columns = 456; + const rows = 123; + const output = gpgpu.createMatrixTexture(rows, columns); + gpgpu.setOutputMatrixTexture(output, rows, columns); + const expected = new Int32Array([0, 0, columns, rows]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); + + it('doesn\'t change gl viewport when downloading a non-output tex', () => { + const output = gpgpu.createMatrixTexture(128, 128); + gpgpu.setOutputMatrixTexture(output, 128, 128); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + const expected = new Int32Array([0, 0, 128, 128]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); +}); + +describe('GPGPUContext setOutputMatrixTexture WebGL 2.0', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + webgl_util.preferWebGL2(); + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + texture = gpgpu.createMatrixTexture(1, 1); + }); + + afterEach(() => { + gpgpu.deleteMatrixTexture(texture); + gpgpu.dispose(); + }); + + it('sets the output texture property to the output texture', () => { + gpgpu.setOutputMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + }); + + it('rebinds the output texture to the color buffer target', () => { + const output: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(texture, 1, 1, new Float32Array([10])); + gpgpu.setOutputMatrixTexture(output, 1, 1); + const tBeforeClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tBeforeClear[0]).toBeCloseTo(10); + gpgpu.gl.clearColor(1, 0, 0, 0); + gpgpu.gl.clear(gpgpu.gl.COLOR_BUFFER_BIT); + const tAfterClear = gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(tAfterClear[0]).toBeCloseTo(10); + gpgpu.deleteMatrixTexture(output); + }); + + it('resets output texture to null if nothing was previously bound', () => { + expect(gpgpu.outputTexture).toBeNull(); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBeNull(); + }); + + it('sets the gl viewport to the output texture dimensions', () => { + const columns = 456; + const rows = 123; + const output = gpgpu.createMatrixTexture(rows, columns); + gpgpu.setOutputMatrixTexture(output, rows, columns); + const expected = new Int32Array([0, 0, columns, rows]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); + + it('doesn\'t change gl viewport when downloading a non-output tex', () => { + const output = gpgpu.createMatrixTexture(128, 128); + gpgpu.setOutputMatrixTexture(output, 128, 128); + gpgpu.downloadMatrixFromTexture(texture, 1, 1); + const expected = new Int32Array([0, 0, 128, 128]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + gpgpu.deleteMatrixTexture(output); + }); +}); + +describe('GPGPUContext setOutputPackedMatrixTexture', () => { + let gpgpu: GPGPUContext; + let texture: WebGLTexture; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + }); + + afterEach(() => { + if (texture != null) { + gpgpu.deleteMatrixTexture(texture); + } + gpgpu.dispose(); + }); + + it('sets the output texture property to the output texture', () => { + texture = gpgpu.createPackedMatrixTexture(1, 1); + gpgpu.setOutputPackedMatrixTexture(texture, 1, 1); + expect(gpgpu.outputTexture).toBe(texture); + }); + + it('sets the gl viewport to the output packed texture dimensions', () => { + const columns = 456; + const rows = 123; + texture = gpgpu.createPackedMatrixTexture(rows, columns); + gpgpu.setOutputPackedMatrixTexture(texture, rows, columns); + const [width, height] = + tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); + const expected = new Int32Array([0, 0, width, height]); + expect(gpgpu.gl.getParameter(gpgpu.gl.VIEWPORT)).toEqual(expected); + }); +}); + +describe('GPGPUContext setOutputMatrixWriteRegion', () => { + let gpgpu: GPGPUContext; + let program: WebGLProgram; + let output: WebGLTexture; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + const src = + 'precision highp float; void main() { gl_FragColor = vec4(2,0,0,0); }'; + program = gpgpu.createProgram(src); + output = gpgpu.createMatrixTexture(4, 4); + gpgpu.uploadMatrixToTexture(output, 4, 4, new Float32Array(16)); + gpgpu.setOutputMatrixTexture(output, 4, 4); + gpgpu.setProgram(program); + }); + + afterEach(() => { + gpgpu.deleteMatrixTexture(output); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + }); + + it('writes to all pixels by default', () => { + gpgpu.executeProgram(); + const result = gpgpu.downloadMatrixFromTexture(output, 4, 4); + const expected = new Float32Array(4 * 4); + expected.fill(2); + test_util.expectArraysClose(result, expected, 0); + }); + + it('sets the scissor box to the requested parameters', () => { + gpgpu.setOutputMatrixWriteRegion(0, 1, 2, 3); + const scissorBox = gpgpu.gl.getParameter(gpgpu.gl.SCISSOR_BOX); + expect(scissorBox[0]).toEqual(2); + expect(scissorBox[1]).toEqual(0); + expect(scissorBox[2]).toEqual(3); + expect(scissorBox[3]).toEqual(1); + }); + + it('writes only to center 2x2 region of 4x4 texture', () => { + gpgpu.setOutputMatrixWriteRegion(1, 2, 1, 2); + gpgpu.executeProgram(); + const result = gpgpu.downloadMatrixFromTexture(output, 4, 4); + const expected = + new Float32Array([0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0]); + test_util.expectArraysClose(result, expected, 0); + }); + + it('preserves data from previous writes outside of write region', () => { + gpgpu.setOutputMatrixWriteRegion(0, 1, 0, 4); // top row + gpgpu.executeProgram(); + gpgpu.setOutputMatrixWriteRegion(3, 1, 0, 4); // bottom row + gpgpu.executeProgram(); + const result = gpgpu.downloadMatrixFromTexture(output, 4, 4); + const expected = + new Float32Array([2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2]); + test_util.expectArraysClose(result, expected, 0); + }); + + it('writes adjacent cells across multiple calls', () => { + for (let row = 0; row < 4; ++row) { + for (let col = 0; col < 4; ++col) { + gpgpu.setOutputMatrixWriteRegion(row, 1, col, 1); + gpgpu.executeProgram(); + } + } + const result = gpgpu.downloadMatrixFromTexture(output, 4, 4); + const expected = new Float32Array(4 * 4); + expected.fill(2); + test_util.expectArraysClose(result, expected, 0); + }); +}); + +describe('GPGPUContext', () => { + let gpgpu: GPGPUContext; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + }); + + afterEach(() => { + gpgpu.dispose(); + }); + + it('throws an error if used after dispose', () => { + const gpgpuContext = new GPGPUContext(); + gpgpuContext.dispose(); + expect(gpgpuContext.dispose).toThrowError(); + }); + + it('throws an error if validation is on and framebuffer incomplete', () => { + const src = `precision highp float; void main() {}`; + const program = gpgpu.createProgram(src); + const result = gpgpu.createMatrixTexture(1, 1); + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(program); + gpgpu.deleteMatrixTexture(result); + expect(gpgpu.executeProgram).toThrowError(); + gpgpu.deleteProgram(program); + }); +}); diff --git a/src/math/webgl/gpgpu_util.ts b/src/math/webgl/gpgpu_util.ts new file mode 100644 index 0000000000..4132e1e6fe --- /dev/null +++ b/src/math/webgl/gpgpu_util.ts @@ -0,0 +1,258 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as tex_util from './tex_util'; +import * as webgl_util from './webgl_util'; + +export function getWebGLContextAttributes(): WebGLContextAttributes { + return { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true + }; +} + +export function createWebGLContext(canvas?: HTMLCanvasElement) { + const attributes = getWebGLContextAttributes(); + let gl: WebGLRenderingContext; + if (canvas != null) { + gl = webgl_util.createWebGLRenderingContextFromCanvas(canvas, attributes); + } else { + gl = webgl_util.createWebGLRenderingContext(attributes); + } + webgl_util.callAndCheck(gl, () => gl.disable(gl.DEPTH_TEST)); + webgl_util.callAndCheck(gl, () => gl.disable(gl.STENCIL_TEST)); + webgl_util.callAndCheck(gl, () => gl.disable(gl.BLEND)); + webgl_util.callAndCheck(gl, () => gl.disable(gl.DITHER)); + webgl_util.callAndCheck(gl, () => gl.disable(gl.POLYGON_OFFSET_FILL)); + webgl_util.callAndCheck(gl, () => gl.disable(gl.SAMPLE_COVERAGE)); + webgl_util.callAndCheck(gl, () => gl.enable(gl.SCISSOR_TEST)); + webgl_util.callAndCheck(gl, () => gl.enable(gl.CULL_FACE)); + webgl_util.callAndCheck(gl, () => gl.cullFace(gl.BACK)); + return gl; +} + +export function createVertexShader(gl: WebGLRenderingContext): WebGLShader { + const vertexShaderSource = ` + precision highp float; + attribute vec3 clipSpacePos; + attribute vec2 uv; + varying vec2 resultUV; + + void main() { + gl_Position = vec4(clipSpacePos, 1); + resultUV = uv; + }`; + return webgl_util.createVertexShader(gl, vertexShaderSource); +} + +export function createVertexBuffer(gl: WebGLRenderingContext): WebGLBuffer { + // [x y z u v] * [upper-left, lower-left, upper-right, lower-right] + const vertexArray = new Float32Array( + [-1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0]); + return webgl_util.createStaticVertexBuffer(gl, vertexArray); +} + +export function createIndexBuffer(gl: WebGLRenderingContext): WebGLBuffer { + // OpenGL (and WebGL) have "CCW == front" winding + const triangleVertexIndices = new Uint16Array([0, 1, 2, 2, 1, 3]); + return webgl_util.createStaticIndexBuffer(gl, triangleVertexIndices); +} + +function getTextureInternalFormat( + gl: WebGLRenderingContext, numChannels: number): number { + if (webgl_util.isWebGL2Enabled()) { + if (numChannels === 4) { + // tslint:disable-next-line:no-any + return (gl as any).RGBA32F; + } + // tslint:disable-next-line:no-any + return (gl as any).R32F; + } + return gl.RGBA; +} + +function getTextureFormat( + gl: WebGLRenderingContext, numChannels: number): number { + if (webgl_util.isWebGL2Enabled() && numChannels === 1) { + // tslint:disable-next-line:no-any + return (gl as any).RED; + } + return gl.RGBA; +} + +function createAndConfigureTexture( + gl: WebGLRenderingContext, width: number, height: number, + numChannels: number): WebGLTexture { + webgl_util.validateTextureSize(gl, width, height); + const texture = webgl_util.createTexture(gl); + + const tex2d = gl.TEXTURE_2D; + const internalFormat = getTextureInternalFormat(gl, numChannels); + const format = getTextureFormat(gl, numChannels); + webgl_util.callAndCheck(gl, () => gl.bindTexture(tex2d, texture)); + webgl_util.callAndCheck( + gl, () => gl.texParameteri(tex2d, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)); + webgl_util.callAndCheck( + gl, () => gl.texParameteri(tex2d, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)); + webgl_util.callAndCheck( + gl, () => gl.texParameteri(tex2d, gl.TEXTURE_MIN_FILTER, gl.NEAREST)); + webgl_util.callAndCheck( + gl, () => gl.texParameteri(tex2d, gl.TEXTURE_MAG_FILTER, gl.NEAREST)); + webgl_util.callAndCheck( + gl, + () => gl.texImage2D( + tex2d, 0, internalFormat, width, height, 0, format, gl.FLOAT, null)); + webgl_util.callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, null)); + return texture; +} + +export function createMatrixTexture( + gl: WebGLRenderingContext, rows: number, columns: number): WebGLTexture { + const [width, height] = + tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns); + const numChannels = 1; + return createAndConfigureTexture(gl, width, height, numChannels); +} + +export function createColorMatrixTexture( + gl: WebGLRenderingContext, rows: number, columns: number): WebGLTexture { + const [width, height] = + tex_util.getColorMatrixTextureShapeWidthHeight(rows, columns); + const numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} + +export function createPackedMatrixTexture( + gl: WebGLRenderingContext, rows: number, columns: number): WebGLTexture { + const [width, height] = + tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); + const numChannels = 4; + return createAndConfigureTexture(gl, width, height, numChannels); +} + +export function bindVertexProgramAttributeStreams( + gl: WebGLRenderingContext, program: WebGLProgram, + vertexBuffer: WebGLBuffer) { + const posOffset = 0; // x is the first buffer element + const uvOffset = 3 * 4; // uv comes after [x y z] + const stride = (3 * 4) + (2 * 4); // xyz + uv, each entry is 4-byte float. + webgl_util.callAndCheck( + gl, () => gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)); + webgl_util.bindVertexBufferToProgramAttribute( + gl, program, 'clipSpacePos', vertexBuffer, 3, stride, posOffset); + try { + webgl_util.bindVertexBufferToProgramAttribute( + gl, program, 'uv', vertexBuffer, 2, stride, uvOffset); + } catch (e) { + // Programs with 1x1 output textures don't use the uv attribute. + // This can cause the shader linker to dead-strip it, so we shouldn't + // complain or fail if it's not present. + if (!e.hasOwnProperty('namedVertexAttributeNotFound')) { + throw e; + } + } +} + +export function uploadPixelDataToTexture( + gl: WebGLRenderingContext, texture: WebGLTexture, + pixels: ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement) { + const numChannels = 4; + const internalFormat = getTextureInternalFormat(gl, numChannels); + webgl_util.callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, texture)); + webgl_util.callAndCheck( + gl, + () => gl.texImage2D( + gl.TEXTURE_2D, 0, internalFormat, gl.RGBA, gl.FLOAT, pixels)); + webgl_util.callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, null)); +} + +function uploadDataToTexture( + gl: WebGLRenderingContext, texture: WebGLTexture, width: number, + height: number, data: Float32Array, numChannels: number) { + const textureFormat = getTextureFormat(gl, numChannels); + + webgl_util.validateTextureSize(gl, width, height); + webgl_util.callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, texture)); + webgl_util.callAndCheck( + gl, + () => gl.texSubImage2D( + gl.TEXTURE_2D, 0, 0, 0, width, height, textureFormat, gl.FLOAT, + data)); + webgl_util.callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, null)); +} + +export function uploadMatrixToTexture( + gl: WebGLRenderingContext, texture: WebGLTexture, rows: number, + columns: number, matrix: Float32Array, numChannels: number) { + const [w, h] = + tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns); + + const channelsPerTexture = + numChannels === 1 ? webgl_util.getChannelsPerTexture() : numChannels; + const unpackedArray = + new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize( + matrix.length, channelsPerTexture)); + tex_util.encodeMatrixToUnpackedArray( + matrix, unpackedArray, channelsPerTexture); + + uploadDataToTexture(gl, texture, w, h, unpackedArray, numChannels); +} + +export function uploadMatrixToPackedTexture( + gl: WebGLRenderingContext, texture: WebGLTexture, rows: number, + columns: number, matrix: Float32Array) { + const [w, h] = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); + const packedRGBA = new Float32Array( + tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + tex_util.encodeMatrixToPackedRGBA(matrix, rows, columns, packedRGBA); + const numChannels = 4; + uploadDataToTexture(gl, texture, w, h, packedRGBA, numChannels); +} + +export function downloadMatrixFromOutputTexture( + gl: WebGLRenderingContext, rows: number, columns: number): Float32Array { + const [w, h] = + tex_util.getUnpackedMatrixTextureShapeWidthHeight(rows, columns); + + const channelsPerTexture = 4; + const unpackedArray = + new Float32Array(tex_util.getUnpackedArraySizeFromMatrixSize( + rows * columns, channelsPerTexture)); + const textureFormat = getTextureFormat(gl, channelsPerTexture); + + webgl_util.callAndCheck( + gl, () => gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, unpackedArray)); + + const matrix = new Float32Array(rows * columns); + tex_util.decodeMatrixFromUnpackedArray( + unpackedArray, matrix, channelsPerTexture); + return matrix; +} + +export function downloadMatrixFromPackedOutputTexture( + gl: WebGLRenderingContext, rows: number, columns: number): Float32Array { + const [w, h] = tex_util.getPackedMatrixTextureShapeWidthHeight(rows, columns); + const packedRGBA = new Float32Array( + tex_util.getPackedRGBAArraySizeFromMatrixShape(rows, columns)); + webgl_util.callAndCheck( + gl, () => gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, packedRGBA)); + const matrix = new Float32Array(rows * columns); + return tex_util.decodeMatrixFromPackedRGBA(packedRGBA, rows, columns, matrix); +} diff --git a/src/math/webgl/gpgpu_util_test.ts b/src/math/webgl/gpgpu_util_test.ts new file mode 100644 index 0000000000..b7499d076e --- /dev/null +++ b/src/math/webgl/gpgpu_util_test.ts @@ -0,0 +1,117 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as gpgpu_util from './gpgpu_util'; + +describe('gpgpu_util createWebGLContext', () => { + let gpgpu: GPGPUContext; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + }); + + afterEach(() => { + gpgpu.dispose(); + }); + + it('disables DEPTH_TEST and STENCIL_TEST', () => { + expect(gpgpu.gl.getParameter(gpgpu.gl.DEPTH_TEST)).toEqual(false); + expect(gpgpu.gl.getParameter(gpgpu.gl.STENCIL_TEST)).toEqual(false); + }); + + it('disables BLEND', () => { + expect(gpgpu.gl.getParameter(gpgpu.gl.BLEND)).toEqual(false); + }); + + it('disables DITHER, POLYGON_OFFSET_FILL', () => { + expect(gpgpu.gl.getParameter(gpgpu.gl.DITHER)).toEqual(false); + expect(gpgpu.gl.getParameter(gpgpu.gl.POLYGON_OFFSET_FILL)).toEqual(false); + }); + + it('enables CULL_FACE with BACK', () => { + expect(gpgpu.gl.getParameter(gpgpu.gl.CULL_FACE)).toEqual(true); + expect(gpgpu.gl.getParameter(gpgpu.gl.CULL_FACE_MODE)) + .toEqual(gpgpu.gl.BACK); + }); + + it('enables SCISSOR_TEST', () => { + expect(gpgpu.gl.getParameter(gpgpu.gl.SCISSOR_TEST)).toEqual(true); + }); +}); + +describe('gpgpu_util createMatrixTexture', () => { + it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => { + const gpgpu = new GPGPUContext(); + const tex = gpgpu_util.createMatrixTexture(gpgpu.gl, 32, 32); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); + expect( + gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_S)) + .toEqual(gpgpu.gl.CLAMP_TO_EDGE); + expect( + gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_T)) + .toEqual(gpgpu.gl.CLAMP_TO_EDGE); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + gpgpu.deleteMatrixTexture(tex); + gpgpu.dispose(); + }); + + it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => { + const gpgpu = new GPGPUContext(); + const tex = gpgpu_util.createMatrixTexture(gpgpu.gl, 32, 32); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); + expect(gpgpu.gl.getTexParameter( + gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MIN_FILTER)) + .toEqual(gpgpu.gl.NEAREST); + expect(gpgpu.gl.getTexParameter( + gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MAG_FILTER)) + .toEqual(gpgpu.gl.NEAREST); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + gpgpu.deleteMatrixTexture(tex); + gpgpu.dispose(); + }); +}); + +describe('gpgpu_util createPackedMatrixTexture', () => { + it('sets the TEXTURE_WRAP S+T parameters to CLAMP_TO_EDGE', () => { + const gpgpu = new GPGPUContext(); + const tex = gpgpu_util.createPackedMatrixTexture(gpgpu.gl, 32, 32); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); + expect( + gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_S)) + .toEqual(gpgpu.gl.CLAMP_TO_EDGE); + expect( + gpgpu.gl.getTexParameter(gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_WRAP_T)) + .toEqual(gpgpu.gl.CLAMP_TO_EDGE); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + gpgpu.deleteMatrixTexture(tex); + gpgpu.dispose(); + }); + + it('sets the TEXTURE_[MIN|MAG]_FILTER parameters to NEAREST', () => { + const gpgpu = new GPGPUContext(); + const tex = gpgpu_util.createPackedMatrixTexture(gpgpu.gl, 32, 32); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, tex); + expect(gpgpu.gl.getTexParameter( + gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MIN_FILTER)) + .toEqual(gpgpu.gl.NEAREST); + expect(gpgpu.gl.getTexParameter( + gpgpu.gl.TEXTURE_2D, gpgpu.gl.TEXTURE_MAG_FILTER)) + .toEqual(gpgpu.gl.NEAREST); + gpgpu.gl.bindTexture(gpgpu.gl.TEXTURE_2D, null); + gpgpu.deleteMatrixTexture(tex); + gpgpu.dispose(); + }); +}); diff --git a/src/math/webgl/log_gpu.ts b/src/math/webgl/log_gpu.ts new file mode 100644 index 0000000000..19426bc338 --- /dev/null +++ b/src/math/webgl/log_gpu.ts @@ -0,0 +1,36 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getLogUnaryOp(): string { + return 'gl_FragColor = vec4(log(value), 0, 0, 0);'; +} + +export function getFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getLogUnaryOp()); +} + +export function log( + gpgpu: GPGPUContext, logProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, logProgram, a, rows, columns, result); +} + +export function uploadLogDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getLogUnaryOp()); +} diff --git a/src/math/webgl/log_gpu_test.ts b/src/math/webgl/log_gpu_test.ts new file mode 100644 index 0000000000..c140046cd2 --- /dev/null +++ b/src/math/webgl/log_gpu_test.ts @@ -0,0 +1,50 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as log_gpu from './log_gpu'; + +describe('log_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(23 * 32); + const result = log_gpu.uploadLogDownload(a, 23, 32); + expect(result.length).toEqual(a.length); + }); + + it('returns 1.0 when the only value in a 1x1 matrix is e', () => { + const a = new Float32Array([Math.E]); + const result = log_gpu.uploadLogDownload(a, 1, 1); + expect(result[0]).toBeCloseTo(1.0); + }); + + it('operates on every value in a matrix', () => { + const a = new Float32Array(6); + a.fill(Math.E); + const result = log_gpu.uploadLogDownload(a, 1, a.length); + const expected = new Float32Array(a.length); + expected.fill(1.0); + test_util.expectArraysClose(result, expected, 0.0001); + }); + + it('calculates f(x)=ln x for every value in the matrix', () => { + const a = new Float32Array([0.5, 1, 2, 3]); + const result = log_gpu.uploadLogDownload(a, 1, a.length); + const expected = new Float32Array(a.length); + for (let i = 0; i < a.length; ++i) { + expected[i] = Math.log(a[i]); + } + test_util.expectArraysClose(result, expected, 0.0001); + }); +}); diff --git a/src/math/webgl/logsumexp_gpu.ts b/src/math/webgl/logsumexp_gpu.ts new file mode 100644 index 0000000000..b3e832e67d --- /dev/null +++ b/src/math/webgl/logsumexp_gpu.ts @@ -0,0 +1,73 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource(rows: number, columns: number): string { + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 resultUV; + + const vec2 aDimCR = vec2(${columns}.0, ${rows}.0); + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + float aMax = texture2D(matrixA, halfCR / aDimCR).r; + for (float r = 0.0; r < aDimCR.y; r += 1.0) { + for (float c = 0.0; c < aDimCR.x; c += 1.0) { + vec2 uv = (vec2(c, r) + halfCR) / aDimCR; + float aCur = texture2D(matrixA, uv).r; + aMax = max(aMax, aCur); + } + } + + float expSum = 0.0; + for (float r = 0.0; r < aDimCR.y; r += 1.0) { + for (float c = 0.0; c < aDimCR.x; c += 1.0) { + vec2 uv = (vec2(c, r) + halfCR) / aDimCR; + float aCur = texture2D(matrixA, uv).r; + expSum += exp(aCur - aMax); + } + } + + gl_FragColor = vec4(aMax + log(expSum), 0, 0, 0); + }`; +} + +export function logSumExp( + gpgpu: GPGPUContext, logSumExpProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(logSumExpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} + +export function uploadLogSumExpDownload( + a: Float32Array, rows: number, columns: number): number { + const gpgpu = new GPGPUContext(); + const program = gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + const aTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + logSumExp(gpgpu, program, aTexture, rows, columns, resultTexture); + const result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result[0]; +} diff --git a/src/math/webgl/logsumexp_gpu_test.ts b/src/math/webgl/logsumexp_gpu_test.ts new file mode 100644 index 0000000000..2c5cc3d25c --- /dev/null +++ b/src/math/webgl/logsumexp_gpu_test.ts @@ -0,0 +1,54 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as logsumexp_gpu from './logsumexp_gpu'; + +function cpuLogSumExp(m: Float32Array): number { + if (m.length === 0) { + throw new Error('m must have length greater than zero.'); + } + let mMax = m[0]; + for (let i = 0; i < m.length; ++i) { + mMax = Math.max(mMax, m[i]); + } + let expSum = 0; + for (let i = 0; i < m.length; ++i) { + expSum += Math.exp(m[i] - mMax); + } + const logSumExp = mMax + Math.log(expSum); + return logSumExp; +} + +describe('logsumexp_gpu', () => { + it('returns 0 (ln(1) = 0) when the 1x1 input matrix is [0]', () => { + const a = new Float32Array([0]); + const result = logsumexp_gpu.uploadLogSumExpDownload(a, 1, 1); + expect(result).toEqual(0); + }); + + it('returns ln(length) when the input matrix is [0]', () => { + const a = new Float32Array(512 * 512); + const result = logsumexp_gpu.uploadLogSumExpDownload(a, 512, 512); + expect(result).toBeCloseTo(Math.log(a.length)); + }); + + it('computes the same result as cpuLogSumExp', () => { + const a = test_util.randomArrayInRange(12 * 29, -2, 2); + const result = logsumexp_gpu.uploadLogSumExpDownload(a, 12, 29); + const expected = cpuLogSumExp(a); + expect(result).toBeCloseTo(expected); + }); +}); diff --git a/src/math/webgl/max_pool_backprop_gpu.ts b/src/math/webgl/max_pool_backprop_gpu.ts new file mode 100644 index 0000000000..97c2e59c33 --- /dev/null +++ b/src/math/webgl/max_pool_backprop_gpu.ts @@ -0,0 +1,101 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderMaxPoolBackprop( + dyShapeRCD: [number, number, number], fSize: number, origStride: number, + origPad: number) { + const origInputDepth = dyShapeRCD[2]; + const pad = fSize - 1 - origPad; + const [dyRows, dyCols, depth] = dyShapeRCD; + + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dyShapeRCD); + + return ` + precision highp float; + uniform sampler2D dy; + uniform sampler2D maxPos; + + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 dyShapeCR = vec2(${dyTexShapeRC[1]}, ${dyTexShapeRC[0]}); + + void main() { + vec2 dxTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (dxTexR, dxTexC) to 3D (dxR, dxC, d). + float dxR = dxTexCR.y; + float dxC = floor(dxTexCR.x / ${origInputDepth}.0); + float d = mod(dxTexCR.x, ${origInputDepth}.0); + + vec2 dyRCCorner = vec2(dxR, dxC) - vec2(${pad}.0, ${pad}.0); + float dyRCorner = dyRCCorner.x; + float dyCCorner = dyRCCorner.y; + + // Convolve dy(?, ?, d) with pos mask(:, :, d) to get dx(yR, dxC, d). + // ? = to be determined. : = across all values in that axis. + float dotProd = 0.0; + for (float wR = 0.0; wR < ${fSize}.0; wR += 1.0) { + + float dyR = (dyRCorner + wR) / ${origStride}.0; + // TODO(nsthorat): Splice this with another version where you call + // getMatrixValueOrZeroPad(). Here and below. + if (dyR < 0.0 || dyR >= ${dyRows}.0 || fract(dyR) > 0.0) { + continue; + } + + float dyTexR = dyR; + + for (float wC = 0.0; wC < ${fSize}.0; wC += 1.0) { + + float dyC = (dyCCorner + wC) / ${origStride}.0; + if (dyC < 0.0 || dyC >= ${dyCols}.0 || fract(dyC) > 0.0) { + continue; + } + + float dyTexC = dyC * ${depth}.0 + d; + + // Read dy(dyR, dyC, d). + vec2 dyUV = (vec2(dyTexC, dyTexR) + halfCR) / dyShapeCR; + float dyValue = texture2D(dy, dyUV).r; + + // Read maxPos(dyR, dyC, d). + float maxPosValue = + ${fSize * fSize - 1}.0 - texture2D(maxPos, dyUV).r; + + // Get the current value, check it against the value from the + // position matrix. + float curPosValue = wR * ${fSize}.0 + wC; + float mask = float(maxPosValue == curPosValue ? 1.0 : 0.0); + + dotProd += dyValue * mask; + } + } + gl_FragColor = vec4(dotProd, 0, 0, 0); + }`; +} + +export function maxPoolBackprop( + gpgpu: GPGPUContext, program: WebGLProgram, dyTex: WebGLTexture, + maxPositionsTex: WebGLTexture, resultTex: WebGLTexture, + resultTexShapeRC: [number, number]) { + gpgpu.setOutputMatrixTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(dyTex, 'dy', 0); + gpgpu.setInputMatrixTexture(maxPositionsTex, 'maxPos', 1); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/max_pool_backprop_gpu_test.ts b/src/math/webgl/max_pool_backprop_gpu_test.ts new file mode 100644 index 0000000000..96bc896e9b --- /dev/null +++ b/src/math/webgl/max_pool_backprop_gpu_test.ts @@ -0,0 +1,141 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MaxPool} from '../../ops/max_pool'; +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as max_pool_backprop_gpu from './max_pool_backprop_gpu'; +import * as max_pool_gpu from './max_pool_gpu'; + +describe('max_pool_backprop_gpu', () => { + + function uploadMaxPoolBackpropDownload( + dy: Array3D, x: Array3D, fSize: number, origStride: number, + origPad: number): Float32Array { + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const depth = dy.shape[2]; + const src = max_pool_backprop_gpu.getFragmentShaderMaxPoolBackprop( + dy.shape, fSize, origStride, origPad); + const program = gpgpu.createProgram(src); + + // Upload dy. + const dyTexShapeRC = conv_util.computeTexShapeFrom3D(dy.shape); + const dyTex = gpgpu.createMatrixTexture(dyTexShapeRC[0], dyTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + dyTex, dyTexShapeRC[0], dyTexShapeRC[1], dy.getValues()); + + // Upload x. + const xTexShapeRC = conv_util.computeTexShapeFrom3D(x.shape); + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + xTex, xTexShapeRC[0], xTexShapeRC[1], x.getValues()); + + // Compute max positions. + const maxPoolResultShape = conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], origStride, origPad); + const maxPoolResultTexShape = + conv_util.computeTexShapeFrom3D(maxPoolResultShape); + const maxPoolPositionsResultTex = gpgpu.createMatrixTexture( + maxPoolResultTexShape[0], maxPoolResultTexShape[1]); + const maxPoolPositionsSrc = + max_pool_gpu.getFragmentShaderMaxPoolPositionsSource( + x.shape, fSize, origStride, origPad); + const maxPoolPositionsProgram = gpgpu.createProgram(maxPoolPositionsSrc); + max_pool_gpu.maxPoolCommon( + gpgpu, maxPoolPositionsProgram, xTex, maxPoolPositionsResultTex, + maxPoolResultTexShape); + + // Figure out the output shape by dilating the input. + const dyRowsDilated = (dy.shape[0] - 1) * origStride + 1; + const dyColsDilated = (dy.shape[1] - 1) * origStride + 1; + const pad = fSize - 1 - origPad; + const resultShapeRCD = conv_util.computeOutputShape3D( + [dyRowsDilated, dyColsDilated, depth], fSize, depth, 1, pad); + const resultTexRC = conv_util.computeTexShapeFrom3D(resultShapeRCD); + const resultTex = gpgpu.createMatrixTexture(resultTexRC[0], resultTexRC[1]); + max_pool_backprop_gpu.maxPoolBackprop( + gpgpu, program, dyTex, maxPoolPositionsResultTex, resultTex, + resultTexRC); + const y = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexRC[0], resultTexRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(dyTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return y; + } + + function compareToCPU( + dyShape: [number, number, number], xShape: [number, number, number], + fSize: number, origStride: number, origPad: number) { + const dy = NDArray.randNormal(dyShape); + const x = NDArray.randNormal(xShape); + + const mathCPU = new NDArrayMathCPU(); + const dxCPU = mathCPU.maxPoolBackprop(dy, x, fSize, origStride, origPad); + const dxGPU = + uploadMaxPoolBackpropDownload(dy, x, fSize, origStride, origPad); + test_util.expectArraysClose(dxGPU, dxCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const depth = 1; + const dyShape: [number, number, number] = [8, 8, depth]; + const xShape: [number, number, number] = [9, 9, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(dyShape, xShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=1,f=3,s=2,p=1', () => { + const depth = 1; + const dyShape: [number, number, number] = [7, 7, depth]; + const xShape: [number, number, number] = [13, 13, depth]; + const fSize = 3; + const stride = 2; + const zeroPad = 1; + compareToCPU(dyShape, xShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=4,f=2,s=1,p=0', () => { + const depth = 4; + const dyShape: [number, number, number] = [8, 8, depth]; + const xShape: [number, number, number] = [9, 9, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(dyShape, xShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=3,f=3,s=3,p=0', () => { + const depth = 3; + const dyShape: [number, number, number] = [7, 7, depth]; + const xShape: [number, number, number] = [21, 21, depth]; + const fSize = 3; + const stride = 3; + const zeroPad = 0; + compareToCPU(dyShape, xShape, fSize, stride, zeroPad); + }); +}); diff --git a/src/math/webgl/max_pool_gpu.ts b/src/math/webgl/max_pool_gpu.ts new file mode 100644 index 0000000000..7cac3f68fc --- /dev/null +++ b/src/math/webgl/max_pool_gpu.ts @@ -0,0 +1,44 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as pool_gpu from './pool_gpu'; + +export function getFragmentShaderMaxPoolPositionsSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number) { + return getFragmentShaderMaxPoolCommonSource( + xShapeRCD, fSize, stride, pad, true); +} + +export function getFragmentShaderMaxPoolSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number) { + return getFragmentShaderMaxPoolCommonSource( + xShapeRCD, fSize, stride, pad, false); +} + +function getFragmentShaderMaxPoolCommonSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number, computeMaxPositions: boolean) { + return pool_gpu.getFragmentShaderPoolCommonSource( + xShapeRCD, fSize, stride, pad, 'max', computeMaxPositions); +} + +export function maxPoolCommon( + gpgpu: GPGPUContext, program: WebGLProgram, x: WebGLTexture, + result: WebGLTexture, resultShapeRowCol: [number, number]) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} \ No newline at end of file diff --git a/src/math/webgl/max_pool_gpu_test.ts b/src/math/webgl/max_pool_gpu_test.ts new file mode 100644 index 0000000000..7c3dcb2817 --- /dev/null +++ b/src/math/webgl/max_pool_gpu_test.ts @@ -0,0 +1,114 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MaxPool} from '../../ops/max_pool'; +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as max_pool_gpu from './max_pool_gpu'; + +describe('max_pool_gpu', () => { + function uploadMaxPoolDownload( + a: Float32Array, aShapeRowColDepth: [number, number, number], + fieldSize: number, stride: number, zeroPad: number): Float32Array { + const aTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + aShapeRowColDepth, fieldSize, aShapeRowColDepth[2], stride, + zeroPad); + + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = max_pool_gpu.getFragmentShaderMaxPoolSource( + aShapeRowColDepth, fieldSize, stride, zeroPad); + const program = gpgpu.createProgram(shaderSource); + + const aTex = gpgpu.createMatrixTexture(aTexShapeRC[0], aTexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(aTex, aTexShapeRC[0], aTexShapeRC[1], a); + + max_pool_gpu.maxPoolCommon( + gpgpu, program, aTex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + function compareToCPU( + xShape: [number, number, number], fSize: number, stride: number, + pad: number) { + const x = NDArray.randNormal(xShape); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = mathCPU.maxPool(x, fSize, stride, pad); + const yGPU = + uploadMaxPoolDownload(x.getValues(), x.shape, fSize, stride, pad); + + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const depth = 1; + const dyShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(dyShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=1,f=3,s=2,p=1', () => { + const depth = 1; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=4,f=2,s=1,p=0', () => { + const depth = 4; + const inputShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=3,f=3,s=3,p=1', () => { + const depth = 3; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); +}); diff --git a/src/math/webgl/max_pool_positions_gpu_test.ts b/src/math/webgl/max_pool_positions_gpu_test.ts new file mode 100644 index 0000000000..86f3a8a390 --- /dev/null +++ b/src/math/webgl/max_pool_positions_gpu_test.ts @@ -0,0 +1,110 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as max_pool_gpu from './max_pool_gpu'; + +describe('max_pool_position', () => { + function uploadMaxPoolPositionDownload( + x: Float32Array, xShapeRowColDepth: [number, number, number], + fieldSize: number, stride: number, pad: number): Float32Array { + const xTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(xShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + xShapeRowColDepth, fieldSize, xShapeRowColDepth[2], stride, pad); + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = max_pool_gpu.getFragmentShaderMaxPoolPositionsSource( + xShapeRowColDepth, fieldSize, stride, pad); + const program = gpgpu.createProgram(shaderSource); + + const xTex = gpgpu.createMatrixTexture(xTexShapeRC[0], xTexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(xTex, xTexShapeRC[0], xTexShapeRC[1], x); + + max_pool_gpu.maxPoolCommon( + gpgpu, program, xTex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(xTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + function compareToCPU( + xShape: [number, number, number], fSize: number, stride: number, + pad: number) { + const x = NDArray.randNormal(xShape); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = mathCPU.maxPoolPositions(x, fSize, stride, pad); + const yGPU = uploadMaxPoolPositionDownload( + x.getValues(), x.shape, fSize, stride, pad); + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const depth = 1; + const dyShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const pad = 0; + compareToCPU(dyShape, fSize, stride, pad); + }); + + it('matches CPU on random input, d=1,f=3,s=2,p=1', () => { + const depth = 1; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 2; + const pad = 1; + compareToCPU(inputShape, fSize, stride, pad); + }); + + it('matches CPU on random input, d=4,f=2,s=1,p=0', () => { + const depth = 4; + const inputShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const pad = 0; + compareToCPU(inputShape, fSize, stride, pad); + }); + + it('matches CPU on random input, d=3,f=3,s=3,p=1', () => { + const depth = 3; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 3; + const pad = 1; + compareToCPU(inputShape, fSize, stride, pad); + }); +}); diff --git a/src/math/webgl/min_pool_gpu.ts b/src/math/webgl/min_pool_gpu.ts new file mode 100644 index 0000000000..f3b8888356 --- /dev/null +++ b/src/math/webgl/min_pool_gpu.ts @@ -0,0 +1,30 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as pool_gpu from './pool_gpu'; + +export function getFragmentShaderMinPoolSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number) { + return pool_gpu.getFragmentShaderPoolCommonSource( + xShapeRCD, fSize, stride, pad, 'min', false); +} + +export function minPool( + gpgpu: GPGPUContext, program: WebGLProgram, x: WebGLTexture, + result: WebGLTexture, resultShapeRowCol: [number, number]) { + pool_gpu.poolCommon(gpgpu, program, x, result, resultShapeRowCol); +} \ No newline at end of file diff --git a/src/math/webgl/min_pool_gpu_test.ts b/src/math/webgl/min_pool_gpu_test.ts new file mode 100644 index 0000000000..1911c649a4 --- /dev/null +++ b/src/math/webgl/min_pool_gpu_test.ts @@ -0,0 +1,112 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as min_pool_gpu from './min_pool_gpu'; + +describe('min_pool_gpu', () => { + function uploadMinPoolDownload( + a: Float32Array, aShapeRowColDepth: [number, number, number], + fieldSize: number, stride: number, zeroPad: number): Float32Array { + const aTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = + conv_util.computeOutputShape3D( + aShapeRowColDepth, fieldSize, aShapeRowColDepth[2], stride, + zeroPad); + + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = min_pool_gpu.getFragmentShaderMinPoolSource( + aShapeRowColDepth, fieldSize, stride, zeroPad); + const program = gpgpu.createProgram(shaderSource); + + const aTex = gpgpu.createMatrixTexture(aTexShapeRC[0], aTexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(aTex, aTexShapeRC[0], aTexShapeRC[1], a); + + min_pool_gpu.minPool(gpgpu, program, aTex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + function compareToCPU( + xShape: [number, number, number], fSize: number, stride: number, + pad: number) { + const x = NDArray.randNormal(xShape); + + const mathCPU = new NDArrayMathCPU(); + const yCPU = mathCPU.minPool(x, fSize, stride, pad); + const yGPU = + uploadMinPoolDownload(x.getValues(), x.shape, fSize, stride, pad); + + test_util.expectArraysClose(yGPU, yCPU.getValues(), 1e-5); + } + + it('matches CPU on random input, d1=1,d2=1,f=2,s=1,p=0', () => { + const depth = 1; + const dyShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(dyShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=1,f=3,s=2,p=1', () => { + const depth = 1; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 2; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=4,f=2,s=1,p=0', () => { + const depth = 4; + const inputShape: [number, number, number] = [8, 8, depth]; + const fSize = 2; + const stride = 1; + const zeroPad = 0; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); + + it('matches CPU on random input, d=3,f=3,s=3,p=1', () => { + const depth = 3; + const inputShape: [number, number, number] = [7, 7, depth]; + const fSize = 3; + const stride = 3; + const zeroPad = 1; + compareToCPU(inputShape, fSize, stride, zeroPad); + }); +}); \ No newline at end of file diff --git a/src/math/webgl/minmax_gpu.ts b/src/math/webgl/minmax_gpu.ts new file mode 100644 index 0000000000..69c9a57f94 --- /dev/null +++ b/src/math/webgl/minmax_gpu.ts @@ -0,0 +1,66 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import {IS_NAN_SHADER_FUNC} from './webgl_util'; + +function getFragmentShaderSource( + rows: number, columns: number, compOp: string): string { + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 outputColumnRow; + + const vec2 aDimCR = vec2(${columns}.0, ${rows}.0); + const vec2 halfCR = vec2(0.5, 0.5); + + ${IS_NAN_SHADER_FUNC} + + void main() { + float value = texture2D(matrixA, halfCR / aDimCR).r; + for (float r = 0.0; r < aDimCR.y; r += 1.0) { + for (float c = 0.0; c < aDimCR.x; c += 1.0) { + vec2 cr = vec2(c, r); + vec2 uv = (cr + halfCR) / aDimCR; + float candidate = texture2D(matrixA, uv).r; + if (isNaN(candidate)) { + gl_FragColor = vec4(candidate, 0, 0, 0); + return; + } + value = ${compOp}(value, candidate); + } + } + gl_FragColor = vec4(value, 0, 0, 0); + }`; +} + +export function getMinFragmentShaderSource( + rows: number, columns: number): string { + return getFragmentShaderSource(rows, columns, 'min'); +} + +export function getMaxFragmentShaderSource( + rows: number, columns: number): string { + return getFragmentShaderSource(rows, columns, 'max'); +} + +export function minMax( + gpgpu: GPGPUContext, minMaxProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(minMaxProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/minmax_gpu_test.ts b/src/math/webgl/minmax_gpu_test.ts new file mode 100644 index 0000000000..c9f05cee2c --- /dev/null +++ b/src/math/webgl/minmax_gpu_test.ts @@ -0,0 +1,103 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as minmax_gpu from './minmax_gpu'; + +function uploadMinMaxDownloadDriver( + a: Float32Array, rows: number, columns: number, + fragmentShaderSource: string): Float32Array { + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = gpgpu.createProgram(fragmentShaderSource); + const aTexture: WebGLTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + minmax_gpu.minMax(gpgpu, program, aTexture, rows, columns, resultTexture); + const result = new Float32Array(4); + gpgpu.gl.readPixels(0, 0, 1, 1, gpgpu.gl.RGBA, gpgpu.gl.FLOAT, result); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} + +function uploadMinDownload( + a: Float32Array, rows: number, columns: number): number { + const src = minmax_gpu.getMinFragmentShaderSource(rows, columns); + const result = uploadMinMaxDownloadDriver(a, rows, columns, src); + return result[0]; +} + +function uploadMaxDownload( + a: Float32Array, rows: number, columns: number): number { + const src = minmax_gpu.getMaxFragmentShaderSource(rows, columns); + const result = uploadMinMaxDownloadDriver(a, rows, columns, src); + return result[0]; +} + +describe('minmax_gpu min', () => { + it('returns the only value in a 1x1 input matrix', () => { + const a = new Float32Array([3.141]); + const minValue = uploadMinDownload(a, 1, 1); + expect(minValue).toEqual(a[0]); + }); + + it('returns min value from the first cell of a 2x1', () => { + const a = new Float32Array([-100, 100]); + const minValue = uploadMinDownload(a, 2, 1); + expect(minValue).toEqual(a[0]); + }); + + it('returns min value from the second cell of a 2x1', () => { + const a = new Float32Array([100, -1.234]); + const minValue = uploadMinDownload(a, 2, 1); + expect(minValue).toEqual(a[1]); + }); + + it('finds the min value of a large array', () => { + const a = new Float32Array(1024 * 1024); + a[a.length - 91] = -0.1; + const minValue = uploadMinDownload(a, 1024, 1024); + expect(minValue).toBeCloseTo(-0.1); + }); +}); + +describe('minmax_gpu max', () => { + it('returns the only value in a 1x1 input matrix', () => { + const a = new Float32Array([3.141]); + const maxValue = uploadMaxDownload(a, 1, 1); + expect(maxValue).toEqual(a[0]); + }); + + it('returns max value from the first cell of a 2x1', () => { + const a = new Float32Array([100, -100]); + const maxValue = uploadMaxDownload(a, 2, 1); + expect(maxValue).toEqual(a[0]); + }); + + it('returns max value from the second cell of a 2x1', () => { + const a = new Float32Array([-1.234, 100]); + const maxValue = uploadMaxDownload(a, 2, 1); + expect(maxValue).toEqual(a[1]); + }); + + it('finds the max value of a large array', () => { + const a = new Float32Array(1024 * 1024); + a[a.length - 91] = 0.1; + const maxValue = uploadMaxDownload(a, 1024, 1024); + expect(maxValue).toBeCloseTo(0.1); + }); +}); diff --git a/src/math/webgl/mulbcast_gpu.ts b/src/math/webgl/mulbcast_gpu.ts new file mode 100644 index 0000000000..8780720d0d --- /dev/null +++ b/src/math/webgl/mulbcast_gpu.ts @@ -0,0 +1,90 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + aNumRows: number, aNumCols: number, bNumRows: number, bNumCols: number, + resultNumRows: number, resultNumCols: number): string { + return ` + precision highp float; + uniform sampler2D matrixA; + uniform sampler2D matrixB; + varying vec2 resultUV; + + const vec2 aDimCR = vec2(${aNumCols}.0, ${aNumRows}.0); + const vec2 bDimCR = vec2(${bNumCols}.0, ${bNumRows}.0); + const vec2 resultDimCR = vec2(${resultNumCols}.0, ${resultNumRows}.0); + const vec4 halfCR = vec4(0.5, 0.5, 0.5, 0.5); + + void main() { + vec2 resultCR = floor(resultUV * resultDimCR); + vec4 resultCRBroadcast = vec4(resultCR, resultCR); + vec4 abDimsCR = vec4(aDimCR, bDimCR); + vec4 abCR = mod(resultCRBroadcast, abDimsCR); + vec4 abCRCenters = abCR + halfCR; + vec4 abUV = abCRCenters / abDimsCR; + vec4 a = texture2D(matrixA, abUV.rg); + vec4 b = texture2D(matrixB, abUV.ba); + float product = a.r * b.r; + gl_FragColor = vec4(product, 0, 0, 0); + }`; +} + +export function multiplyBroadcast( + gpgpu: GPGPUContext, multiplyBroadcastProgram: WebGLProgram, + a: WebGLTexture, aNumRows: number, aNumCols: number, b: WebGLTexture, + bNumRows: number, bNumCols: number, result: WebGLTexture, + resultNumRows: number, resultNumCols: number) { + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(multiplyBroadcastProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} + +export function uploadMultiplyBroadcastDownload( + a: Float32Array, aNumRows: number, aNumCols: number, b: Float32Array, + bNumRows: number, bNumCols: number): Float32Array { + const resultNumRows = Math.max(aNumRows, bNumRows); + const resultNumCols = Math.max(aNumCols, bNumCols); + + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = gpgpu.createProgram(getFragmentShaderSource( + aNumRows, aNumCols, bNumRows, bNumCols, resultNumRows, resultNumCols)); + + const aTexture: WebGLTexture = gpgpu.createMatrixTexture(aNumRows, aNumCols); + const bTexture: WebGLTexture = gpgpu.createMatrixTexture(bNumRows, bNumCols); + const resultTexture: WebGLTexture = + gpgpu.createMatrixTexture(resultNumRows, resultNumCols); + + gpgpu.uploadMatrixToTexture(aTexture, aNumRows, aNumCols, a); + gpgpu.uploadMatrixToTexture(bTexture, bNumRows, bNumCols, b); + + multiplyBroadcast( + gpgpu, program, aTexture, aNumRows, aNumCols, bTexture, bNumRows, + bNumCols, resultTexture, resultNumRows, resultNumCols); + + const result = gpgpu.downloadMatrixFromTexture( + resultTexture, resultNumRows, resultNumCols); + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return result; +} diff --git a/src/math/webgl/mulbcast_gpu_test.ts b/src/math/webgl/mulbcast_gpu_test.ts new file mode 100644 index 0000000000..e32c50179e --- /dev/null +++ b/src/math/webgl/mulbcast_gpu_test.ts @@ -0,0 +1,140 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as mulbcast_gpu from './mulbcast_gpu'; + +export function cpuMultiplyBroadcast( + a: Float32Array, aNumRows: number, aNumCols: number, b: Float32Array, + bNumRows: number, bNumCols: number): Float32Array { + const resultNumRows = Math.max(aNumRows, bNumRows); + const resultNumCols = Math.max(aNumCols, bNumCols); + const result = new Float32Array(resultNumRows * resultNumCols); + let dst = 0; + for (let r = 0; r < resultNumRows; ++r) { + for (let c = 0; c < resultNumCols; ++c) { + const ai = ((r % aNumRows) * aNumCols) + (c % aNumCols); + const bi = ((r % bNumRows) * bNumCols) + (c % bNumCols); + result[dst] = a[ai] * b[bi]; + ++dst; + } + } + return result; +} + +describe('mulbcast_gpu', () => { + it('returns a matrix dimensions [max(aRows, bRows), max(aCols, bCols)]', + () => { + const a = new Float32Array(13 * 100); + const b = new Float32Array(100 * 99); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 1, 100, b, 100, 1); + expect(result.length).toEqual(100 * 100); + }); + + it('returns [0] when A is [0], A and B same size', () => { + const a = new Float32Array(16 * 16); + const b = test_util.randomArrayInRange(16 * 16, -10, 10); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 16, 16, b, 16, 16); + test_util.expectArraysClose(a, result, 0.00001); + }); + + it('returns [0] when B is [0], A and B same size', () => { + const a = test_util.randomArrayInRange(16 * 16, -10, 10); + const b = new Float32Array(16 * 16); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 16, 16, b, 16, 16); + test_util.expectArraysClose(b, result, 0.00001); + }); + + it('returns A when B is [1] and matrices have the same size', () => { + const a = new Float32Array(16 * 16); + a.fill(1); + const b = test_util.randomArrayInRange(16 * 16, -10, 10); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 16, 16, b, 16, 16); + test_util.expectArraysClose(result, b, 0.00001); + }); + + it('returns B when A is [1] and matrices have the same size', () => { + const a = test_util.randomArrayInRange(16 * 16, -10, 10); + const b = new Float32Array(16 * 16); + b.fill(1); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 16, 16, b, 16, 16); + test_util.expectArraysClose(result, a, 0.00001); + }); + + it('returns B when A is [1] and A is narrower than B', () => { + const a = new Float32Array(16 * 8); + a.fill(1); + const b = test_util.randomArrayInRange(16 * 16, -10, 10); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 16, 8, b, 16, 16); + test_util.expectArraysClose(result, b, 0.00001); + }); + + it('returns B when A is [1] and A is shorter than B', () => { + const a = new Float32Array(8 * 16); + a.fill(1); + const b = test_util.randomArrayInRange(16 * 16, -10, 10); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 8, 16, b, 16, 16); + test_util.expectArraysClose(result, b, 0.00001); + }); + + it('returns B when A is [1] and A is smaller than B', () => { + const a = new Float32Array(7 * 6); + a.fill(1); + const b = test_util.randomArrayInRange(18 * 21, -1, 1); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 7, 6, b, 18, 21); + test_util.expectArraysClose(result, b, 0.00001); + }); + + it('broadcasts a smaller A [2x2] across B [4x4]', () => { + const a = new Float32Array([1, 0, 1, 0]); + const b = new Float32Array(4 * 4); + for (let i = 0; i < b.length; ++i) { + b[i] = i + 1; + } + const expected = + new Float32Array([1, 0, 3, 0, 5, 0, 7, 0, 9, 0, 11, 0, 13, 0, 15, 0]); + const gpuResult = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 2, 2, b, 4, 4); + const cpuResult = cpuMultiplyBroadcast(a, 2, 2, b, 4, 4); + test_util.expectArraysClose(cpuResult, expected, 0.0001); + test_util.expectArraysClose(gpuResult, expected, 0.0001); + }); + + it('broadcasts a non-square A [3x5] across a larger B [16x16]', () => { + const a = test_util.randomArrayInRange(3 * 5, -1, 1); + const b = test_util.randomArrayInRange(16 * 16, -1, 1); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 3, 5, b, 16, 16); + test_util.expectArraysClose( + result, cpuMultiplyBroadcast(a, 3, 5, b, 16, 16), 0.0001); + }); + + it('broadcasts a non-square A across a larger non-square B', () => { + const a = test_util.randomArrayInRange(37 * 63, -1, 1); + const b = test_util.randomArrayInRange(128 * 150, -1, 1); + const result = + mulbcast_gpu.uploadMultiplyBroadcastDownload(a, 37, 63, b, 128, 150); + test_util.expectArraysClose( + result, cpuMultiplyBroadcast(a, 37, 63, b, 128, 150), 0.0001); + }); +}); diff --git a/src/math/webgl/mulmat_gpu.ts b/src/math/webgl/mulmat_gpu.ts new file mode 100644 index 0000000000..309b8501f8 --- /dev/null +++ b/src/math/webgl/mulmat_gpu.ts @@ -0,0 +1,62 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MatrixOrientation} from '../math'; +import {Array2D} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as shader_compiler from './shader_compiler'; + +export function getFragmentShader( + a: Array2D, b: Array2D, out: Array2D, aOrientation: MatrixOrientation, + bOrientation: MatrixOrientation): string { + const sharedDim = + (aOrientation === MatrixOrientation.REGULAR ? a.shape[1] : a.shape[0]); + const aSnippet = + (aOrientation === MatrixOrientation.REGULAR) ? 'aRow, i' : 'i, aRow'; + const bSnippet = + (bOrientation === MatrixOrientation.REGULAR) ? 'i, bCol' : 'bCol, i'; + + const inputs = [{name: 'matrixA', array: a}, {name: 'matrixB', array: b}]; + const userCode = ` + const float sharedDim = ${sharedDim}.0; + + float dotARowBCol(float aRow, float bCol) { + float result = 0.0; + for (float i = 0.0; i < sharedDim; i += 1.0) { + float a = getMatrixA(${aSnippet}); + float b = getMatrixB(${bSnippet}); + result += (a * b); + } + return result; + } + + void main() { + vec2 resRC = getOutputCoords(); + setOutput(dotARowBCol(resRC.x, resRC.y)); + } + `; + return shader_compiler.makeShader(inputs, out, userCode); +} + +export function multiplyMatrix( + gpgpu: GPGPUContext, multiplyProgram: WebGLProgram, a: WebGLTexture, + b: WebGLTexture, result: WebGLTexture, outTexShape: [number, number]) { + gpgpu.setOutputMatrixTexture(result, outTexShape[0], outTexShape[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/mulmat_gpu_test.ts b/src/math/webgl/mulmat_gpu_test.ts new file mode 100644 index 0000000000..6ba5a5ed46 --- /dev/null +++ b/src/math/webgl/mulmat_gpu_test.ts @@ -0,0 +1,382 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import {MatrixOrientation} from '../math'; +import {Array2D} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as mulmat_gpu from './mulmat_gpu'; + +describe('mulmat_gpu (1x1 * 1x1)', () => { + it('returns a 1x1 matrix', () => { + const a = new Float32Array([0]); + const b = new Float32Array([0]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result.length).toEqual(1); + }); + + it('returns [0] when multiplying [0] by [0]', () => { + const a = new Float32Array([0]); + const b = new Float32Array([0]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toEqual(0); + }); + + it('returns [1] when multiplying [1] by [1]', () => { + const a = new Float32Array([1]); + const b = new Float32Array([1]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toEqual(1); + }); + + it('returns [-1] when multiplying [1] by [-1]', () => { + const a = new Float32Array([1]); + const b = new Float32Array([-1]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toEqual(-1); + }); + + it('returns [4.08] when multiplying [1.2] by [3.4]', () => { + const a = new Float32Array([1.2]); + const b = new Float32Array([3.4]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toBeCloseTo(4.08); + }); + + it('returns [356000] when multiplying [356] by [1000]', () => { + const a = new Float32Array([356]); + const b = new Float32Array([1000]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toEqual(356000); + }); + + it('returns [-31415926] when multiplying [-3.1415926] by [10000000]', () => { + const a = new Float32Array([-3.1415926]); + const b = new Float32Array([10000000]); + const result = uploadMultiplyMatrixDownload(a, 1, 1, b, 1, 1); + expect(result[0]).toEqual(-31415926); + }); +}); + +describe('mulmat_gpu (dot product)', () => { + it('returns a 1x1 matrix', () => { + const a = new Float32Array(5); + const b = new Float32Array(5); + const result = uploadMultiplyMatrixDownload(a, 1, a.length, b, b.length, 1); + expect(result.length).toEqual(1); + }); + + it('returns zero when one vector is all zeroes', () => { + const a = new Float32Array(5); + const b = new Float32Array([1, 2, 3, 4, 5]); + const result = uploadMultiplyMatrixDownload(a, 1, a.length, b, b.length, 1); + expect(result[0]).toEqual(0); + }); + + it('returns the sum of b when a is all ones', () => { + const a = new Float32Array([1, 1, 1, 1, 1]); + const b = new Float32Array([0, 1, 2, 3, 100]); + const result = uploadMultiplyMatrixDownload(a, 1, a.length, b, b.length, 1); + expect(result[0]).toEqual(106); + }); + + it('computes the dot product of a and b', () => { + const a = new Float32Array([10, 20, 30, 40, 50]); + const b = new Float32Array([0.5, 1.1, 12.4, 32.5, -123.98]); + const result = uploadMultiplyMatrixDownload(a, 1, a.length, b, b.length, 1); + const expected = test_util.cpuDotProduct(a, b); + expect(result[0]).toBeCloseTo(expected); + }); + + it('computes a dot product on very large vectors', () => { + const a: Float32Array = test_util.randomArrayInRange(2048, -1, 1); + const b: Float32Array = test_util.randomArrayInRange(2048, -1, 1); + const result = uploadMultiplyMatrixDownload(a, 1, a.length, b, b.length, 1); + const expected = test_util.cpuDotProduct(a, b); + expect(result[0]).toBeCloseTo(expected); + }); +}); + +function cpuMul2x2(a: Float32Array, b: Float32Array): Float32Array { + if (a.length !== 4 || b.length !== 4) { + throw new Error('a and b must have 4 elements.'); + } + /* + a = [0 1 b = [0 1 + 2 3] 2 3] + a[0] = [a0 a1] dot [b0 b2] + a[1] = [a0 a1] dot [b1 b3] + a[2] = [a2 a3] dot [b0 b2] + a[3] = [a2 a3] dot [b1 b3] + */ + const result = new Float32Array(4); + result[0] = (a[0] * b[0]) + (a[1] * b[2]); + result[1] = (a[0] * b[1]) + (a[1] * b[3]); + result[2] = (a[2] * b[0]) + (a[3] * b[2]); + result[3] = (a[2] * b[1]) + (a[3] * b[3]); + return result; +} + +describe('mulmat_gpu (2x2 * 2x2)', () => { + it('returns a 2x2 matrix', () => { + const a = new Float32Array([0, 0, 0, 0]); + const b = new Float32Array([0, 0, 0, 0]); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result.length).toEqual(4); + }); + + it('returns the identity when multiplying two identity matrices', () => { + const a = test_util.makeIdentity(2); + const b = test_util.makeIdentity(2); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(cpuMul2x2(a, b)); + }); + + it('returns [0] when A is [0]', () => { + const a = new Float32Array([0, 0, 0, 0]); + const b = new Float32Array([1, 2, 3, 4]); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(a); + }); + + it('returns [0] when B is [0]', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array(4); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(b); + }); + + it('returns B when A is identity', () => { + const a = test_util.makeIdentity(2); + const b = new Float32Array([11, -22, 33.333, -44.44444]); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(b); + }); + + it('returns A when B is identity', () => { + const a = new Float32Array([11, -22, 33.333, -44.44444]); + const b = test_util.makeIdentity(2); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(a); + }); + + it('returns the product of A and B when non-identity', () => { + const a = new Float32Array([10000.02, -1.2, 3.14159, -2345.1234]); + const b = new Float32Array([-23.45, 0.01234, 100, 2.5]); + const result = uploadMultiplyMatrixDownload(a, 2, 2, b, 2, 2); + expect(result).toEqual(cpuMul2x2(a, b)); + }); +}); + +describe('mulmat_gpu (different shapes)', () => { + it('returns a 4x1 when multiplying a 4x4 with a 4x1', () => { + const a = new Float32Array(16); + const b = new Float32Array(4); + const result = uploadMultiplyMatrixDownload(a, 4, 4, b, 4, 1); + expect(result.length).toEqual(4); + }); + + it('returns B (4x1) when A (4x4) is I', () => { + const a = test_util.makeIdentity(4); + const b = new Float32Array([1, 2, 3, 4]); + const result = uploadMultiplyMatrixDownload(a, 4, 4, b, 4, 1); + expect(result).toEqual(b); + }); + + it('multiplies a 4x1 by a non-identity 4x4', () => { + const a = new Float32Array( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + const b = new Float32Array([1, 2, 3, 4]); + const result = uploadMultiplyMatrixDownload(a, 4, 4, b, 4, 1); + expect(result).toEqual(test_util.cpuMultiplyMatrix(a, 4, 4, b, 4, 1)); + }); + + it('returns a 2x3 when multiplying a 2x4 by a 4x3', () => { + const a = new Float32Array(8); + const b = new Float32Array(12); + const result = uploadMultiplyMatrixDownload(a, 2, 4, b, 4, 3); + expect(result.length).toEqual(6); + }); + + it('multiplies A (2x4) by B(4x3)', () => { + const a = new Float32Array([0.1, 3.2, -4.5, 11.78, -0.234, -2.999, 7, 9]); + const b = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const result = uploadMultiplyMatrixDownload(a, 2, 4, b, 4, 3); + const expected = test_util.cpuMultiplyMatrix(a, 2, 4, b, 4, 3); + test_util.expectArraysClose(result, expected, 0.00001); + }); +}); + +describe('mulmat_gpu (large matrices)', () => { + it('returns 128x128 when multiplying 2 128x128s', () => { + const a = new Float32Array(128 * 128); + const b = new Float32Array(128 * 128); + const result = uploadMultiplyMatrixDownload(a, 128, 128, b, 128, 128); + expect(result.length).toEqual(128 * 128); + }); + + it('multiplies 2 128x128s', () => { + const a = test_util.randomArrayInRange(128 * 128, -1, 1); + const b = test_util.randomArrayInRange(128 * 128, -1, 1); + const result = uploadMultiplyMatrixDownload(a, 128, 128, b, 128, 128); + const expected = test_util.cpuMultiplyMatrix(a, 128, 128, b, 128, 128); + test_util.expectArraysClose(result, expected, 0.001); + }); +}); + +describe('mulmat_gpu (multiple matrices)', () => { + it('4x2 * 2x12 * 12x1 === 4x1', () => { + const aData = new Float32Array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]); + const bData = new Float32Array([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 + ]); + const cData = new Float32Array([ + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2 + ]); + + const gpgpu = new GPGPUContext(); + + const aShape: [number, number] = [4, 2]; + const bShape: [number, number] = [2, 12]; + const abShape: [number, number] = [aShape[0], bShape[1]]; + const cShape: [number, number] = [12, 1]; + const rShape: [number, number] = [aShape[0], cShape[1]]; + + const a: WebGLTexture = gpgpu.createMatrixTexture(aShape[0], aShape[1]); + const b: WebGLTexture = gpgpu.createMatrixTexture(bShape[0], bShape[1]); + const ab: WebGLTexture = gpgpu.createMatrixTexture(abShape[0], abShape[1]); + const c: WebGLTexture = gpgpu.createMatrixTexture(cShape[0], cShape[1]); + const r: WebGLTexture = gpgpu.createMatrixTexture(rShape[0], rShape[1]); + + const aArr = new Array2D(aShape, {texture: a, textureShapeRC: aShape}); + const bArr = new Array2D(bShape, {texture: b, textureShapeRC: bShape}); + const abArr = new Array2D(abShape, {texture: ab, textureShapeRC: abShape}); + const cArr = new Array2D(cShape, {texture: c, textureShapeRC: cShape}); + const rArr = new Array2D(rShape, {texture: r, textureShapeRC: rShape}); + + const axbProgram = gpgpu.createProgram(mulmat_gpu.getFragmentShader( + aArr, bArr, cArr, MatrixOrientation.REGULAR, + MatrixOrientation.REGULAR)); + const abxcProgram = gpgpu.createProgram(mulmat_gpu.getFragmentShader( + abArr, cArr, rArr, MatrixOrientation.REGULAR, + MatrixOrientation.REGULAR)); + + gpgpu.uploadMatrixToTexture(a, aShape[0], aShape[1], aData); + gpgpu.uploadMatrixToTexture(b, bShape[0], bShape[1], bData); + gpgpu.uploadMatrixToTexture(c, cShape[0], cShape[1], cData); + + mulmat_gpu.multiplyMatrix( + gpgpu, axbProgram, a, b, ab, [abShape[0], abShape[1]]); + mulmat_gpu.multiplyMatrix( + gpgpu, abxcProgram, ab, c, r, [rShape[0], rShape[1]]); + + const result = gpgpu.downloadMatrixFromTexture(r, rShape[0], rShape[1]); + const expected = test_util.cpuMultiplyMatrix( + test_util.cpuMultiplyMatrix(aData, 4, 2, bData, 2, 12), 4, 12, cData, + 12, 1); + test_util.expectArraysClose(result, expected, 0.0001); + + gpgpu.deleteMatrixTexture(a); + gpgpu.deleteMatrixTexture(b); + gpgpu.deleteMatrixTexture(ab); + gpgpu.deleteMatrixTexture(c); + gpgpu.deleteMatrixTexture(r); + gpgpu.deleteProgram(axbProgram); + gpgpu.deleteProgram(abxcProgram); + gpgpu.dispose(); + }); +}); + +describe('mulmat_gpu (transposed versions)', () => { + it('A * B^t', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = uploadMultiplyMatrixDownload( + a, 2, 3, b, 2, 3, MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([7, 10, 16, 31]); + expect(c).toEqual(expected); + }); + + it('A^t * B', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = uploadMultiplyMatrixDownload( + a, 2, 3, b, 2, 3, MatrixOrientation.TRANSPOSED, + MatrixOrientation.REGULAR); + const expected = new Float32Array([17, 12, 2, 22, 15, 4, 27, 18, 6]); + expect(c).toEqual(expected); + }); + + it('A^t * B^t', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = uploadMultiplyMatrixDownload( + a, 3, 2, b, 2, 3, MatrixOrientation.TRANSPOSED, + MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([11, 13, 14, 20]); + expect(c).toEqual(expected); + }); +}); + +export function uploadMultiplyMatrixDownload( + a: Float32Array, aNumRows: number, aNumCols: number, b: Float32Array, + bNumRows: number, bNumCols: number, + aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const outNumRows = + (aOrientation === MatrixOrientation.REGULAR) ? aNumRows : aNumCols; + const outNumCols = + (bOrientation === MatrixOrientation.REGULAR) ? bNumCols : bNumRows; + const gpgpu = new GPGPUContext(); + const aShape: [number, number] = [aNumRows, aNumCols]; + const bShape: [number, number] = [bNumRows, bNumCols]; + const outShape: [number, number] = [outNumRows, outNumCols]; + + const aTexture = gpgpu.createMatrixTexture(aNumRows, aNumCols); + const aArr = new Array2D( + aShape, {texture: aTexture, textureShapeRC: [aNumRows, aNumCols]}); + const bTexture = gpgpu.createMatrixTexture(bNumRows, bNumCols); + const bArr = new Array2D( + bShape, {texture: bTexture, textureShapeRC: [bNumRows, bNumCols]}); + const resultTexture: WebGLTexture = + gpgpu.createMatrixTexture(outNumRows, outNumCols); + const resArr = + new Array2D(outShape, {texture: resultTexture, textureShapeRC: outShape}); + const shader = mulmat_gpu.getFragmentShader( + aArr, bArr, resArr, aOrientation, bOrientation); + const program: WebGLProgram = gpgpu.createProgram(shader); + + + gpgpu.uploadMatrixToTexture(aTexture, aNumRows, aNumCols, a); + gpgpu.uploadMatrixToTexture(bTexture, bNumRows, bNumCols, b); + + mulmat_gpu.multiplyMatrix( + gpgpu, program, aTexture, bTexture, resultTexture, + resArr.getTextureShapeRC()); + + const result = + gpgpu.downloadMatrixFromTexture(resultTexture, outNumRows, outNumCols); + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return result; +} diff --git a/src/math/webgl/mulmat_packed_gpu.ts b/src/math/webgl/mulmat_packed_gpu.ts new file mode 100644 index 0000000000..a025f3a6b0 --- /dev/null +++ b/src/math/webgl/mulmat_packed_gpu.ts @@ -0,0 +1,130 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {MatrixOrientation} from '../math'; + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource( + sharedDimension: number, aOrientation: MatrixOrientation, + bOrientation: MatrixOrientation): string { + /* + A = [0 1 B = [0 1 out = [A0*B0+A1*B2 A0*B1+A1*B3 + 2 3] 2 3] A2*B0+A1*B2 A2*B1+Aw*B3] + out.0 = A0 * B0 + A1 * B2 + out.1 = A0 * B1 + A1 * B3 + out.2 = A2 * B0 + A3 * B2 + out.3 = A2 * B1 + A3 * B3 + + A*B = A.xxzz * B.xyxy + A.yyww * B.zwzw + A^t*B = A.xxyy * B.xyxy + A.zzww * B.zwzw + A*B^t = A.xxzz * B.xzxz + A.yyww * B.ywyw + A^t*B^t = A.xxyy * B.xzxz + A.zzww * B.ywyw + */ + const sharedDimensionPacked = Math.ceil(sharedDimension / 2); + const aSample = (aOrientation === MatrixOrientation.REGULAR) ? + 'center, resultUV.t' : + 'resultUV.t, center'; + const bSample = (bOrientation === MatrixOrientation.REGULAR) ? + 'resultUV.s, center' : + 'center, resultUV.s'; + const aSwizzle: [string, string] = + (aOrientation === MatrixOrientation.REGULAR) ? ['a.xxzz', 'a.yyww'] : + ['a.xxyy', 'a.zzww']; + const bSwizzle: [string, string] = + (bOrientation === MatrixOrientation.REGULAR) ? ['b.xyxy', 'b.zwzw'] : + ['b.xzxz', 'b.ywyw']; + return ` + precision highp float; + uniform sampler2D matrixA; + uniform sampler2D matrixB; + varying vec2 resultUV; + + const float sharedDimension = ${sharedDimensionPacked}.0; + + vec4 dot2x2ARowBCol() { + vec4 result = vec4(0, 0, 0, 0); + for (float i = 0.0; i < sharedDimension; i += 1.0) { + float center = (i + 0.5) / sharedDimension; + vec4 a = texture2D(matrixA, vec2(${aSample})); + vec4 b = texture2D(matrixB, vec2(${bSample})); + result += + (${aSwizzle[0]} * ${bSwizzle[0]}) + (${aSwizzle[1]} * ${bSwizzle[1]}); + } + return result; + } + + void main() { + gl_FragColor = dot2x2ARowBCol(); + }`; +} + +export function multiplyMatrixPacked( + gpgpu: GPGPUContext, multiplyProgram: WebGLProgram, a: WebGLTexture, + b: WebGLTexture, result: WebGLTexture, + resultShapeRowCol: [number, number]) { + gpgpu.setOutputPackedMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(multiplyProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.setInputMatrixTexture(b, 'matrixB', 1); + gpgpu.executeProgram(); +} + +export function uploadMultiplyMatrixPackedDownload( + a: Float32Array, aShapeRowCol: [number, number], b: Float32Array, + bShapeRowCol: [number, number], aOrientation = MatrixOrientation.REGULAR, + bOrientation = MatrixOrientation.REGULAR): Float32Array { + const resultNumRows = (aOrientation === MatrixOrientation.REGULAR) ? + aShapeRowCol[0] : + aShapeRowCol[1]; + const resultNumCols = (bOrientation === MatrixOrientation.REGULAR) ? + bShapeRowCol[1] : + bShapeRowCol[0]; + const sharedDimension = (aOrientation === MatrixOrientation.REGULAR) ? + aShapeRowCol[1] : + aShapeRowCol[0]; + + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = gpgpu.createProgram( + getFragmentShaderSource(sharedDimension, aOrientation, bOrientation)); + + const aTexture: WebGLTexture = + gpgpu.createPackedMatrixTexture(aShapeRowCol[0], aShapeRowCol[1]); + const bTexture: WebGLTexture = + gpgpu.createPackedMatrixTexture(bShapeRowCol[0], bShapeRowCol[1]); + const resultTexture: WebGLTexture = + gpgpu.createPackedMatrixTexture(resultNumRows, resultNumCols); + + gpgpu.uploadMatrixToPackedTexture( + aTexture, aShapeRowCol[0], aShapeRowCol[1], a); + gpgpu.uploadMatrixToPackedTexture( + bTexture, bShapeRowCol[0], bShapeRowCol[1], b); + + multiplyMatrixPacked( + gpgpu, program, aTexture, bTexture, resultTexture, + [resultNumRows, resultNumCols]); + + const result = gpgpu.downloadMatrixFromPackedTexture( + resultTexture, resultNumRows, resultNumCols); + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(bTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + + return result; +} diff --git a/src/math/webgl/mulmat_packed_gpu_test.ts b/src/math/webgl/mulmat_packed_gpu_test.ts new file mode 100644 index 0000000000..72f4555a32 --- /dev/null +++ b/src/math/webgl/mulmat_packed_gpu_test.ts @@ -0,0 +1,404 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import {MatrixOrientation} from '../math'; + +import {GPGPUContext} from './gpgpu_context'; +import * as mulmat_packed_gpu from './mulmat_packed_gpu'; + +describe('mulmat_packed_gpu (1x1 * 1x1)', () => { + it('returns a 1x1 matrix', () => { + const a = new Float32Array([0]); + const b = new Float32Array([0]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result.length).toEqual(1); + }); + + it('returns [0] when multiplying [0] by [0]', () => { + const a = new Float32Array([0]); + const b = new Float32Array([0]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toEqual(0); + }); + + it('returns [1] when multiplying [1] by [1]', () => { + const a = new Float32Array([1]); + const b = new Float32Array([1]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toEqual(1); + }); + + it('returns [-1] when multiplying [1] by [-1]', () => { + const a = new Float32Array([1]); + const b = new Float32Array([-1]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toEqual(-1); + }); + + it('returns [4.08] when multiplying [1.2] by [3.4]', () => { + const a = new Float32Array([1.2]); + const b = new Float32Array([3.4]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toBeCloseTo(4.08); + }); + + it('returns [356000] when multiplying [356] by [1000]', () => { + const a = new Float32Array([356]); + const b = new Float32Array([1000]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toEqual(356000); + }); + + it('returns [-31415926] when multiplying [-3.1415926] by [10000000]', () => { + const a = new Float32Array([-3.1415926]); + const b = new Float32Array([10000000]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1]); + expect(result[0]).toEqual(-31415926); + }); +}); + +describe('mulmat_packed_gpu (dot product)', () => { + it('returns a 1x1 matrix', () => { + const a = new Float32Array(5); + const b = new Float32Array(5); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, a.length], b, [b.length, 1]); + expect(result.length).toEqual(1); + }); + + it('returns zero when one vector is all zeroes', () => { + const a = new Float32Array(5); + const b = new Float32Array([1, 2, 3, 4, 5]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, a.length], b, [b.length, 1]); + expect(result[0]).toEqual(0); + }); + + it('returns the sum of b when a is all ones', () => { + const a = new Float32Array([1, 1, 1, 1, 1]); + const b = new Float32Array([0, 1, 2, 3, 100]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, a.length], b, [b.length, 1]); + expect(result[0]).toEqual(106); + }); + + it('computes the dot product of a and b', () => { + const a = new Float32Array([10, 20, 30, 40, 50]); + const b = new Float32Array([0.5, 1.1, 12.4, 32.5, -123.98]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, a.length], b, [b.length, 1]); + const expected = test_util.cpuDotProduct(a, b); + expect(result[0]).toBeCloseTo(expected); + }); + + it('computes a dot product on very large vectors', () => { + const a: Float32Array = test_util.randomArrayInRange(2048, -1, 1); + const b: Float32Array = test_util.randomArrayInRange(2048, -1, 1); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, a.length], b, [b.length, 1]); + const expected = test_util.cpuDotProduct(a, b); + expect(result[0]).toBeCloseTo(expected); + }); +}); + +function cpuMul2x2(a: Float32Array, b: Float32Array): Float32Array { + if (a.length !== 4 || b.length !== 4) { + throw new Error('a and b must have 4 elements.'); + } + /* + a = [0 1 b = [0 1 + 2 3] 2 3] + a[0] = [a0 a1] dot [b0 b2] + a[1] = [a0 a1] dot [b1 b3] + a[2] = [a2 a3] dot [b0 b2] + a[3] = [a2 a3] dot [b1 b3] + */ + const result = new Float32Array(4); + result[0] = (a[0] * b[0]) + (a[1] * b[2]); + result[1] = (a[0] * b[1]) + (a[1] * b[3]); + result[2] = (a[2] * b[0]) + (a[3] * b[2]); + result[3] = (a[2] * b[1]) + (a[3] * b[3]); + return result; +} + +describe('mulmat_packed_gpu (2x2 * 2x2)', () => { + it('returns a 2x2 matrix', () => { + const a = new Float32Array([0, 0, 0, 0]); + const b = new Float32Array([0, 0, 0, 0]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result.length).toEqual(4); + }); + + it('returns the identity when multiplying two identity matrices', () => { + const a = test_util.makeIdentity(2); + const b = test_util.makeIdentity(2); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(cpuMul2x2(a, b)); + }); + + it('returns [0] when A is [0]', () => { + const a = new Float32Array([0, 0, 0, 0]); + const b = new Float32Array([1, 2, 3, 4]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(a); + }); + + it('returns [0] when B is [0]', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array(4); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(b); + }); + + it('returns B when A is identity', () => { + const a = test_util.makeIdentity(2); + const b = new Float32Array([11, -22, 33.333, -44.44444]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(b); + }); + + it('returns A when B is identity', () => { + const a = new Float32Array([11, -22, 33.333, -44.44444]); + const b = test_util.makeIdentity(2); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(a); + }); + + it('returns the product of A and B when non-identity', () => { + const a = new Float32Array([10000.02, -1.2, 3.14159, -2345.1234]); + const b = new Float32Array([-23.45, 0.01234, 100, 2.5]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2]); + expect(result).toEqual(cpuMul2x2(a, b)); + }); +}); + +describe('mulmat_packed_gpu (different shapes)', () => { + it('returns a 4x1 when multiplying a 4x4 with a 4x1', () => { + const a = new Float32Array(16); + const b = new Float32Array(4); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [4, 4], b, [4, 1]); + expect(result.length).toEqual(4); + }); + + it('returns B (4x1) when A (4x4) is I', () => { + const a = test_util.makeIdentity(4); + const b = new Float32Array([1, 2, 3, 4]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [4, 4], b, [4, 1]); + expect(result).toEqual(b); + }); + + it('4x2 * 2x2', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]); + const b = new Float32Array([9, 10, 11, 12]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [4, 2], b, [2, 2]); + const expected = test_util.cpuMultiplyMatrix(a, 4, 2, b, 2, 2); + test_util.expectArraysClose(result, expected, 0); + }); + + it('multiplies a 4x1 by a non-identity 4x4', () => { + const a = new Float32Array( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + const b = new Float32Array([1, 2, 3, 4]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [4, 4], b, [4, 1]); + expect(result).toEqual(test_util.cpuMultiplyMatrix(a, 4, 4, b, 4, 1)); + }); + + it('returns a 2x3 when multiplying a 2x4 by a 4x3', () => { + const a = new Float32Array(8); + const b = new Float32Array(12); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 4], b, [4, 3]); + expect(result.length).toEqual(6); + }); + + it('multiplies A (2x4) by B(4x3)', () => { + const a = new Float32Array([0.1, 3.2, -4.5, 11.78, -0.234, -2.999, 7, 9]); + const b = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 4], b, [4, 3]); + const expected = test_util.cpuMultiplyMatrix(a, 2, 4, b, 4, 3); + test_util.expectArraysClose(result, expected, 0.00001); + }); +}); + +describe('mulmat_packed_gpu (large matrices)', () => { + it('returns 128x128 when multiplying 2 128x128s', () => { + const a = new Float32Array(128 * 128); + const b = new Float32Array(128 * 128); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [128, 128], b, [128, 128]); + expect(result.length).toEqual(128 * 128); + }); + + it('multiplies 2 128x128s', () => { + const a = test_util.randomArrayInRange(128 * 128, -1, 1); + const b = test_util.randomArrayInRange(128 * 128, -1, 1); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [128, 128], b, [128, 128]); + const expected = test_util.cpuMultiplyMatrix(a, 128, 128, b, 128, 128); + test_util.expectArraysClose(result, expected, 0.001); + }); +}); + +describe('mulmat_packed_gpu (multiple matrices)', () => { + it('4x2 * 2x12 * 12x1 === 4x1', () => { + const aData = new Float32Array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]); + const bData = new Float32Array([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 + ]); + const cData = new Float32Array([ + -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2 + ]); + + const gpgpu = new GPGPUContext(); + + const axbProgram = + gpgpu.createProgram(mulmat_packed_gpu.getFragmentShaderSource( + 2, MatrixOrientation.REGULAR, MatrixOrientation.REGULAR)); + const abxcProgram = + gpgpu.createProgram(mulmat_packed_gpu.getFragmentShaderSource( + 12, MatrixOrientation.REGULAR, MatrixOrientation.REGULAR)); + + const a: WebGLTexture = gpgpu.createPackedMatrixTexture(4, 2); + const b: WebGLTexture = gpgpu.createPackedMatrixTexture(2, 12); + const ab: WebGLTexture = gpgpu.createPackedMatrixTexture(4, 12); + const c: WebGLTexture = gpgpu.createPackedMatrixTexture(12, 1); + const r: WebGLTexture = gpgpu.createPackedMatrixTexture(4, 1); + + gpgpu.uploadMatrixToPackedTexture(a, 4, 2, aData); + gpgpu.uploadMatrixToPackedTexture(b, 2, 12, bData); + gpgpu.uploadMatrixToPackedTexture(c, 12, 1, cData); + + mulmat_packed_gpu.multiplyMatrixPacked( + gpgpu, axbProgram, a, b, ab, [4, 12]); + mulmat_packed_gpu.multiplyMatrixPacked( + gpgpu, abxcProgram, ab, c, r, [4, 1]); + + const result = gpgpu.downloadMatrixFromPackedTexture(r, 4, 1); + const expected = test_util.cpuMultiplyMatrix( + test_util.cpuMultiplyMatrix(aData, 4, 2, bData, 2, 12), 4, 12, cData, + 12, 1); + test_util.expectArraysClose(result, expected, 0.0001); + + gpgpu.deleteMatrixTexture(a); + gpgpu.deleteMatrixTexture(b); + gpgpu.deleteMatrixTexture(ab); + gpgpu.deleteMatrixTexture(c); + gpgpu.deleteMatrixTexture(r); + gpgpu.deleteProgram(axbProgram); + gpgpu.deleteProgram(abxcProgram); + gpgpu.dispose(); + }); +}); + +describe('mulmat_packed_gpu A * B^t', () => { + it('1x1 * 1x1', () => { + const a = new Float32Array([2]); + const b = new Float32Array([3]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [1, 1], b, [1, 1], MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + expect(result[0]).toEqual(6); + }); + + it('2x2 * 2x2', () => { + const a = new Float32Array([1, 2, 3, 4]); + const b = new Float32Array([5, 6, 7, 8]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 2], b, [2, 2], MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + + const bt = new Float32Array([b[0], b[2], b[1], b[3]]); + const expected = test_util.cpuMultiplyMatrix(a, 2, 2, bt, 2, 2); + test_util.expectArraysClose(result, expected, 0); + }); + + it('2x4 * 4x2', () => { + /* + A = [1 2 3 4 B = [ 9 10 11 12 B^t = [ 9 13 + 5 6 7 8] 13 14 15 16] 10 14 + 11 15 + 12 16] + + A * B^t = [1*9+2*10+3*11+4*12 1*13+2*14+3*15+4*16 + 5*9+6*10+7*11+8*12 5*13+6*14+7*15+8*16] + + = [110 150 + 278 382 + */ + const a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]); + const b = new Float32Array([9, 10, 11, 12, 13, 14, 15, 16]); + const result = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 4], b, [2, 4], MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + + const bt = + new Float32Array([b[0], b[4], b[1], b[5], b[2], b[6], b[3], b[7]]); + const expected = test_util.cpuMultiplyMatrix(a, 2, 4, bt, 4, 2); + test_util.expectArraysClose(result, expected, 0); + }); +}); + +describe('mulmat_packed_gpu (transposed versions)', () => { + it('A * B^t', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 3], b, [2, 3], MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([7, 10, 16, 31]); + expect(c).toEqual(expected); + }); + + it('A^t * B', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [2, 3], b, [2, 3], MatrixOrientation.TRANSPOSED, + MatrixOrientation.REGULAR); + const expected = new Float32Array([17, 12, 2, 22, 15, 4, 27, 18, 6]); + expect(c).toEqual(expected); + }); + + it('A^t * B^t', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const b = new Float32Array([1, 0, 2, 4, 3, 0]); + const c = mulmat_packed_gpu.uploadMultiplyMatrixPackedDownload( + a, [3, 2], b, [2, 3], MatrixOrientation.TRANSPOSED, + MatrixOrientation.TRANSPOSED); + const expected = new Float32Array([11, 13, 14, 20]); + expect(c).toEqual(expected); + }); +}); diff --git a/src/math/webgl/neg_gpu.ts b/src/math/webgl/neg_gpu.ts new file mode 100644 index 0000000000..988b7a8178 --- /dev/null +++ b/src/math/webgl/neg_gpu.ts @@ -0,0 +1,36 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getNegUnaryOp(): string { + return 'gl_FragColor = vec4(-value, 0, 0, 0);'; +} + +export function getFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getNegUnaryOp()); +} + +export function neg( + gpgpu: GPGPUContext, program: WebGLProgram, a: WebGLTexture, rows: number, + columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, program, a, rows, columns, result); +} + +export function uploadNegDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getNegUnaryOp()); +} diff --git a/src/math/webgl/neg_gpu_test.ts b/src/math/webgl/neg_gpu_test.ts new file mode 100644 index 0000000000..ddbd9c8dec --- /dev/null +++ b/src/math/webgl/neg_gpu_test.ts @@ -0,0 +1,50 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as neg_gpu from './neg_gpu'; + +describe('neg_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(28 * 32); + const result = neg_gpu.uploadNegDownload(a, 28, 32); + expect(result.length).toEqual(a.length); + }); + + it('preserves zero values', () => { + const a = new Float32Array([0]); + const result = neg_gpu.uploadNegDownload(a, 1, 1); + expect(result[0]).toBeCloseTo(0); + }); + + it('negates positive values into negative values', () => { + const a = new Float32Array([1]); + const result = neg_gpu.uploadNegDownload(a, 1, 1); + expect(result[0]).toEqual(-1); + }); + + it('negates negative values into positive values', () => { + const a = new Float32Array([-1]); + const result = neg_gpu.uploadNegDownload(a, 1, 1); + expect(result[0]).toEqual(1); + }); + + it('operates on every value in a matrix', () => { + const a = new Float32Array([0.5, 0, -2.3, 4, -12, -Math.E]); + const result = neg_gpu.uploadNegDownload(a, 2, 3); + const expected = new Float32Array([-0.5, 0, 2.3, -4, 12, Math.E]); + test_util.expectArraysClose(result, expected, 0.0001); + }); +}); diff --git a/src/math/webgl/pool_gpu.ts b/src/math/webgl/pool_gpu.ts new file mode 100644 index 0000000000..a588deb33d --- /dev/null +++ b/src/math/webgl/pool_gpu.ts @@ -0,0 +1,121 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; +import {GPGPUContext} from './gpgpu_context'; +import {IS_NAN_SHADER_FUNC} from './webgl_util'; + +export function getFragmentShaderPoolCommonSource( + xShapeRCD: [number, number, number], fSize: number, stride: number, + pad: number, poolType: 'max'|'min'|'avg', computePositions: boolean) { + if (poolType === 'avg' && computePositions) { + throw new Error('Cannot compute positions for average pool.'); + } + + const depth = xShapeRCD[2]; + + const xTexShapeRC = conv_util.computeTexShapeFrom3D(xShapeRCD); + + let returnValue = 'minMaxValue'; + if (computePositions) { + returnValue = 'minMaxPosition'; + } else if (poolType === 'avg') { + returnValue = 'avgValue'; + } + + return ` + precision highp float; + uniform sampler2D x; + varying vec2 resultUV; + + const vec2 halfCR = vec2(0.5, 0.5); + const vec2 xShapeCR = vec2(${xTexShapeRC[1]}, ${xTexShapeRC[0]}); + + ${IS_NAN_SHADER_FUNC} + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d2). + float yR = yTexCR.y; + float yC = floor(yTexCR.x / ${depth}.0); + float d = mod(yTexCR.x, ${depth}.0); + + vec2 xRCCorner = vec2(yR, yC) * vec2(${stride}, ${stride}) - + vec2(${pad}.0, ${pad}.0); + float xRCorner = xRCCorner.x; + float xCCorner = xRCCorner.y; + + // max/min x(?, ?, d) to get y(yR, yC, d). + // ? = to be determined + float minMaxValue = 0.0; + float minMaxValueFound = 0.0; + float minMaxPosition = 0.0; + float avgValue = 0.0; + + for (float wR = 0.0; wR < ${fSize}.0; wR += 1.0) { + float xR = xRCorner + wR; + float xTexR = xR; + + for (float wC = 0.0; wC < ${fSize}.0; wC += 1.0) { + float xC = xCCorner + wC; + float xTexC = xC * ${depth}.0 + d; + + vec2 texCR = vec2(xTexC, xTexR); + + // Check if the requested UV is invalid. + vec2 uv = (texCR + halfCR) / xShapeCR; + bool lessThanZero = any(lessThan(uv, vec2(0, 0))); + bool greaterThanOne = any(greaterThan(uv, vec2(1, 1))); + bool outside = lessThanZero || greaterThanOne; + if (outside) { + continue; + } + + float value = texture2D(x, uv).r; + if (isNaN(value)) { + gl_FragColor = vec4(value, 0, 0, 0); + return; + } + if (${poolType === 'avg'}) { + avgValue += value / ${fSize * fSize}.0; + } else { + // If a min / max value has already been found, use it. If not, use + // the current value. + float currentMinMaxValue = mix( + value, minMaxValue, minMaxValueFound); + if (value ${poolType === 'min' ? '<=' : '>='} currentMinMaxValue) { + minMaxValue = value; + minMaxValueFound = 1.0; + if (${computePositions}) { + minMaxPosition = wR * ${fSize}.0 + wC; + } + } + } + } + } + gl_FragColor = vec4(${returnValue}, 0, 0, 0); + }`; +} + +export function poolCommon( + gpgpu: GPGPUContext, program: WebGLProgram, x: WebGLTexture, + result: WebGLTexture, resultShapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(program); + gpgpu.setInputMatrixTexture(x, 'x', 0); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/reducesum_gpu.ts b/src/math/webgl/reducesum_gpu.ts new file mode 100644 index 0000000000..e3fde5d476 --- /dev/null +++ b/src/math/webgl/reducesum_gpu.ts @@ -0,0 +1,63 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource(rows: number, columns: number): string { + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 resultUV; + + const vec2 aDimCR = vec2(${columns}.0, ${rows}.0); + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + float sum = 0.0; + for (float r = 0.0; r < aDimCR.y; r += 1.0) { + for (float c = 0.0; c < aDimCR.x; c += 1.0) { + vec2 uv = (vec2(c, r) + halfCR) / aDimCR; + sum += texture2D(matrixA, uv).r; + } + } + gl_FragColor = vec4(sum, 0, 0, 0); + }`; +} + +export function reduceSum( + gpgpu: GPGPUContext, reduceSumProgram: WebGLProgram, a: WebGLTexture, + aNumRows: number, aNumCols: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, 1, 1); + gpgpu.setProgram(reduceSumProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} + +export function uploadReduceSumDownload( + a: Float32Array, rows: number, columns: number): number { + const gpgpu = new GPGPUContext(); + const program: WebGLProgram = + gpgpu.createProgram(getFragmentShaderSource(rows, columns)); + const aTexture: WebGLTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture: WebGLTexture = gpgpu.createMatrixTexture(1, 1); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + reduceSum(gpgpu, program, aTexture, rows, columns, resultTexture); + const result = gpgpu.downloadMatrixFromTexture(resultTexture, 1, 1)[0]; + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} diff --git a/src/math/webgl/reducesum_gpu_test.ts b/src/math/webgl/reducesum_gpu_test.ts new file mode 100644 index 0000000000..cea94b0ac3 --- /dev/null +++ b/src/math/webgl/reducesum_gpu_test.ts @@ -0,0 +1,65 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as reducesum_gpu from './reducesum_gpu'; + +describe('reducesum_gpu', () => { + it('returns 0 when A is [0]', () => { + const a = new Float32Array(129 * 257); + const result = reducesum_gpu.uploadReduceSumDownload(a, 129, 257); + expect(result).toEqual(0); + }); + + it('returns 1 when A is [1]', () => { + const a = new Float32Array([1]); + const result = reducesum_gpu.uploadReduceSumDownload(a, 1, 1); + expect(result).toEqual(1); + }); + + it('returns 1 when A has one 1', () => { + const a = new Float32Array(100 * 100); + a[49] = 1; + const result = reducesum_gpu.uploadReduceSumDownload(a, 100, 100); + expect(result).toEqual(1); + }); + + it('returns 2 when A has two ones', () => { + const a = new Float32Array(513 * 257); + a[23] = 1; + a[1000] = 1; + const result = reducesum_gpu.uploadReduceSumDownload(a, 513, 257); + expect(result).toEqual(2); + }); + + it('accumulates values from matrix', () => { + const a = test_util.randomArrayInRange(100, -1, 1); + const result = reducesum_gpu.uploadReduceSumDownload(a, 10, 10); + const expected = (() => { + let accum = 0; + for (let i = 0; i < a.length; ++i) { + accum += a[i]; + } + return accum; + })(); + expect(result).toBeCloseTo(expected); + }); + + it('computes 7 from 3x2 [1,2,3,0,0,1]', () => { + const a = new Float32Array([1, 2, 3, 0, 0, 1]); + const result = reducesum_gpu.uploadReduceSumDownload(a, 3, 2); + expect(result).toEqual(7); + }); +}); diff --git a/src/math/webgl/relu_gpu.ts b/src/math/webgl/relu_gpu.ts new file mode 100644 index 0000000000..5e365a3d14 --- /dev/null +++ b/src/math/webgl/relu_gpu.ts @@ -0,0 +1,39 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getReluUnaryOp(): string { + return ` + float result = (value < 0.0 ? 0.0 : value); + gl_FragColor = vec4(result, 0, 0, 0); + `; +} + +export function getFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getReluUnaryOp()); +} + +export function relu( + gpgpu: GPGPUContext, reluProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, reluProgram, a, rows, columns, result); +} + +export function uploadReluDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getReluUnaryOp()); +} diff --git a/src/math/webgl/relu_gpu_test.ts b/src/math/webgl/relu_gpu_test.ts new file mode 100644 index 0000000000..8a7731d849 --- /dev/null +++ b/src/math/webgl/relu_gpu_test.ts @@ -0,0 +1,57 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as relu_gpu from './relu_gpu'; + +describe('relu_gpu', () => { + it('returns a matrix with the shape of the input matrix', () => { + const a = new Float32Array(17 * 257); + const result = relu_gpu.uploadReluDownload(a, 17, 257); + expect(result.length).toEqual(a.length); + }); + + it('does nothing to positive values', () => { + const a = new Float32Array([1]); + const result = relu_gpu.uploadReluDownload(a, 1, 1); + expect(result[0]).toBeCloseTo(a[0]); + }); + + it('sets negative values to 0', () => { + const a = new Float32Array([-1]); + const result = relu_gpu.uploadReluDownload(a, 1, 1); + expect(result[0]).toEqual(0); + }); + + it('preserves zero values', () => { + const a = new Float32Array([0]); + const result = relu_gpu.uploadReluDownload(a, 1, 1); + expect(result[0]).toEqual(0); + }); + + it('operates on multiple values', () => { + const a = new Float32Array([-1, 2, -3, 4, -5, 6, -7, 8, -9]); + const result = relu_gpu.uploadReluDownload(a, 3, 3); + test_util.expectArraysClose( + result, new Float32Array([0, 2, 0, 4, 0, 6, 0, 8, 0]), 0.0001); + }); + + it('propagates NaNs', () => { + const a = new Float32Array([-1, NaN, -3, 4, 6, 0, -3, 1]); + const result = relu_gpu.uploadReluDownload(a, 1, 8); + test_util.expectArraysClose( + result, new Float32Array([0, NaN, 0, 4, 6, 0, 0, 1]), 0.0001); + }); +}); diff --git a/src/math/webgl/render_ndarray_gpu_util.ts b/src/math/webgl/render_ndarray_gpu_util.ts new file mode 100644 index 0000000000..de4d07ccee --- /dev/null +++ b/src/math/webgl/render_ndarray_gpu_util.ts @@ -0,0 +1,59 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +import * as webgl_util from './webgl_util'; + +export function getRenderRGBShader( + gpgpu: GPGPUContext, destinationWidth: number): WebGLProgram { + const fragmentShaderSource = ` + precision highp float; + uniform sampler2D source; + varying vec2 resultUV; + + const float destinationWidth = ${destinationWidth}.0; + const float a = 1.0; + + void main() { + float xr = floor(resultUV.s * destinationWidth) * 3.0; + vec3 x = xr + vec3(0, 1, 2); + + float sourceWidth = destinationWidth * 3.0; + vec3 u = (x + 0.5) / sourceWidth; + float v = 1.0 - resultUV.t; + + float r = texture2D(source, vec2(u[0], v)).r; + float g = texture2D(source, vec2(u[1], v)).r; + float b = texture2D(source, vec2(u[2], v)).r; + + gl_FragColor = vec4(r, g, b, a); + }`; + + return gpgpu.createProgram(fragmentShaderSource); +} + +export function renderToCanvas( + gpgpu: GPGPUContext, renderShader: WebGLProgram, sourceTex: WebGLTexture) { + webgl_util.bindCanvasToFramebuffer(gpgpu.gl); + renderToFramebuffer(gpgpu, renderShader, sourceTex); +} + +export function renderToFramebuffer( + gpgpu: GPGPUContext, renderShader: WebGLProgram, sourceTex: WebGLTexture) { + gpgpu.setProgram(renderShader); + gpgpu.setInputMatrixTexture(sourceTex, 'source', 0); + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/render_ndarray_gpu_util_test.ts b/src/math/webgl/render_ndarray_gpu_util_test.ts new file mode 100644 index 0000000000..c7d45b3b27 --- /dev/null +++ b/src/math/webgl/render_ndarray_gpu_util_test.ts @@ -0,0 +1,70 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; + +import {GPGPUContext} from './gpgpu_context'; +import * as gpgpu_util from './gpgpu_util'; +import * as render_ndarray_gpu_util from './render_ndarray_gpu_util'; + +function uploadRenderRGBDownload( + source: Float32Array, sourceShapeRowColDepth: [number, number, number]) { + const canvas = document.createElement('canvas'); + canvas.width = sourceShapeRowColDepth[0]; + canvas.height = sourceShapeRowColDepth[1]; + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const program = render_ndarray_gpu_util.getRenderRGBShader( + gpgpu, sourceShapeRowColDepth[1]); + + const sourceTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(sourceShapeRowColDepth); + + const sourceTex = + gpgpu.createMatrixTexture(sourceTexShapeRC[0], sourceTexShapeRC[1]); + gpgpu.uploadMatrixToTexture( + sourceTex, sourceTexShapeRC[0], sourceTexShapeRC[1], source); + + const resultTex = gpgpu_util.createColorMatrixTexture( + gpgpu.gl, sourceShapeRowColDepth[0], sourceShapeRowColDepth[1]); + gpgpu.setOutputMatrixTexture( + resultTex, sourceShapeRowColDepth[0], sourceShapeRowColDepth[1]); + render_ndarray_gpu_util.renderToFramebuffer(gpgpu, program, sourceTex); + + const result = new Float32Array( + sourceShapeRowColDepth[0] * sourceShapeRowColDepth[1] * 4); + gpgpu.gl.readPixels( + 0, 0, sourceShapeRowColDepth[1], sourceShapeRowColDepth[0], gpgpu.gl.RGBA, + gpgpu.gl.FLOAT, result); + return result; +} + +describe('render_gpu', () => { + it('Packs a 1x1x3 vector to a 1x1 color texture', () => { + const source = new Float32Array([1, 2, 3]); + const result = uploadRenderRGBDownload(source, [1, 1, 3]); + expect(result).toEqual(new Float32Array([1, 2, 3, 1])); + }); + + it('Packs a 2x2x3 vector to a 2x2 color texture, mirrored vertically', () => { + const source = new Float32Array([1, 2, 3, 30, 20, 10, 2, 3, 4, 40, 30, 20]); + const result = uploadRenderRGBDownload(source, [2, 2, 3]); + // The resulting rendered image is flipped vertically. + expect(result).toEqual(new Float32Array( + [2, 3, 4, 1, 40, 30, 20, 1, 1, 2, 3, 1, 30, 20, 10, 1])); + }); +}); diff --git a/src/math/webgl/reshape_gpu.ts b/src/math/webgl/reshape_gpu.ts new file mode 100644 index 0000000000..a451a78134 --- /dev/null +++ b/src/math/webgl/reshape_gpu.ts @@ -0,0 +1,65 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../../util'; +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource(): string { + return ` + precision highp float; + uniform sampler2D matrixA; + uniform vec2 inputDimCR; + uniform vec2 resultDimCR; + varying vec2 resultUV; + const vec2 halfCR = vec2(0.5, 0.5); + + void main() { + vec2 resultCR = floor(resultUV * resultDimCR); + // indexInFlat = row * stride + column, where stride == numOutputColumns + float indexInFlat = resultCR.y * resultDimCR.x + resultCR.x; + + vec2 inputCR = vec2( + mod(indexInFlat, inputDimCR.x), // col = indexInFlat % numInputColumns + floor(indexInFlat / inputDimCR.x) // row = indexInFlat / numInputColumns + ) + halfCR; + + vec2 inputUV = inputCR / inputDimCR; + gl_FragColor = texture2D(matrixA, inputUV); + }`; +} + +export function reshape( + gpgpu: GPGPUContext, reshapeProgram: WebGLProgram, a: WebGLTexture, + aNumRows: number, aNumCols: number, result: WebGLTexture, + resultNumRows: number, resultNumCols: number) { + const inputSize = aNumRows * aNumCols; + const outputSize = resultNumCols * resultNumRows; + util.assert( + inputSize === outputSize, + `The input size (${inputSize}) and output size (${outputSize}) ` + + `must match`); + + gpgpu.setOutputMatrixTexture(result, resultNumRows, resultNumCols); + gpgpu.setProgram(reshapeProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + + const inputDimCRLocation = gpgpu.getUniformLocation('inputDimCR'); + gpgpu.gl.uniform2f(inputDimCRLocation, aNumCols, aNumRows); + + const resultDimCRLocation = gpgpu.getUniformLocation('resultDimCR'); + gpgpu.gl.uniform2f(resultDimCRLocation, resultNumCols, resultNumRows); + + gpgpu.executeProgram(); +} diff --git a/src/math/webgl/reshape_gpu_test.ts b/src/math/webgl/reshape_gpu_test.ts new file mode 100644 index 0000000000..0f83a6e69e --- /dev/null +++ b/src/math/webgl/reshape_gpu_test.ts @@ -0,0 +1,88 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as reshape_gpu from './reshape_gpu'; + +describe('reshape_gpu', () => { + let gpgpu: GPGPUContext; + + beforeEach(() => { + gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + }); + + afterEach(() => { + gpgpu.dispose(); + }); + + it('reshape a 2x3 matrix into the same size', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const result = uploadReshapeDownload(a, 2, 3, 2, 3); + expect(result).toEqual(a); + }); + + it('reshape a 2x3 matrix into a column (6x1)', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const result = uploadReshapeDownload(a, 2, 3, 6, 1); + expect(result).toEqual(a); + }); + + it('reshape a 2x3 matrix into a row (1x6) vector', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const result = uploadReshapeDownload(a, 2, 3, 1, 6); + expect(result).toEqual(a); + }); + + it('reshape a 2x3 into a 3x2 matrix', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const result = uploadReshapeDownload(a, 2, 3, 3, 2); + expect(result).toEqual(a); + }); + + it('reshape a 2x3 into a 3x1 causes an error', () => { + const a = new Float32Array([1, 2, 3, 4, 5, 6]); + const f = () => { + uploadReshapeDownload(a, 2, 3, 3, 1); + }; + + expect(f).toThrowError(); + }); + + function uploadReshapeDownload( + a: Float32Array, aNumRows: number, aNumCols: number, + resultNumRows: number, resultNumCols: number): Float32Array { + const program = gpgpu.createProgram(reshape_gpu.getFragmentShaderSource()); + + const aTexture = gpgpu.createMatrixTexture(aNumRows, aNumCols); + gpgpu.uploadMatrixToTexture(aTexture, aNumRows, aNumCols, a); + + const resultTexture: WebGLTexture = + gpgpu.createMatrixTexture(resultNumRows, resultNumCols); + + reshape_gpu.reshape( + gpgpu, program, aTexture, aNumRows, aNumCols, resultTexture, + resultNumRows, resultNumCols); + + const result = gpgpu.downloadMatrixFromTexture( + resultTexture, resultNumRows, resultNumCols); + + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + + return result; + } +}); diff --git a/src/math/webgl/resize_bilinear_gpu.ts b/src/math/webgl/resize_bilinear_gpu.ts new file mode 100644 index 0000000000..ec57158227 --- /dev/null +++ b/src/math/webgl/resize_bilinear_gpu.ts @@ -0,0 +1,92 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as conv_util from '../conv_util'; + +import {GPGPUContext} from './gpgpu_context'; +import * as webgl_util from './webgl_util'; + +export function getFragmentShaderSource( + inputShapeRCD: [number, number, number], + outputDimensionsRowCol: [number, number], alignCorners: boolean): string { + const depth = inputShapeRCD[2]; + + const inputTexShapeRC = conv_util.computeTexShapeFrom3D(inputShapeRCD); + + const effectiveInputShapeRCD = alignCorners ? + [inputShapeRCD[0] - 1, inputShapeRCD[1] - 1, depth] : + inputShapeRCD; + + const effectiveOutputShapeRCD = alignCorners ? + [outputDimensionsRowCol[0] - 1, outputDimensionsRowCol[1] - 1, depth] : + [outputDimensionsRowCol[0], outputDimensionsRowCol[1], depth]; + + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 resultUV; + const vec2 halfCR = vec2(0.5, 0.5); + + const vec2 inputShapeCR = vec2(${inputShapeRCD[1]}, ${inputShapeRCD[0]}); + const vec2 inputShapeTexCR = vec2( + ${inputTexShapeRC[1]}, ${inputTexShapeRC[0]}); + + const vec2 effectiveInputOverOutputRatioCR = vec2( + ${effectiveInputShapeRCD[1] / effectiveOutputShapeRCD[1]}, + ${effectiveInputShapeRCD[0] / effectiveOutputShapeRCD[0]}); + + float sampleInput(float col, float row, float d) { + vec2 uv = (vec2(col * ${depth}.0 + d, row) + halfCR) / inputShapeTexCR; + return texture2D(matrixA, uv).r; + } + + void main() { + vec2 yTexCR = floor(gl_FragCoord.xy); + + // Map from 2D (yTexR, yTexC) to 3D (yR, yC, d). + vec2 yCR = vec2(floor(yTexCR.x / ${depth}.0), yTexCR.y); + float d = mod(yTexCR.x, ${depth}.0); + + // Fractional source index. + vec2 sourceFracIndexCR = yCR * effectiveInputOverOutputRatioCR; + + // Compute the four integer indices. + vec2 sourceFloorCR = floor(sourceFracIndexCR); + vec2 sourceCeilCR = min(inputShapeCR - 1.0, ceil(sourceFracIndexCR)); + + float topLeft = sampleInput(sourceFloorCR[0], sourceFloorCR[1], d); + float bottomLeft = sampleInput(sourceFloorCR[0], sourceCeilCR[1], d); + float topRight = sampleInput(sourceCeilCR[0], sourceFloorCR[1], d); + float bottomRight = sampleInput(sourceCeilCR[0], sourceCeilCR[1], d); + + vec2 fracCR = sourceFracIndexCR - sourceFloorCR; + + float top = topLeft + (topRight - topLeft) * fracCR[0]; + float bottom = bottomLeft + (bottomRight - bottomLeft) * fracCR[0]; + float newValue = top + (bottom - top) * fracCR[1]; + + gl_FragColor = vec4(newValue, 0.0, 0.0, 0.0); + }`; +} + +export function resizeBilinear( + gpgpu: GPGPUContext, resizeBilinearProgram: WebGLProgram, a: WebGLTexture, + result: WebGLTexture, resultShapeRowCol: [number, number]) { + gpgpu.setOutputMatrixTexture( + result, resultShapeRowCol[0], resultShapeRowCol[1]); + gpgpu.setProgram(resizeBilinearProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} \ No newline at end of file diff --git a/src/math/webgl/resize_bilinear_gpu_test.ts b/src/math/webgl/resize_bilinear_gpu_test.ts new file mode 100644 index 0000000000..e0d20e78bd --- /dev/null +++ b/src/math/webgl/resize_bilinear_gpu_test.ts @@ -0,0 +1,127 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as conv_util from '../conv_util'; +import {NDArrayMathCPU} from '../math_cpu'; +import {Array3D, NDArray} from '../ndarray'; + +import {GPGPUContext} from './gpgpu_context'; +import * as resize_bilinear_gpu from './resize_bilinear_gpu'; + +describe('resize bilinear', () => { + function uploadResizeBilinearDownload( + a: Float32Array, aShapeRowColDepth: [number, number, number], + outputDimensionsRowCol: [number, number], + alignCorners: boolean): Float32Array { + const aTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(aShapeRowColDepth); + + const resultShapeRCD: [number, number, number] = [ + outputDimensionsRowCol[0], outputDimensionsRowCol[1], aShapeRowColDepth[2] + ]; + + const resultTexShapeRC: [number, number] = + conv_util.computeTexShapeFrom3D(resultShapeRCD); + + const gpgpu = new GPGPUContext(); + gpgpu.enableAutomaticDebugValidation(true); + + const shaderSource = resize_bilinear_gpu.getFragmentShaderSource( + aShapeRowColDepth, outputDimensionsRowCol, alignCorners); + const program = gpgpu.createProgram(shaderSource); + + const aTex = gpgpu.createMatrixTexture(aTexShapeRC[0], aTexShapeRC[1]); + const resultTex = + gpgpu.createMatrixTexture(resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.uploadMatrixToTexture(aTex, aTexShapeRC[0], aTexShapeRC[1], a); + + resize_bilinear_gpu.resizeBilinear( + gpgpu, program, aTex, resultTex, resultTexShapeRC); + + const result = gpgpu.downloadMatrixFromTexture( + resultTex, resultTexShapeRC[0], resultTexShapeRC[1]); + + gpgpu.deleteMatrixTexture(resultTex); + gpgpu.deleteMatrixTexture(aTex); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; + } + + it('simple bilinear', () => { + const a = new Float32Array([2, 2, 4, 4]); + + const result = uploadResizeBilinearDownload(a, [2, 2, 1], [3, 3], false); + + test_util.expectArraysClose( + result, new Float32Array([2, 2, 2, 10 / 3, 10 / 3, 10 / 3, 4, 4, 4]), + 1e-4); + }); + + it('simple alignCorners=true', () => { + const a = new Float32Array([2, 2, 4, 4]); + + const result = uploadResizeBilinearDownload(a, [2, 2, 1], [3, 3], true); + + test_util.expectArraysClose( + result, new Float32Array([2, 2, 2, 3, 3, 3, 4, 4, 4]), 1e-4); + }); + + it('matches tensorflow w/ random numbers alignCorners=false', () => { + const a = new Float32Array([ + 1.19074044, 0.91373104, 2.01611669, -0.52270832, 0.38725395, 1.30809779, + 0.61835143, 3.49600659, 2.09230986, 0.56473997, 0.03823943, 1.19864896 + ]); + + const result = uploadResizeBilinearDownload(a, [2, 3, 2], [4, 5], false); + + test_util.expectArraysClose( + result, new Float32Array([ + 1.19074047, 0.91373104, 1.68596613, 0.05186744, 1.69034398, + -0.15654698, 0.7130264, 0.94193673, 0.38725394, 1.30809784, + 0.9045459, 2.20486879, 1.59434628, 0.89455694, 1.68591988, + 0.26748738, 0.58103991, 1.00690198, 0.21274668, 1.25337338, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893, + 0.6183514, 3.49600649, 1.50272655, 1.73724651, 1.68149579, + 0.69152176, 0.44905344, 1.07186723, 0.03823943, 1.19864893 + ]), + 1e-4); + }); + + it('matches tensorflow w/ random numbers alignCorners=true', () => { + const a = new Float32Array([ + 1.56324531, 2.13817752, 1.44398421, 1.07632684, 0.59306785, -0.36970865, + 1.62451879, 1.8367334, 1.13944798, 2.01993218, 2.01919952, 2.67524054 + ]); + + const result = uploadResizeBilinearDownload(a, [2, 3, 2], [4, 5], true); + + test_util.expectArraysClose( + result, new Float32Array([ + 1.5632453, 2.13817763, 1.50361478, 1.60725224, 1.44398427, + 1.07632685, 1.01852608, 0.35330909, 0.59306782, -0.36970866, + 1.58366978, 2.03769612, 1.46307099, 1.71427906, 1.3424722, + 1.39086199, 1.20545864, 1.01806819, 1.06844509, 0.6452744, + 1.60409427, 1.93721485, 1.42252707, 1.82130599, 1.24096, + 1.70539713, 1.3923912, 1.68282723, 1.54382229, 1.66025746, + 1.62451875, 1.83673346, 1.38198328, 1.92833281, 1.13944793, + 2.01993227, 1.57932377, 2.34758639, 2.01919961, 2.67524052 + ]), + 1e-4); + }); +}); \ No newline at end of file diff --git a/src/math/webgl/shader_compiler.ts b/src/math/webgl/shader_compiler.ts new file mode 100644 index 0000000000..f87bde891c --- /dev/null +++ b/src/math/webgl/shader_compiler.ts @@ -0,0 +1,126 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from '../../util'; +import {NDArray} from '../ndarray'; + +export type Input = { + name: string; array: NDArray; +}; + +export function makeShaderKey(inputs: NDArray[], output: NDArray): string { + const ins = inputs.map(x => x.shape + '_' + x.getTextureShapeRC()); + return ins.join('_') + '_' + output.shape + '_' + output.getTextureShapeRC(); +} + +export function makeShader( + inputs: Input[], output: NDArray, userCode: string): string { + const inputPrefixSnippet = + inputs.map(x => `uniform sampler2D ${x.name};`).join('\n'); + const inputSamplingSnippet = + inputs.map(x => getInputSamplingSnippet(x)).join('\n'); + const outTexShape = output.getTextureShapeRC(); + const outputSamplingSnippet = + getOutputSamplingSnippet(output.shape, outTexShape); + const source = [ + SHADER_PREFIX, inputPrefixSnippet, SAMPLE_2D_SNIPPET, inputSamplingSnippet, + outputSamplingSnippet, userCode + ].join('\n'); + return source; +} + +function getInputSamplingSnippet(input: Input) { + const arr = input.array; + const shape = arr.shape; + const texShape = arr.getTextureShapeRC(shape as [number, number]); + switch (shape.length) { + case 2: + return getSampler2D(input.name, shape as [number, number], texShape); + default: + throw new Error(`${arr.rank}-D input sampling is not yet supported`); + } +} + +function getOutputSamplingSnippet( + outShape: number[], outTexShape: [number, number]): string { + switch (outShape.length) { + case 2: + return getOutput2DCoords(outShape as [number, number], outTexShape); + default: + throw new Error( + `${outShape.length}-D output sampling is not yet supported`); + } +} + +const SHADER_PREFIX = ` + precision highp float; + varying vec2 resultUV; + const vec2 halfCR = vec2(0.5, 0.5); + + void setOutput(float val) { + gl_FragColor = vec4(val, 0, 0, 0); + } +`; + +const SAMPLE_2D_SNIPPET = ` + float sample2D(sampler2D texture, float texNumR, float texNumC, float numC, + float row, float col) { + float index = dot(vec2(row, col), vec2(numC, 1.0)); + float texR = floor(index / texNumC); + float texC = mod(index, texNumC); + vec2 uv = (vec2(texC, texR) + halfCR) / vec2(texNumC, texNumR); + return texture2D(texture, uv).r; + } +`; + +function getOutput2DCoords( + shape: [number, number], texShape: [number, number]) { + if (util.arraysEqual(shape, texShape)) { + return ` + vec2 getOutputCoords() { + return floor(gl_FragCoord.yx); + } + `; + } + return ` + vec2 getOutputCoords() { + vec2 resTexRC = floor(gl_FragCoord.yx); + float index = dot(resTexRC, vec2(${texShape[1]}.0, 1.0)); + float r = floor(index / ${shape[1]}.0); + float c = mod(index, ${shape[1]}.0); + return vec2(r, c); + } + `; +} + +function getSampler2D( + texName: string, shape: [number, number], texShape: [number, number]) { + const funcName = 'get' + texName.charAt(0).toUpperCase() + texName.slice(1); + const tR = texShape[0]; + const tC = texShape[1]; + if (util.arraysEqual(shape, texShape)) { + return ` + float ${funcName}(float row, float col) { + vec2 uv = (vec2(col, row) + halfCR) / vec2(${tC}.0, ${tR}.0); + return texture2D(${texName}, uv).r; + } + `; + } + return ` + float ${funcName}(float row, float col) { + return sample2D(${texName}, ${tR}.0, ${tC}.0, ${shape[1]}.0, row, col); + } + `; +} diff --git a/src/math/webgl/sigmoid_gpu.ts b/src/math/webgl/sigmoid_gpu.ts new file mode 100644 index 0000000000..eff7dfa296 --- /dev/null +++ b/src/math/webgl/sigmoid_gpu.ts @@ -0,0 +1,37 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getSigmoidUnaryOp(): string { + return 'gl_FragColor = vec4(1.0 / (1.0 + exp(-1.0 * value)), 0, 0, 0);'; +} + +export function getSigmoidFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getSigmoidUnaryOp()); +} + +export function sigmoid( + gpgpu: GPGPUContext, sigmoidProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, sigmoidProgram, a, rows, columns, result); +} + +export function uploadSigmoidDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload( + a, rows, columns, getSigmoidUnaryOp()); +} \ No newline at end of file diff --git a/src/math/webgl/sigmoid_gpu_test.ts b/src/math/webgl/sigmoid_gpu_test.ts new file mode 100644 index 0000000000..68b032926d --- /dev/null +++ b/src/math/webgl/sigmoid_gpu_test.ts @@ -0,0 +1,38 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as util from '../../util'; +import * as sigmoid_gpu from './sigmoid_gpu'; + +describe('sigmoid_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(28 * 32); + const result = sigmoid_gpu.uploadSigmoidDownload(a, 28, 32); + expect(result.length).toEqual(a.length); + }); + + it('Sigmoid equals CPU', () => { + const size = 10; + const a = test_util.randomArrayInRange(size, -1, 1); + const expectedResult = new Float32Array(size); + for (let i = 0; i < a.length; i++) { + expectedResult[i] = 1 / (1 + Math.exp(-a[i])); + } + + const result = sigmoid_gpu.uploadSigmoidDownload(a, 1, size); + test_util.expectArraysClose(result, expectedResult, 1e-6); + }); +}); diff --git a/src/math/webgl/step_gpu.ts b/src/math/webgl/step_gpu.ts new file mode 100644 index 0000000000..f61bcc5272 --- /dev/null +++ b/src/math/webgl/step_gpu.ts @@ -0,0 +1,39 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +function getStepUnaryOp(): string { + return ` + float res = value == value ? (value > 0.0 ? 1.0 : 0.0) : value; + gl_FragColor = vec4(res, 0, 0, 0); + `; +} + +export function getFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getStepUnaryOp()); +} + +export function step( + gpgpu: GPGPUContext, stepProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, stepProgram, a, rows, columns, result); +} + +export function uploadStepDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getStepUnaryOp()); +} diff --git a/src/math/webgl/step_gpu_test.ts b/src/math/webgl/step_gpu_test.ts new file mode 100644 index 0000000000..7b207ad580 --- /dev/null +++ b/src/math/webgl/step_gpu_test.ts @@ -0,0 +1,65 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as step_gpu from './step_gpu'; + +describe('step_gpu', () => { + it('returns a matrix with the shape of the input matrix', () => { + const a = new Float32Array(67 * 901); + const result = step_gpu.uploadStepDownload(a, 67, 901); + expect(result.length).toEqual(a.length); + }); + + it('preserves zeroes from the input matrix', () => { + const a = new Float32Array(1); + const result = step_gpu.uploadStepDownload(a, 1, 1); + expect(result[0]).toEqual(0); + }); + + it('preserves ones from the input matrix', () => { + const a = new Float32Array([1]); + const result = step_gpu.uploadStepDownload(a, 1, 1); + expect(result[0]).toEqual(a[0]); + }); + + it('transforms negative values to zeroes', () => { + const a = new Float32Array([-123.45]); + const result = step_gpu.uploadStepDownload(a, 1, 1); + expect(result[0]).toEqual(0); + }); + + it('transforms positive values to ones', () => { + const a = new Float32Array([0.1]); + const result = step_gpu.uploadStepDownload(a, 1, 1); + expect(result[0]).toEqual(1); + }); + + it('operates on every element of a matrix', () => { + const a = new Float32Array(24); + a.fill(0.1); + const result = step_gpu.uploadStepDownload(a, 4, 6); + const expected = new Float32Array(a.length); + expected.fill(1); + test_util.expectArraysClose(result, expected, 0); + }); + + it('operates on a heterogeneous matrix', () => { + const a = new Float32Array([-1, 0, 100, -0.001]); + const result = step_gpu.uploadStepDownload(a, 4, 1); + const expected = new Float32Array([0, 0, 1, 0]); + test_util.expectArraysClose(result, expected, 0); + }); +}); diff --git a/src/math/webgl/tex_util.ts b/src/math/webgl/tex_util.ts new file mode 100644 index 0000000000..ae4a5815d0 --- /dev/null +++ b/src/math/webgl/tex_util.ts @@ -0,0 +1,233 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export function getUnpackedMatrixTextureShapeWidthHeight( + rows: number, columns: number): [number, number] { + return [columns, rows]; +} + +export function getUnpackedArraySizeFromMatrixSize( + matrixSize: number, channelsPerTexture: number): number { + return matrixSize * channelsPerTexture; +} + +export function getColorMatrixTextureShapeWidthHeight( + rows: number, columns: number): [number, number] { + return [columns * 4, rows]; +} + +export function getMatrixSizeFromUnpackedArraySize( + unpackedSize: number, channelsPerTexture: number): number { + if (unpackedSize % channelsPerTexture !== 0) { + throw new Error( + 'unpackedSize (' + unpackedSize + ') must be a multiple of ' + + channelsPerTexture); + } + return unpackedSize / channelsPerTexture; +} + +export function encodeMatrixToUnpackedArray( + matrix: Float32Array, unpackedArray: Float32Array, + channelsPerTexture: number) { + const requiredSize = + getUnpackedArraySizeFromMatrixSize(matrix.length, channelsPerTexture); + if (unpackedArray.length < requiredSize) { + throw new Error( + 'unpackedArray length (' + unpackedArray.length + + ') must be >= ' + requiredSize); + } + let dst = 0; + for (let src = 0; src < matrix.length; ++src) { + unpackedArray[dst] = matrix[src]; + dst += channelsPerTexture; + } +} + +export function decodeMatrixFromUnpackedArray( + unpackedArray: Float32Array, matrix: Float32Array, + channelsPerTexture: number) { + const requiredSize = getMatrixSizeFromUnpackedArraySize( + unpackedArray.length, channelsPerTexture); + if (matrix.length < requiredSize) { + throw new Error( + 'matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + let dst = 0; + for (let src = 0; src < unpackedArray.length; src += channelsPerTexture) { + matrix[dst++] = unpackedArray[src]; + } +} + +export function getPackedMatrixTextureShapeWidthHeight( + rows: number, columns: number): [number, number] { + return [Math.ceil(columns / 2), Math.ceil(rows / 2)]; +} + +export function getPackedRGBAArraySizeFromMatrixShape( + rows: number, columns: number): number { + const [w, h] = getPackedMatrixTextureShapeWidthHeight(rows, columns); + return w * h * 4; +} + +export function encodeMatrixToPackedRGBA( + matrix: Float32Array, rows: number, columns: number, + packedRGBA: Float32Array) { + const requiredSize = getPackedRGBAArraySizeFromMatrixShape(rows, columns); + if (packedRGBA.length < requiredSize) { + throw new Error( + 'packedRGBA length (' + packedRGBA.length + + ') must be >= ' + requiredSize); + } + /* + Unpacked matrix, row-major order in Float32Array[16]: A B C D + E F G H + I J K L + M N O P + + Packed matrix, 2x2 RGBA32 texture (memory view): ABEF CDGH IJMN KLOP + + Packed matrix, 2x2 RGBA32 texture (matrix view): AB|CD + EF|GH + --+-- + IJ|KL + MN|OP + */ + const [textureWidth, textureHeight] = + getPackedMatrixTextureShapeWidthHeight(rows, columns); + const oddWidth = (columns % 2) === 1; + const oddHeight = (rows % 2) === 1; + const widthInFullBlocks = Math.floor(columns / 2); + const heightInFullBlocks = Math.floor(rows / 2); + + // loop over full 2x2 blocks + { + const dstStride = (oddWidth ? 4 : 0); + const oneRow = columns; + let dst = 0; + for (let blockY = 0; blockY < heightInFullBlocks; ++blockY) { + const matrixSrcRow = (blockY * 2 * columns); + for (let blockX = 0; blockX < widthInFullBlocks; ++blockX) { + const matrixSrcCol = blockX * 2; + const src = matrixSrcRow + matrixSrcCol; + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 1] = matrix[src + 1]; + packedRGBA[dst + 2] = matrix[src + oneRow]; + packedRGBA[dst + 3] = matrix[src + oneRow + 1]; + dst += 4; + } + dst += dstStride; + } + } + + // loop down final odd column + if (oddWidth) { + let src = columns - 1; + let dst = (textureWidth - 1) * 4; + const srcStride = 2 * columns; + const dstStride = textureWidth * 4; + for (let blockY = 0; blockY < heightInFullBlocks; ++blockY) { + packedRGBA[dst] = matrix[src]; + packedRGBA[dst + 2] = matrix[src + columns]; + src += srcStride; + dst += dstStride; + } + } + + // loop across final row + if (oddHeight) { + let src = (rows - 1) * columns; + let dst = (textureHeight - 1) * textureWidth * 4; + for (let blockX = 0; blockX < widthInFullBlocks; ++blockX) { + packedRGBA[dst++] = matrix[src++]; + packedRGBA[dst++] = matrix[src++]; + dst += 2; + } + } + + // fill in bottom-right texel + if (oddWidth && oddHeight) { + packedRGBA[packedRGBA.length - 4] = matrix[matrix.length - 1]; + } + + return packedRGBA; +} + +export function decodeMatrixFromPackedRGBA( + packedRGBA: Float32Array, rows: number, columns: number, + matrix: Float32Array): Float32Array { + const requiredSize = rows * columns; + if (requiredSize < matrix.length) { + throw new Error( + 'matrix length (' + matrix.length + ') must be >= ' + requiredSize); + } + const oddWidth = (columns % 2) === 1; + const oddHeight = (rows % 2) === 1; + const widthInFullBlocks = Math.floor(columns / 2); + const heightInFullBlocks = Math.floor(rows / 2); + const [textureWidth, textureHeight] = + getPackedMatrixTextureShapeWidthHeight(rows, columns); + + // loop over full 2x2 blocks + { + const srcStride = oddWidth ? 4 : 0; + const dstStride = columns + (oddWidth ? 1 : 0); + let src = 0; + let dstRow1 = 0; + let dstRow2 = columns; + for (let blockY = 0; blockY < heightInFullBlocks; ++blockY) { + for (let blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow1++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + matrix[dstRow2++] = packedRGBA[src++]; + } + src += srcStride; + dstRow1 += dstStride; + dstRow2 += dstStride; + } + } + + // loop down final column + if (oddWidth) { + let src = (textureWidth - 1) * 4; + let dst = columns - 1; + const srcStride = textureWidth * 4; + const dstStride = 2 * columns; + for (let blockY = 0; blockY < heightInFullBlocks; ++blockY) { + matrix[dst] = packedRGBA[src]; + matrix[dst + columns] = packedRGBA[src + 2]; + src += srcStride; + dst += dstStride; + } + } + + // loop across final row + if (oddHeight) { + let src = (textureHeight - 1) * textureWidth * 4; + let dst = (rows - 1) * columns; + for (let blockX = 0; blockX < widthInFullBlocks; ++blockX) { + matrix[dst++] = packedRGBA[src++]; + matrix[dst++] = packedRGBA[src++]; + src += 2; + } + } + + // fill in bottom-right cell + if (oddWidth && oddHeight) { + matrix[matrix.length - 1] = packedRGBA[packedRGBA.length - 4]; + } + + return matrix; +} diff --git a/src/math/webgl/tex_util_test.ts b/src/math/webgl/tex_util_test.ts new file mode 100644 index 0000000000..c278a924b0 --- /dev/null +++ b/src/math/webgl/tex_util_test.ts @@ -0,0 +1,301 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as tex_util from './tex_util'; + +describe('tex_util getUnpackedMatrixTextureShapeWidthHeight', () => { + it('[1x1] => [1x1]', () => { + expect(tex_util.getUnpackedMatrixTextureShapeWidthHeight(1, 1)).toEqual([ + 1, 1 + ]); + }); + + it('[MxN] => [NxM]', () => { + expect(tex_util.getUnpackedMatrixTextureShapeWidthHeight(123, 456)) + .toEqual([456, 123]); + }); +}); + +describe('tex_util getPackedMatrixTextureShapeWidthHeight', () => { + it('[1x1] => [1x1]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(1, 1); + expect(shape).toEqual([1, 1]); + }); + + it('[1x2] => [1x1]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(1, 2); + expect(shape).toEqual([1, 1]); + }); + + it('[2x1] => [1x1]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(2, 1); + expect(shape).toEqual([1, 1]); + }); + + it('[2x2] => [1x1]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(2, 2); + expect(shape).toEqual([1, 1]); + }); + + it('[3x3] => [2x2]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(3, 3); + expect(shape).toEqual([2, 2]); + }); + + it('[4x3] => [2x2]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(4, 3); + expect(shape).toEqual([2, 2]); + }); + + it('[3x4] => [2x2]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(3, 4); + expect(shape).toEqual([2, 2]); + }); + + it('[4x4] => [2x2]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(4, 4); + expect(shape).toEqual([2, 2]); + }); + + it('[1024x1024] => [512x512]', () => { + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(1024, 1024); + expect(shape).toEqual([512, 512]); + }); + + it('[MxN] => [ceil(N/2)xceil(M/2)]', () => { + const M = 123; + const N = 5013; + const shape = tex_util.getPackedMatrixTextureShapeWidthHeight(M, N); + expect(shape).toEqual([Math.ceil(N / 2), Math.ceil(M / 2)]); + }); +}); + +describe('tex_util encodeMatrixToUnpackedArray, channels = 4', () => { + it('1x1 writes the only matrix array value to the only texel', () => { + const matrix = new Float32Array([1]); + const unpackedRGBA = new Float32Array([0, 0, 0, 0]); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 4); + test_util.expectArraysClose( + unpackedRGBA, new Float32Array([1, 0, 0, 0]), 0); + }); + + it('1x1 can upload texels with values greater than 1', () => { + const matrix = new Float32Array([100]); + const unpackedRGBA = new Float32Array([0, 0, 0, 0]); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 4); + test_util.expectArraysClose( + unpackedRGBA, new Float32Array([100, 0, 0, 0]), 0); + }); + + it('1x4 each texel has 4 elements with matrix value in R channel', () => { + const matrix = new Float32Array([1, 2, 3, 4]); + const unpackedRGBA = new Float32Array(16); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 4); + test_util.expectArraysClose( + unpackedRGBA, + new Float32Array([1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]), 0); + }); +}); + +describe('tex_util encodeMatrixToUnpackedArray, channels = 1', () => { + it('1x1 writes the only matrix array value to the only texel', () => { + const matrix = new Float32Array([1]); + const unpackedRGBA = new Float32Array([0]); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 1); + test_util.expectArraysClose(unpackedRGBA, new Float32Array([1]), 0); + }); + + it('1x1 can upload texels with values greater than 1', () => { + const matrix = new Float32Array([100]); + const unpackedRGBA = new Float32Array([0]); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 1); + test_util.expectArraysClose(unpackedRGBA, new Float32Array([100]), 0); + }); + + it('1x4 each texel has 4 elements with matrix value in R channel', () => { + const matrix = new Float32Array([1, 2, 3, 4]); + const unpackedRGBA = new Float32Array(4); + tex_util.encodeMatrixToUnpackedArray(matrix, unpackedRGBA, 1); + test_util.expectArraysClose( + unpackedRGBA, new Float32Array([1, 2, 3, 4]), 0); + }); +}); + +describe('tex_util decodeMatrixFromUnpackedArray', () => { + it('1x1 writes the only matrix array value to the first element', () => { + const unpackedRGBA = new Float32Array([1, 0, 0, 0]); + const matrix = new Float32Array(1); + tex_util.decodeMatrixFromUnpackedArray(unpackedRGBA, matrix, 4); + expect(matrix.length).toEqual(1); + expect(matrix[0]).toEqual(1); + }); + + it('1x2 writes the second texel R component to the second element', () => { + const unpackedRGBA = new Float32Array([1, 0, 0, 0, 2, 0, 0, 0]); + const matrix = new Float32Array(2); + tex_util.decodeMatrixFromUnpackedArray(unpackedRGBA, matrix, 4); + expect(matrix.length).toEqual(2); + test_util.expectArraysClose(matrix, new Float32Array([1, 2]), 0); + }); +}); + +describe('tex_util encodeMatrixToPackedRGBA', () => { + it('1x1 loads the element into R and 0\'s into GBA', () => { + const matrix = new Float32Array([1]); + const packedRGBA = new Float32Array(4); + tex_util.encodeMatrixToPackedRGBA(matrix, 1, 1, packedRGBA); + test_util.expectArraysClose(packedRGBA, new Float32Array([1, 0, 0, 0]), 0); + }); + + it('1x2 loads the second element into G and 0\'s into BA', () => { + const matrix = new Float32Array([1, 2]); + const packedRGBA = new Float32Array(4); + tex_util.encodeMatrixToPackedRGBA(matrix, 1, 2, packedRGBA); + test_util.expectArraysClose(packedRGBA, new Float32Array([1, 2, 0, 0]), 0); + }); + + it('2x1 loads the second element into G and 0\'s into BA', () => { + const matrix = new Float32Array([1, 2]); + const packedRGBA = new Float32Array(4); + tex_util.encodeMatrixToPackedRGBA(matrix, 2, 1, packedRGBA); + test_util.expectArraysClose(packedRGBA, new Float32Array([1, 0, 2, 0]), 0); + }); + + it('2x2 exactly fills one texel', () => { + const matrix = new Float32Array([1, 2, 3, 4]); + const packedRGBA = new Float32Array(4); + tex_util.encodeMatrixToPackedRGBA(matrix, 2, 2, packedRGBA); + test_util.expectArraysClose(packedRGBA, new Float32Array([1, 2, 3, 4]), 0); + }); + + it('4x3 pads the final column G and A channels with 0', () => { + /* + 1 2 3 1 2 4 5 | 3 0 6 0 + 4 5 6 => -----------+----------- + 7 8 9 7 8 10 11 | 9 0 12 0 + 10 11 12 + */ + const matrix = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const packedRGBA = new Float32Array(16); + tex_util.encodeMatrixToPackedRGBA(matrix, 4, 3, packedRGBA); + test_util.expectArraysClose( + packedRGBA, + new Float32Array([1, 2, 4, 5, 3, 0, 6, 0, 7, 8, 10, 11, 9, 0, 12, 0]), + 0); + }); + + it('3x4 pads the final row B and A channels with 0', () => { + /* + 1 2 3 4 1 2 5 6 | 3 4 7 8 + 5 6 7 8 => ---------+---------- + 9 10 11 12 9 10 0 0 | 11 12 0 0 + */ + const matrix = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + const packedRGBA = new Float32Array(16); + tex_util.encodeMatrixToPackedRGBA(matrix, 3, 4, packedRGBA); + test_util.expectArraysClose( + packedRGBA, + new Float32Array([1, 2, 5, 6, 3, 4, 7, 8, 9, 10, 0, 0, 11, 12, 0, 0]), + 0); + }); + + it('3x3 bottom-right texel is R000', () => { + /* + 1 2 3 1 2 4 5 | 3 0 6 0 + 4 5 6 => --------+-------- + 7 8 9 7 8 0 0 | 9 0 0 0 + */ + const matrix = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + const packedRGBA = new Float32Array(16); + tex_util.encodeMatrixToPackedRGBA(matrix, 3, 3, packedRGBA); + test_util.expectArraysClose( + packedRGBA, + new Float32Array([1, 2, 4, 5, 3, 0, 6, 0, 7, 8, 0, 0, 9, 0, 0, 0]), 0); + }); +}); + +describe('tex_util decodeMatrixFromPackedRGBA', () => { + it('1x1 matrix only loads R component from only texel', () => { + const packedRGBA = new Float32Array([1, 0, 0, 0]); + const matrix = new Float32Array(1); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 1, 1, matrix); + expect(matrix[0]).toEqual(1); + }); + + it('1x2 matrix loads RG from only texel', () => { + const packedRGBA = new Float32Array([1, 2, 0, 0]); + const matrix = new Float32Array(2); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 1, 2, matrix); + test_util.expectArraysClose(matrix, new Float32Array([1, 2]), 0); + }); + + it('2x1 matrix loads RB from only texel', () => { + const packedRGBA = new Float32Array([1, 0, 2, 0]); + const matrix = new Float32Array(2); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 2, 1, matrix); + test_util.expectArraysClose(matrix, new Float32Array([1, 2]), 0); + }); + + it('2x2 matrix loads RGBA from only texel', () => { + const packedRGBA = new Float32Array([1, 2, 3, 4]); + const matrix = new Float32Array(4); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 2, 2, matrix); + test_util.expectArraysClose(matrix, new Float32Array([1, 2, 3, 4]), 0); + }); + + it('4x3 final column only reads RB from edge texels', () => { + /* + 1 2 4 5 | 3 0 6 0 1 2 3 + -----------+----------- => 4 5 6 + 7 8 10 11 | 9 0 12 0 7 8 9 + 10 11 12 + */ + const packedRGBA = + new Float32Array([1, 2, 4, 5, 3, 0, 6, 0, 7, 8, 10, 11, 9, 0, 12, 0]); + const matrix = new Float32Array(12); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 4, 3, matrix); + test_util.expectArraysClose( + matrix, new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), 0); + }); + + it('3x4 final row only reads RG from edge texels', () => { + /* + 1 2 5 6 | 3 4 7 8 1 2 3 4 + ---------+---------- => 5 6 7 8 + 9 10 0 0 | 11 12 0 0 9 10 11 12 + */ + const packedRGBA = + new Float32Array([1, 2, 5, 6, 3, 4, 7, 8, 9, 10, 0, 0, 11, 12, 0, 0]); + const matrix = new Float32Array(12); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 3, 4, matrix); + test_util.expectArraysClose( + matrix, new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), 0); + }); + + it('3x3 bottom-right only reads R from corner texel', () => { + /* + 1 2 4 5 | 3 0 6 0 1 2 3 + --------+-------- => 4 5 6 + 7 8 0 0 | 9 0 0 0 7 8 9 + */ + const packedRGBA = + new Float32Array([1, 2, 4, 5, 3, 0, 6, 0, 7, 8, 0, 0, 9, 0, 0, 0]); + const matrix = new Float32Array(9); + tex_util.decodeMatrixFromPackedRGBA(packedRGBA, 3, 3, matrix); + test_util.expectArraysClose( + matrix, new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]), 0); + }); +}); diff --git a/src/math/webgl/texture_manager.ts b/src/math/webgl/texture_manager.ts new file mode 100644 index 0000000000..6aa8374676 --- /dev/null +++ b/src/math/webgl/texture_manager.ts @@ -0,0 +1,92 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export class TextureManager { + private numUsedTextures = 0; + private numFreeTextures = 0; + private freeTextures: {[shape: string]: WebGLTexture[]} = {}; + private logEnabled = false; + private usedTextureCount: {[shape: string]: number} = {}; + + constructor(private gpgpu: GPGPUContext) {} + + acquireTexture(shapeRC: [number, number]): WebGLTexture { + const shapeKey = getKeyFromTextureShape(shapeRC); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + if (!(shapeKey in this.usedTextureCount)) { + this.usedTextureCount[shapeKey] = 0; + } + this.usedTextureCount[shapeKey]++; + + if (this.freeTextures[shapeKey].length > 0) { + this.numFreeTextures--; + this.numUsedTextures++; + this.log(); + return this.freeTextures[shapeKey].shift()!; + } + this.numUsedTextures++; + this.log(); + + return this.gpgpu.createMatrixTexture(shapeRC[0], shapeRC[1]); + } + + releaseTexture(texture: WebGLTexture, shape: [number, number]): void { + const shapeKey = getKeyFromTextureShape(shape); + if (!(shapeKey in this.freeTextures)) { + this.freeTextures[shapeKey] = []; + } + this.freeTextures[shapeKey].push(texture); + this.numFreeTextures++; + this.numUsedTextures--; + this.usedTextureCount[shapeKey]--; + this.log(); + } + + private log() { + if (!this.logEnabled) { + return; + } + const total = this.numFreeTextures + this.numUsedTextures; + console.log( + 'Free/Used', this.numFreeTextures + ' / ' + this.numUsedTextures, + `(${total})`); + } + + getNumUsedTextures(): number { + return this.numUsedTextures; + } + + getNumFreeTextures(): number { + return this.numFreeTextures; + } + + dispose() { + for (const shape in this.freeTextures) { + if (this.freeTextures.hasOwnProperty(shape)) { + for (let i = 0; i < this.freeTextures[shape].length; i++) { + this.gpgpu.deleteMatrixTexture(this.freeTextures[shape][i]); + } + } + } + } +} + +function getKeyFromTextureShape(shapeRowsCol: [number, number]): string { + return shapeRowsCol[0] + '_' + shapeRowsCol[1]; +} diff --git a/src/math/webgl/trig_gpu.ts b/src/math/webgl/trig_gpu.ts new file mode 100644 index 0000000000..e4edd6cf71 --- /dev/null +++ b/src/math/webgl/trig_gpu.ts @@ -0,0 +1,66 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; +import * as unaryop_gpu from './unaryop_gpu'; + +/** + * Sine + */ +function getSinUnaryOp(): string { + return ` + gl_FragColor = vec4(sin(value), 0, 0, 0); + `; +} + +export function getSinFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getSinUnaryOp()); +} + +export function sin( + gpgpu: GPGPUContext, sinProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, sinProgram, a, rows, columns, result); +} + +export function uploadSinDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getSinUnaryOp()); +} + +/** + * Tanh + */ +function getTanhUnaryOp(): string { + return ` + float e2x = exp(-2.0 * value); + gl_FragColor = vec4((1.0 - e2x) / (1.0 + e2x), 0, 0, 0); + `; +} + +export function getTanhFragmentShaderSource(): string { + return unaryop_gpu.getFragmentShaderSource(getTanhUnaryOp()); +} + +export function tanh( + gpgpu: GPGPUContext, tanhProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + unaryop_gpu.unaryOp(gpgpu, tanhProgram, a, rows, columns, result); +} + +export function uploadTanhDownload( + a: Float32Array, rows: number, columns: number): Float32Array { + return unaryop_gpu.uploadUnaryOpDownload(a, rows, columns, getTanhUnaryOp()); +} diff --git a/src/math/webgl/trig_gpu_test.ts b/src/math/webgl/trig_gpu_test.ts new file mode 100644 index 0000000000..8f6bfe9f67 --- /dev/null +++ b/src/math/webgl/trig_gpu_test.ts @@ -0,0 +1,57 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as test_util from '../../test_util'; +import * as util from '../../util'; +import * as trig_gpu from './trig_gpu'; + +describe('sin_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(28 * 32); + const result = trig_gpu.uploadSinDownload(a, 28, 32); + expect(result.length).toEqual(a.length); + }); + + it('Sin equals CPU', () => { + const size = 10; + const a = test_util.randomArrayInRange(size, -1, 1); + const expectedResult = new Float32Array(size); + for (let i = 0; i < a.length; i++) { + expectedResult[i] = Math.sin(a[i]); + } + const result = trig_gpu.uploadSinDownload(a, 1, size); + test_util.expectArraysClose(result, expectedResult, 1e-3); + }); +}); + + +describe('tanh_gpu', () => { + it('returns a matrix with the same shape as the input matrix', () => { + const a = new Float32Array(28 * 32); + const result = trig_gpu.uploadTanhDownload(a, 28, 32); + expect(result.length).toEqual(a.length); + }); + + it('Tanh equals CPU', () => { + const size = 10; + const a = test_util.randomArrayInRange(size, -1, 1); + const expectedResult = new Float32Array(size); + for (let i = 0; i < a.length; i++) { + expectedResult[i] = util.tanh(a[i]); + } + const result = trig_gpu.uploadTanhDownload(a, 1, size); + test_util.expectArraysClose(result, expectedResult, 1e-6); + }); +}); diff --git a/src/math/webgl/unaryop_gpu.ts b/src/math/webgl/unaryop_gpu.ts new file mode 100644 index 0000000000..76181eb68a --- /dev/null +++ b/src/math/webgl/unaryop_gpu.ts @@ -0,0 +1,55 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {GPGPUContext} from './gpgpu_context'; + +export function getFragmentShaderSource(resultOp: string): string { + return ` + precision highp float; + uniform sampler2D matrixA; + varying vec2 resultUV; + + void main() { + float value = texture2D(matrixA, resultUV).r; + ${resultOp} + }`; +} + +export function unaryOp( + gpgpu: GPGPUContext, unaryOpProgram: WebGLProgram, a: WebGLTexture, + rows: number, columns: number, result: WebGLTexture) { + gpgpu.setOutputMatrixTexture(result, rows, columns); + gpgpu.setProgram(unaryOpProgram); + gpgpu.setInputMatrixTexture(a, 'matrixA', 0); + gpgpu.executeProgram(); +} + +export function uploadUnaryOpDownload( + a: Float32Array, rows: number, columns: number, + resultOp: string): Float32Array { + const gpgpu = new GPGPUContext(); + const fragmentShaderSrc = getFragmentShaderSource(resultOp); + const program: WebGLProgram = gpgpu.createProgram(fragmentShaderSrc); + const aTexture: WebGLTexture = gpgpu.createMatrixTexture(rows, columns); + const resultTexture: WebGLTexture = gpgpu.createMatrixTexture(rows, columns); + gpgpu.uploadMatrixToTexture(aTexture, rows, columns, a); + unaryOp(gpgpu, program, aTexture, rows, columns, resultTexture); + const result = gpgpu.downloadMatrixFromTexture(resultTexture, rows, columns); + gpgpu.deleteMatrixTexture(aTexture); + gpgpu.deleteMatrixTexture(resultTexture); + gpgpu.deleteProgram(program); + gpgpu.dispose(); + return result; +} diff --git a/src/math/webgl/webgl_util.ts b/src/math/webgl/webgl_util.ts new file mode 100644 index 0000000000..1238393f21 --- /dev/null +++ b/src/math/webgl/webgl_util.ts @@ -0,0 +1,418 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +let USE_WEBGL2_WHEN_AVAILABLE = false; +let WEBGL2_ENABLED: boolean|undefined = null!; +let MAX_TEXTURE_SIZE: number = null!; + +import * as util from '../../util'; + +export interface WebGLContextAttributes { + alpha?: boolean; + antialias?: boolean; + premultipliedAlpha?: boolean; + preserveDrawingBuffer?: boolean; + depth?: boolean; + stencil?: boolean; + failIfMajorPerformanceCaveat?: boolean; +} + +/** @hidden */ +export const IS_NAN_SHADER_FUNC = ` +bool isNaN(float val) { + return val == val ? false : true; +} +`; + +export interface WebGLLoseContextExtension { loseContext(): void; } + +export function createWebGLRenderingContext(attributes: WebGLContextAttributes): + WebGLRenderingContext { + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return createWebGLRenderingContextFromCanvas(canvas, attributes); +} + +/** + * Force the library to prefer WebGL 1.0 instead of WebGL 2.0 even when WebGL + * 2.0 is available. + */ +export function preferWebGL1() { + USE_WEBGL2_WHEN_AVAILABLE = false; + WEBGL2_ENABLED = undefined; +} + +/** + * Prefer WebGL 2.0 to WebGL 1.0. This is the default configuration. + */ +export function preferWebGL2() { + USE_WEBGL2_WHEN_AVAILABLE = true; + WEBGL2_ENABLED = undefined; +} + +export function isWebGL2Enabled() { + if (!USE_WEBGL2_WHEN_AVAILABLE) { + return false; + } + + if (WEBGL2_ENABLED === undefined) { + const tempCanvas = document.createElement('canvas'); + const gl = tempCanvas.getContext('webgl2'); + if (gl != null) { + WEBGL2_ENABLED = true; + + const loseContextExtension = + getExtensionOrThrow( + gl as WebGLRenderingContext, 'WEBGL_lose_context') as + WebGLLoseContextExtension; + loseContextExtension.loseContext(); + } else { + WEBGL2_ENABLED = false; + } + } + return WEBGL2_ENABLED; +} + +export function createWebGLRenderingContextFromCanvas( + canvas: HTMLCanvasElement, + attributes: WebGLContextAttributes): WebGLRenderingContext { + let gl: WebGLRenderingContext; + if (isWebGL2Enabled()) { + gl = canvas.getContext('webgl2', attributes) as WebGLRenderingContext; + } else { + gl = (canvas.getContext('webgl', attributes) || + canvas.getContext('experimental-webgl', attributes)) as + WebGLRenderingContext; + } + + if (gl == null) { + throw new Error('This browser does not support WebGL.'); + } + return gl; +} + +export function callAndCheck(gl: WebGLRenderingContext, func: () => T): T { + const returnValue = func(); + checkWebGLError(gl); + return returnValue; +} + +let webGLDebugErrorCheckingEnabled = false; + +export function enableDebugWebGLErrorChecking(enabled: boolean) { + webGLDebugErrorCheckingEnabled = enabled; +} + +export function checkWebGLError(gl: WebGLRenderingContext) { + if (webGLDebugErrorCheckingEnabled) { + const error = gl.getError(); + if (error !== gl.NO_ERROR) { + throw new Error('WebGL Error: ' + getWebGLErrorMessage(gl, error)); + } + } +} + +export function getWebGLErrorMessage( + gl: WebGLRenderingContext, status: number): string { + switch (status) { + case gl.NO_ERROR: + return 'NO_ERROR'; + case gl.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return 'Unknown error code ' + status; + } +} + +export function getExtensionOrThrow( + gl: WebGLRenderingContext, extensionName: string): {} { + return throwIfNull<{}>( + gl, () => gl.getExtension(extensionName), + 'Extension "' + extensionName + '" not supported on this browser.'); +} + +export function createVertexShader( + gl: WebGLRenderingContext, vertexShaderSource: string): WebGLShader { + const vertexShader: WebGLShader = throwIfNull( + gl, () => gl.createShader(gl.VERTEX_SHADER), + 'Unable to create vertex WebGLShader.'); + callAndCheck(gl, () => gl.shaderSource(vertexShader, vertexShaderSource)); + callAndCheck(gl, () => gl.compileShader(vertexShader)); + if (gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(vertexShader)); + throw new Error('Failed to compile vertex shader.'); + } + return vertexShader; +} + +export function createFragmentShader( + gl: WebGLRenderingContext, fragmentShaderSource: string): WebGLShader { + const fragmentShader: WebGLShader = throwIfNull( + gl, () => gl.createShader(gl.FRAGMENT_SHADER), + 'Unable to create fragment WebGLShader.'); + callAndCheck(gl, () => gl.shaderSource(fragmentShader, fragmentShaderSource)); + callAndCheck(gl, () => gl.compileShader(fragmentShader)); + if (gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) === false) { + console.log(gl.getShaderInfoLog(fragmentShader)); + throw new Error('Failed to compile fragment shader.'); + } + return fragmentShader; +} + +export function createProgram(gl: WebGLRenderingContext): WebGLProgram { + return throwIfNull( + gl, () => gl.createProgram(), 'Unable to create WebGLProgram.'); +} + +export function linkProgram(gl: WebGLRenderingContext, program: WebGLProgram) { + callAndCheck(gl, () => gl.linkProgram(program)); + if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Failed to link vertex and fragment shaders.'); + } +} + +export function validateProgram( + gl: WebGLRenderingContext, program: WebGLProgram) { + callAndCheck(gl, () => gl.validateProgram(program)); + if (gl.getProgramParameter(program, gl.VALIDATE_STATUS) === false) { + console.log(gl.getProgramInfoLog(program)); + throw new Error('Shader program validation failed.'); + } +} + +export function createStaticVertexBuffer( + gl: WebGLRenderingContext, data: Float32Array): WebGLBuffer { + const buffer: WebGLBuffer = throwIfNull( + gl, () => gl.createBuffer(), 'Unable to create WebGLBuffer'); + callAndCheck(gl, () => gl.bindBuffer(gl.ARRAY_BUFFER, buffer)); + callAndCheck(gl, () => gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)); + return buffer; +} + +export function createStaticIndexBuffer( + gl: WebGLRenderingContext, data: Uint16Array): WebGLBuffer { + const buffer: WebGLBuffer = throwIfNull( + gl, () => gl.createBuffer(), 'Unable to create WebGLBuffer'); + callAndCheck(gl, () => gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer)); + callAndCheck( + gl, () => gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW)); + return buffer; +} + +export function queryMaxTextureSize(gl: WebGLRenderingContext): number { + if (MAX_TEXTURE_SIZE != null) { + return MAX_TEXTURE_SIZE; + } + MAX_TEXTURE_SIZE = + callAndCheck(gl, () => gl!.getParameter(gl!.MAX_TEXTURE_SIZE)); + return MAX_TEXTURE_SIZE; +} + +export function getChannelsPerTexture(): number { + if (isWebGL2Enabled()) { + return 1; + } + return 4; +} + +export function createTexture(gl: WebGLRenderingContext): WebGLTexture { + return throwIfNull( + gl, () => gl.createTexture(), 'Unable to create WebGLTexture.'); +} + +export function validateTextureSize( + gl: WebGLRenderingContext, width: number, height: number) { + const maxTextureSize: number = queryMaxTextureSize(gl); + if ((width <= 0) || (height <= 0)) { + const requested = '[' + width + 'x' + height + ']'; + throw new Error('Requested texture size ' + requested + ' is invalid.'); + } + if ((width > maxTextureSize) || (height > maxTextureSize)) { + const requested = '[' + width + 'x' + height + ']'; + const max = '[' + maxTextureSize + 'x' + maxTextureSize + ']'; + throw new Error( + 'Requested texture size ' + requested + + ' greater than WebGL maximum on this browser / GPU ' + max + '.'); + } +} + +export function createFramebuffer(gl: WebGLRenderingContext): WebGLFramebuffer { + return throwIfNull( + gl, () => gl.createFramebuffer(), 'Unable to create WebGLFramebuffer.'); +} + +export function bindVertexBufferToProgramAttribute( + gl: WebGLRenderingContext, program: WebGLProgram, attribute: string, + buffer: WebGLBuffer, arrayEntriesPerItem: number, itemStrideInBytes: number, + itemOffsetInBytes: number) { + const loc = gl.getAttribLocation(program, attribute); + if (loc === -1) { + const error = new Error( + 'Unable to get attribute "' + attribute + '" on WebGLProgram.'); + // tslint:disable-next-line:no-any + (error as any).namedVertexAttributeNotFound = attribute; + throw error; + } + callAndCheck(gl, () => gl.bindBuffer(gl.ARRAY_BUFFER, buffer)); + callAndCheck( + gl, + () => gl.vertexAttribPointer( + loc, arrayEntriesPerItem, gl.FLOAT, false, itemStrideInBytes, + itemOffsetInBytes)); + callAndCheck(gl, () => gl.enableVertexAttribArray(loc)); +} + +export function bindTextureUnit( + gl: WebGLRenderingContext, texture: WebGLTexture, textureUnit: number) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, () => gl.activeTexture(gl.TEXTURE0 + textureUnit)); + callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, texture)); +} + +export function unbindTextureUnit( + gl: WebGLRenderingContext, textureUnit: number) { + validateTextureUnit(gl, textureUnit); + callAndCheck(gl, () => gl.activeTexture(gl.TEXTURE0 + textureUnit)); + callAndCheck(gl, () => gl.bindTexture(gl.TEXTURE_2D, null)); +} + +export function getProgramUniformLocationOrThrow( + gl: WebGLRenderingContext, program: WebGLProgram, + uniformName: string): WebGLUniformLocation { + return throwIfNull( + gl, () => gl.getUniformLocation(program, uniformName), + 'uniform "' + uniformName + '" not present in program.'); +} + +export function bindTextureToProgramUniformSampler( + gl: WebGLRenderingContext, program: WebGLProgram, texture: WebGLTexture, + uniformSamplerName: string, textureUnit: number) { + callAndCheck(gl, () => bindTextureUnit(gl, texture, textureUnit)); + const samplerLocation = + getProgramUniformLocationOrThrow(gl, program, uniformSamplerName); + callAndCheck(gl, () => gl.uniform1i(samplerLocation, textureUnit)); +} + +export function bindCanvasToFramebuffer(gl: WebGLRenderingContext) { + callAndCheck(gl, () => gl.bindFramebuffer(gl.FRAMEBUFFER, null)); + callAndCheck(gl, () => gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)); + callAndCheck(gl, () => gl.scissor(0, 0, gl.canvas.width, gl.canvas.height)); +} + +export function bindColorTextureToFramebuffer( + gl: WebGLRenderingContext, texture: WebGLTexture, + framebuffer: WebGLFramebuffer) { + callAndCheck(gl, () => gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)); + callAndCheck( + gl, + () => gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)); +} + +export function unbindColorTextureFromFramebuffer( + gl: WebGLRenderingContext, framebuffer: WebGLFramebuffer) { + callAndCheck(gl, () => gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)); + callAndCheck( + gl, + () => gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0)); +} + +export function validateFramebuffer(gl: WebGLRenderingContext) { + const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (status !== gl.FRAMEBUFFER_COMPLETE) { + throw new Error( + 'Error binding framebuffer: ' + getFramebufferErrorMessage(gl, status)); + } +} + +export function getFramebufferErrorMessage( + gl: WebGLRenderingContext, status: number): string { + switch (status) { + case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'; + case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'; + case gl.FRAMEBUFFER_UNSUPPORTED: + return 'FRAMEBUFFER_UNSUPPORTED'; + default: + return 'unknown error ' + status; + } +} + +function throwIfNull( + gl: WebGLRenderingContext, returnTOrNull: () => T | null, + failureMessage: string): T { + const tOrNull: T|null = callAndCheck(gl, () => returnTOrNull()); + if (tOrNull == null) { + throw new Error(failureMessage); + } + return tOrNull as T; +} + +function validateTextureUnit(gl: WebGLRenderingContext, textureUnit: number) { + const maxTextureUnit = gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1; + const glTextureUnit = textureUnit + gl.TEXTURE0; + if (glTextureUnit < gl.TEXTURE0 || glTextureUnit > maxTextureUnit) { + const textureUnitRange = '[gl.TEXTURE0, gl.TEXTURE' + maxTextureUnit + ']'; + throw new Error('textureUnit must be in ' + textureUnitRange + '.'); + } +} + +export function getTextureShapeFromLogicalShape( + gl: WebGLRenderingContext, logicalShape: number[], + preferredTexShape?: [number, number]): [number, number] { + const maxTexSize = queryMaxTextureSize(gl); + const size = util.sizeFromShape(logicalShape); + if (preferredTexShape != null) { + const sizePreferred = util.sizeFromShape(preferredTexShape); + util.assert( + size === sizePreferred, + `Size of shape (${size}) must match size of ` + + `preferredShape (${sizePreferred})`); + if (preferredTexShape[0] <= maxTexSize && + preferredTexShape[1] <= maxTexSize) { + return preferredTexShape; + } + } + + if (logicalShape.length <= 1 && size <= maxTexSize) { + return [size, 1]; + } else if ( + logicalShape.length === 2 && logicalShape[0] <= maxTexSize && + logicalShape[1] <= maxTexSize) { + return logicalShape as [number, number]; + } else if ( + logicalShape.length === 3 && logicalShape[0] <= maxTexSize && + logicalShape[1] * logicalShape[2] <= maxTexSize) { + return [logicalShape[0], logicalShape[1] * logicalShape[2]]; + } else { + return util.sizeToSquarishShape(size); + } +} diff --git a/src/operation_emitter.ts b/src/operation_emitter.ts new file mode 100644 index 0000000000..220d775868 --- /dev/null +++ b/src/operation_emitter.ts @@ -0,0 +1,125 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {AddNode, ArgMaxEqualsNode, ArgMaxNode, Concat3DNode, Convolution2DNode, DivideNode, ExpNode, FusedLinearCombinationNode, Graph, LogNode, MatMulNode, MaxPoolNode, MeanSquaredCostNode, MultiplyNode, Node, ReduceSumNode, ReLUNode, ReshapeNode, SigmoidNode, SoftmaxCrossEntropyCostNode, SoftmaxNode, SplitNode, SquareNode, SubtractNode, TanHNode, Tensor} from './graph'; +import * as graph_util from './graph_util'; +import {Add} from './ops/add'; +import {ArgMax} from './ops/argmax'; +import {ArgMaxEquals} from './ops/argmaxequals'; +import {Concat3D} from './ops/concat3d'; +import {Convolution2D} from './ops/convolution'; +import {Divide} from './ops/divide'; +import {ReLU, Sigmoid, Square, TanH} from './ops/element_wise_activation'; +import {MeanSquaredCost} from './ops/element_wise_cost'; +import {Exp} from './ops/exp'; +import {LinearCombination} from './ops/linear_combination'; +import {Log} from './ops/log'; +import {MatMul} from './ops/matmul'; +import {MaxPool} from './ops/max_pool'; +import {Multiply} from './ops/multiply'; +import {Operation} from './ops/op'; +import {ReduceSum} from './ops/reduce_sum'; +import {Reshape} from './ops/reshape'; +import {Softmax, SoftmaxCrossEntropyCost} from './ops/softmax'; +import {Split} from './ops/split'; +import {Subtract} from './ops/subtract'; + +export function emitFromGraphNodes(nodes: Node[]): Operation[] { + const ops: Operation[] = []; + nodes.forEach(node => Array.prototype.push.apply(ops, emitOpFromNode(node))); + return ops; +} + +function emitOpFromNode(node: Node): Operation[] { + if (node instanceof ReshapeNode) { + return [new Reshape(node.inputs[ReshapeNode.X], node.output)]; + } else if (node instanceof MatMulNode) { + const x1 = node.inputs[MatMulNode.X1]; + const x2 = node.inputs[MatMulNode.X2]; + return [new MatMul(x1, x2, node.output)]; + } else if (node instanceof Convolution2DNode) { + const w = node.inputs[Convolution2DNode.W]; + const x = node.inputs[Convolution2DNode.X]; + const b = node.inputs[Convolution2DNode.B]; + return [new Convolution2D( + w, x, b, node.output, node.fieldSize, node.outputDepth, node.stride, + node.zeroPad)]; + } else if (node instanceof MaxPoolNode) { + const x = node.inputs[MaxPoolNode.X]; + return [new MaxPool( + x, node.output, node.fieldSize, node.stride, node.zeroPad)]; + } else if (node instanceof ExpNode) { + return [new Exp(node.inputs[ExpNode.X], node.output)]; + } else if (node instanceof LogNode) { + return [new Log(node.inputs[LogNode.X], node.output)]; + } else if (node instanceof ReLUNode) { + return [new ReLU(node.inputs[ReLUNode.X], node.output)]; + } else if (node instanceof TanHNode) { + return [new TanH(node.inputs[TanHNode.X], node.output)]; + } else if (node instanceof SigmoidNode) { + return [new Sigmoid(node.inputs[SigmoidNode.X], node.output)]; + } else if (node instanceof SoftmaxCrossEntropyCostNode) { + const x = node.inputs[SoftmaxCrossEntropyCostNode.X]; + const target = node.inputs[SoftmaxCrossEntropyCostNode.TARGET]; + return [new SoftmaxCrossEntropyCost(x, target, node.output)]; + } else if (node instanceof SoftmaxNode) { + return [new Softmax(node.inputs[SoftmaxNode.X], node.output)]; + } else if (node instanceof MeanSquaredCostNode) { + const label = node.inputs[MeanSquaredCostNode.LABEL]; + const prediction = node.inputs[MeanSquaredCostNode.PREDICTION]; + return [new MeanSquaredCost(label, prediction, node.output)]; + } else if (node instanceof ArgMaxEqualsNode) { + return [new ArgMaxEquals( + node.inputs[ArgMaxEqualsNode.X1], node.inputs[ArgMaxEqualsNode.X2], + node.output)]; + } else if (node instanceof ArgMaxNode) { + return [new ArgMax(node.x, node.output)]; + } else if (node instanceof FusedLinearCombinationNode) { + return [new LinearCombination( + node.inputs[FusedLinearCombinationNode.T1], + node.inputs[FusedLinearCombinationNode.T2], + node.inputs[FusedLinearCombinationNode.C1], + node.inputs[FusedLinearCombinationNode.C2], node.output)]; + } else if (node instanceof Concat3DNode) { + return [new Concat3D( + node.inputs[Concat3DNode.X1], node.inputs[Concat3DNode.X2], node.axis, + node.output)]; + } else if (node instanceof SquareNode) { + return [new Square(node.inputs[SquareNode.X], node.output)]; + } else if (node instanceof AddNode) { + return [new Add( + node.inputs[AddNode.T1], node.inputs[AddNode.T2], node.output)]; + } else if (node instanceof SubtractNode) { + return [new Subtract( + node.inputs[SubtractNode.T1], node.inputs[SubtractNode.T2], + node.output)]; + } else if (node instanceof MultiplyNode) { + return [new Multiply( + node.inputs[MultiplyNode.T1], node.inputs[MultiplyNode.T2], + node.output)]; + } else if (node instanceof DivideNode) { + return [new Divide( + node.inputs[DivideNode.T1], node.inputs[DivideNode.T2], node.output)]; + } else if (node instanceof SplitNode) { + return [new Split(node.inputs[SplitNode.X], node.outputs)]; + } else if (node instanceof ReduceSumNode) { + return [new ReduceSum(node.inputs[ReduceSumNode.X], node.output)]; + } else if (graph_util.isInputNode(node)) { + return []; + } else { + // tslint:disable-next-line:no-any + throw Error('Unsupported node type: ' + (node.constructor as any).name); + } +} diff --git a/src/ops/add.ts b/src/ops/add.ts new file mode 100644 index 0000000000..2d391c5a6e --- /dev/null +++ b/src/ops/add.ts @@ -0,0 +1,102 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Add extends Operation { + private dySizeScalar: Scalar; + + /** Element-wise add operation. Broadcasts if one of the tensors is scalar. */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private yTensor: Tensor) { + super(); + util.assert( + util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), + 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + let result: NDArray; + if (util.isScalarShape(x1.shape)) { + result = math.scalarPlusArray(x1, x2); + } else if (util.isScalarShape(x2.shape)) { + result = math.scalarPlusArray(x2, x1); + } else { + result = math.add(x1, x2); + } + inferenceArrays.set(this.yTensor, keep(result)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + const dy = gradientArrays.get(this.yTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.x1Tensor)) { + if (util.isScalarShape(this.x1Tensor.shape)) { + const sum = math.sum(dy); + if (this.dySizeScalar == null) { + this.dySizeScalar = Scalar.new(dy.size); + } + gradientArrays.set( + this.x1Tensor, keep(math.divide(sum, this.dySizeScalar))); + } else { + gradientArrays.set(this.x1Tensor, dy); + } + } + + if (graph_util.shouldBackProp(this.x2Tensor)) { + if (util.isScalarShape(this.x2Tensor.shape)) { + const sum = math.sum(dy); + if (this.dySizeScalar == null) { + this.dySizeScalar = Scalar.new(dy.size); + } + gradientArrays.set( + this.x2Tensor, keep(math.divide(sum, this.dySizeScalar))); + } else { + gradientArrays.set(this.x2Tensor, dy); + } + } + }); + } + + dispose() { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + } +} diff --git a/src/ops/add_test.ts b/src/ops/add_test.ts new file mode 100644 index 0000000000..78e45cf109 --- /dev/null +++ b/src/ops/add_test.ts @@ -0,0 +1,193 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Add} from './add'; + +describe('add operation', () => { + let math: NDArrayMathCPU; + + let t1: Tensor; + let t2: Tensor; + let y: Tensor; + let addOp: Add; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(t1); + activations.disposeArray(t2); + activations.disposeArray(y); + gradients.disposeArray(t1); + gradients.disposeArray(t2); + gradients.disposeArray(y); + }); + + it('adds two 1-D tensors', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array1D.new([3, 4, 5]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + addOp = new Add(t1, t2, y); + addOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([3]); + expect(yVal.getValues()).toEqual(new Float32Array([4, 6, 8])); + + const dy = Array1D.new([6, 7, 8]); + gradients.set(y, dy); + + addOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(dy.getValues()); + }); + + it('adds two 2-D tensors', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Array2D.new([2, 3], [3, 4, 5, 7, 8, 9]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + addOp = new Add(t1, t2, y); + addOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([4, 6, 8, 11, 13, 15])); + + const dy = Array2D.new([2, 3], [10, 11, 12, 13, 14, 15]); + gradients.set(y, dy); + + addOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(dy.getValues()); + }); + + it('ndarray + scalar', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Scalar.new(2); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + addOp = new Add(t1, t2, y); + addOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([3, 4, 5, 6, 7, 8])); + + const dy = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + gradients.set(y, dy); + + addOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.get()).toEqual(7); + }); + + it('scalar + array', () => { + const x1 = Scalar.new(2); + const x2 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + addOp = new Add(t1, t2, y); + addOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([3, 4, 5, 6, 7, 8])); + + const dy = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + gradients.set(y, dy); + + addOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.get()).toEqual(7); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(dy.getValues()); + }); + + it('throws when shapes of X1 and X2 do not match', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Array2D.new([3, 2], [1, 2, 3, 4, 5, 6]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + expect(() => new Add(t1, t2, y)).toThrowError(); + }); +}); diff --git a/src/ops/argmax.ts b/src/ops/argmax.ts new file mode 100644 index 0000000000..762d77e503 --- /dev/null +++ b/src/ops/argmax.ts @@ -0,0 +1,47 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMath} from '../math/math'; +import {Array1D, Array2D, NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class ArgMax extends Operation { + /** + * An ArgMax operation. + */ + constructor(private xTensor: Tensor, private yTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor); + math.scope((keep) => { + inferenceArrays.set(this.yTensor, keep(math.argMax(x))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + throw new Error('ArgMax backprop unimplemented'); + } +} diff --git a/src/ops/argmax_test.ts b/src/ops/argmax_test.ts new file mode 100644 index 0000000000..e833dac6e1 --- /dev/null +++ b/src/ops/argmax_test.ts @@ -0,0 +1,67 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {ArgMax} from './argmax'; + +describe('Argmax oper', () => { + let math: NDArrayMathCPU; + + let x: Tensor; + let y: Tensor; + let tensorArrayMap: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + tensorArrayMap = new TensorArrayMap(); + }); + + afterEach(() => { + tensorArrayMap.disposeArray(x); + tensorArrayMap.disposeArray(y); + }); + + it('argmax of Array1D', () => { + const vals = Array1D.new([0, 2, 1]); + x = new Tensor(vals.shape); + y = new Tensor([]); + tensorArrayMap.set(x, vals); + + const argmaxOp = new ArgMax(x, y); + argmaxOp.feedForward(math, tensorArrayMap); + const yVal = tensorArrayMap.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.get()).toEqual(1); + }); + + it('argmax of Array2D', () => { + const vals = Array2D.new([2, 3], [[0, 2, 1], [2, 3, 0]]); + x = new Tensor(vals.shape); + y = new Tensor([]); + tensorArrayMap.set(x, vals); + + const argmaxOp = new ArgMax(x, y); + argmaxOp.feedForward(math, tensorArrayMap); + const yVal = tensorArrayMap.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.get()).toEqual(4); + }); +}); diff --git a/src/ops/argmaxequals.ts b/src/ops/argmaxequals.ts new file mode 100644 index 0000000000..e6656c0ebe --- /dev/null +++ b/src/ops/argmaxequals.ts @@ -0,0 +1,50 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMath} from '../math/math'; +import {Array1D, Array2D, NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class ArgMaxEquals extends Operation { + /** + * An ArgMaxEquals operation. + */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private yTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + math.scope((keep) => { + inferenceArrays.set(this.yTensor, keep(math.argMaxEquals(x1, x2))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + throw new Error('ArgMaxEquals backprop unimplemented'); + } +} diff --git a/src/ops/argmaxequals_test.ts b/src/ops/argmaxequals_test.ts new file mode 100644 index 0000000000..5fa45bad00 --- /dev/null +++ b/src/ops/argmaxequals_test.ts @@ -0,0 +1,80 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {ArgMaxEquals} from './argmaxequals'; + +describe('Argmax equals oper', () => { + let math: NDArrayMathCPU; + + let t1: Tensor; + let t2: Tensor; + let y: Tensor; + let argmaxEqualsOp: ArgMaxEquals; + let tensorArrayMap: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + tensorArrayMap = new TensorArrayMap(); + }); + + afterEach(() => { + tensorArrayMap.disposeArray(t1); + tensorArrayMap.disposeArray(t2); + tensorArrayMap.disposeArray(y); + }); + + it('argmax equals', () => { + const x1 = Array1D.new([0, 2, 1]); + const x2 = Array1D.new([2, 4, 3]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + tensorArrayMap.set(t1, x1); + tensorArrayMap.set(t2, x2); + + argmaxEqualsOp = new ArgMaxEquals(t1, t2, y); + argmaxEqualsOp.feedForward(math, tensorArrayMap); + const yVal = tensorArrayMap.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.getValues()).toEqual(new Float32Array([1])); + }); + + it('argmax not equals', () => { + const x1 = Array1D.new([0, 2, 1]); + const x2 = Array1D.new([5, 4, 3]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + tensorArrayMap.set(t1, x1); + tensorArrayMap.set(t2, x2); + + argmaxEqualsOp = new ArgMaxEquals(t1, t2, y); + argmaxEqualsOp.feedForward(math, tensorArrayMap); + const yVal = tensorArrayMap.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.getValues()).toEqual(new Float32Array([0])); + }); +}); \ No newline at end of file diff --git a/src/ops/concat3d.ts b/src/ops/concat3d.ts new file mode 100644 index 0000000000..2c82c6b89b --- /dev/null +++ b/src/ops/concat3d.ts @@ -0,0 +1,57 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as concat3d_util from '../math/concat3d_util'; +import {NDArrayMath} from '../math/math'; +import {Array3D, NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Concat3D extends Operation { + /** + * A Concat 3D operation. + * + * Concats two 3D tensors along an axis. + */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, private axis: number, + private yTensor: Tensor) { + super(); + concat3d_util.assertConcat3DShapesMatch( + x1Tensor.shape, x2Tensor.shape, axis); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor) as Array3D; + const x2 = inferenceArrays.get(this.x2Tensor) as Array3D; + + math.scope((keep) => { + const concatResult = math.concat3D(x1, x2, this.axis); + inferenceArrays.set(this.yTensor, keep(concatResult)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + throw new Error('Concat3D backprop not implemented.'); + } +} diff --git a/src/ops/concat3d_test.ts b/src/ops/concat3d_test.ts new file mode 100644 index 0000000000..48b7619eb9 --- /dev/null +++ b/src/ops/concat3d_test.ts @@ -0,0 +1,115 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as concat3d_util from '../math/concat3d_util'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array3D} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Concat3D} from './concat3d'; + +describe('concat3d operation', () => { + let math: NDArrayMathCPU; + + let x1Tensor: Tensor; + let x2Tensor: Tensor; + let yTensor: Tensor; + let concatOperation: Concat3D; + let tensorArrayMap: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + tensorArrayMap = new TensorArrayMap(); + }); + + afterEach(() => { + tensorArrayMap.disposeArray(x1Tensor); + tensorArrayMap.disposeArray(x2Tensor); + tensorArrayMap.disposeArray(yTensor); + }); + + it('concats tensors, axis=0', () => { + const axis = 0; + + const x1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const x2 = Array3D.new([1, 1, 3], [4, 5, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor( + concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis)); + + tensorArrayMap.set(x1Tensor, x1); + tensorArrayMap.set(x2Tensor, x2); + + concatOperation = new Concat3D(x1Tensor, x2Tensor, axis, yTensor); + + concatOperation.feedForward(math, tensorArrayMap); + + const y = tensorArrayMap.get(yTensor); + + expect(y.shape).toEqual([2, 1, 3]); + expect(y.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('concats tensors, axis=1', () => { + const axis = 1; + + const x1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const x2 = Array3D.new([1, 1, 3], [4, 5, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor( + concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis)); + + tensorArrayMap.set(x1Tensor, x1); + tensorArrayMap.set(x2Tensor, x2); + + concatOperation = new Concat3D(x1Tensor, x2Tensor, axis, yTensor); + + concatOperation.feedForward(math, tensorArrayMap); + + const y = tensorArrayMap.get(yTensor); + + expect(y.shape).toEqual([1, 2, 3]); + expect(y.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); + + it('concats tensors, axis=2', () => { + const axis = 2; + + const x1 = Array3D.new([1, 1, 3], [1, 2, 3]); + const x2 = Array3D.new([1, 1, 3], [4, 5, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor( + concat3d_util.computeConcat3DOutputShape(x1.shape, x2.shape, axis)); + + tensorArrayMap.set(x1Tensor, x1); + tensorArrayMap.set(x2Tensor, x2); + + concatOperation = new Concat3D(x1Tensor, x2Tensor, axis, yTensor); + + concatOperation.feedForward(math, tensorArrayMap); + + const y = tensorArrayMap.get(yTensor); + + expect(y.shape).toEqual([1, 1, 6]); + expect(y.getValues()).toEqual(new Float32Array([1, 2, 3, 4, 5, 6])); + }); +}); \ No newline at end of file diff --git a/src/ops/convolution.ts b/src/ops/convolution.ts new file mode 100644 index 0000000000..a3f3ffa955 --- /dev/null +++ b/src/ops/convolution.ts @@ -0,0 +1,100 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as conv_util from '../math/conv_util'; +import {MatrixOrientation, NDArrayMath} from '../math/math'; +import {Array1D, Array2D, Array3D, Array4D, NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Convolution2D extends Operation { + private zeroPad: number; + + /** + * Constructs a convolution op with the specified properties. + * + * @param inputShape The shape of the input ndarray. + * @param fieldSize The size of the filter (rows/cols of sliding window). + * @param outputDepth The depth of the output (Number of filters). + * @param stride How many pixels to shift the filter by when sliding. + * Defaults to 1. + * @param zeroPad How many pixels to pad the input from each side. Defaults to + * a value so that the rows and columns of the output ndarray is + * the same as the input ndarray. + * @param weights Optional. The weights of the filters. + * @param biases Optional. The bias terms of the filters. + */ + constructor( + private wTensor: Tensor, private xTensor: Tensor, private bTensor: Tensor, + private yTensor: Tensor, private fieldSize: number, + private outputDepth: number, private stride = 1, zeroPad?: number) { + super(); + this.assertWeightsShape(wTensor.shape); + this.zeroPad = zeroPad != null ? + zeroPad : + conv_util.computeDefaultPad( + this.xTensor.shape as [number, number, number], this.fieldSize, + this.stride); + util.assert( + util.isInt(this.zeroPad), + `The zero padding (${this.zeroPad}) must be an integer. Change the ` + + `stride and/or zero pad parameters`); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const weights = inferenceArrays.get(this.wTensor) as Array4D; + const biases = inferenceArrays.get(this.bTensor) as Array1D; + const x = inferenceArrays.get(this.xTensor) as Array3D; + + math.scope((keep) => { + inferenceArrays.set( + this.yTensor, + keep(math.conv2d(x, weights, biases, this.stride, this.zeroPad))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const weights = inferenceArrays.get(this.wTensor) as Array4D; + const x = inferenceArrays.get(this.xTensor) as Array3D; + const dy = gradientArrays.get(this.yTensor) as Array3D; + + math.scope((keep) => { + const {dw, db, dx} = + math.conv2dBackProp(x, dy, weights, this.stride, this.zeroPad); + gradientArrays.set(this.wTensor, keep(dw)); + gradientArrays.set(this.bTensor, keep(db)); + gradientArrays.set(this.xTensor, keep(dx)); + }); + } + + private assertWeightsShape(weightsShape: number[]) { + util.assert( + weightsShape[0] === this.fieldSize && + weightsShape[1] === this.fieldSize && + weightsShape[2] === this.xTensor.shape[2] && + weightsShape[3] === this.outputDepth, + `weights must be of shape [${this.fieldSize},${this.fieldSize},` + + `${this.xTensor.shape[2]},${this.outputDepth}] but they are of` + + `shape [${weightsShape}]`); + } +} diff --git a/src/ops/convolution_test.ts b/src/ops/convolution_test.ts new file mode 100644 index 0000000000..671c93cf17 --- /dev/null +++ b/src/ops/convolution_test.ts @@ -0,0 +1,352 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as conv_util from '../math/conv_util'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Array3D, Array4D, NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as test_util from '../test_util'; + +import {Convolution2D} from './convolution'; + +function assertNoNaNs(t: NDArray) { + const values = t.getValues(); + for (let i = 0; i < values.length; ++i) { + expect(isNaN(values[i])).toBe(false); + } +} + +describe('Convolution', () => { + let math: NDArrayMathCPU; + let wTensor: Tensor; + let xTensor: Tensor; + let bTensor: Tensor; + let yTensor: Tensor; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(wTensor); + activations.disposeArray(xTensor); + activations.disposeArray(bTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(wTensor); + gradients.disposeArray(xTensor); + gradients.disposeArray(bTensor); + gradients.disposeArray(yTensor); + }); + + it('Forward prop comparison with convnetjs', () => { + const inputDepth = 3; + const outputDepth = 2; + const fieldSize = 3; + const stride = 2; + const zeroPad = 1; + const weights2D = + Array2D.new([fieldSize * fieldSize * inputDepth, outputDepth], [ + 1, -1, 1, 0, -1, 1, -1, 0, -1, 0, 0, 1, -1, 1, 1, 1, 1, 1, + 0, 1, 0, 0, 0, 1, -1, -1, 1, 0, 1, -1, 1, 1, 1, 1, 1, -1, + -1, 0, 1, 0, 0, 0, 1, -1, -1, -1, 1, 0, -1, 1, 0, -1, 0, 1 + ]); + + const weights = + weights2D.as4D(fieldSize, fieldSize, inputDepth, outputDepth); + const biases = Array1D.new([1, 0]); + const x2D = Array2D.new([25, inputDepth], [ + 1, 2, 2, 0, 0, 2, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 0, + 2, 2, 1, 1, 0, 0, 2, 1, 1, 0, 1, 2, 2, 0, 0, 2, 2, 1, 2, + 2, 2, 1, 2, 2, 2, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 1, + 0, 1, 2, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 0, 2, 0, 0, 0 + ]); + const x = x2D.as3D(5, 5, inputDepth); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fieldSize, outputDepth, stride, zeroPad)); + + activations.set(wTensor, weights); + activations.set(xTensor, x); + activations.set(bTensor, biases); + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fieldSize, outputDepth, stride, + zeroPad); + conv.feedForward(math, activations); + + const result = activations.get(yTensor); + + expect(result.getValues()).toEqual(new Float32Array([ + 7, -8, 8, -2, 7, -2, 5, 5, 4, 6, 1, 2, -1, 3, 7, -2, 1, 4 + ])); + }); + + it('Maintains the rows and cols of input', () => { + const inputDepth = 3; + const outputDepth = 2; + const fSize = 3; + const stride = 1; + + const weights = + NDArray.randNormal([fSize, fSize, inputDepth, outputDepth]); + const biases = NDArray.randNormal([outputDepth]); + const x = NDArray.randNormal([5, 5, inputDepth]); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor( + conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride)); + + activations.set(wTensor, weights); + activations.set(xTensor, x); + activations.set(bTensor, biases); + + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fSize, outputDepth, stride); + + conv.feedForward(math, activations); + + const result = activations.get(yTensor); + + expect(result.shape).toEqual([5, 5, outputDepth]); + }); + + it('Can not maintain the rows and cols of input', () => { + const inputDepth = 3; + const outputDepth = 2; + const fSize = 2; + const stride = 1; + + const weights = + NDArray.randNormal([fSize, fSize, inputDepth, outputDepth]); + const biases = NDArray.randNormal([outputDepth]); + const x = NDArray.randNormal([5, 5, inputDepth]); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor( + conv_util.computeOutputShape3D(x.shape, fSize, outputDepth, stride)); + + activations.set(wTensor, weights); + activations.set(xTensor, x); + activations.set(bTensor, biases); + + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fSize, outputDepth, stride); + + conv.feedForward(math, activations); + + const result = activations.get(yTensor); + + expect(result.shape).toEqual([4, 4, outputDepth]); + }); + + it('Large convolution', () => { + const inputDepth = 3; + const fSize = 7; + const outputDepth = 10; + const stride = 1; + const zeroPad = 1; + + const weights = + NDArray.randNormal([fSize, fSize, inputDepth, outputDepth]); + const biases = NDArray.randNormal([outputDepth]); + const x = NDArray.randNormal([30, 30, inputDepth]); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, outputDepth, stride, zeroPad)); + + activations.set(wTensor, weights); + activations.set(xTensor, x); + activations.set(bTensor, biases); + + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fSize, outputDepth, stride, + zeroPad); + + conv.feedForward(math, activations); + + const result = activations.get(yTensor); + + assertNoNaNs(result); + expect(result.shape).toEqual([26, 26, outputDepth]); + }); + + it('simple conv backprop with d1=d2=1 (input and output)', () => { + // 3X3 image convolved with a 2x2 filter with no padding and stride 1. + // To keep the test simple, we work with input and output depth of 1. + const inputDepth = 1; + const fSize = 2; + const outputDepth = 1; + const stride = 1; + const zeroPad = 0; + + const x3d = NDArray.randNormal([3, 3, inputDepth]); + const x = x3d.as2D(3, 3); + const weights = + NDArray.randNormal([fSize, fSize, inputDepth, outputDepth]); + const biases = NDArray.randNormal([outputDepth]); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x3d.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x3d.shape, fSize, outputDepth, stride, zeroPad)); + + activations.set(wTensor, weights); + activations.set(xTensor, x3d); + activations.set(bTensor, biases); + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fSize, outputDepth, stride, + zeroPad); + + conv.feedForward(math, activations); + + const y = activations.get(yTensor); + + assertNoNaNs(y); + + expect(y.get(0, 0, 0)) + .toBeCloseTo( + x.get(0, 0) * weights.get(0, 0, 0, 0) + + x.get(0, 1) * weights.get(0, 1, 0, 0) + + x.get(1, 0) * weights.get(1, 0, 0, 0) + + x.get(1, 1) * weights.get(1, 1, 0, 0) + biases.get(0)); + + expect(y.get(0, 1, 0)) + .toBeCloseTo( + x.get(0, 1) * weights.get(0, 0, 0, 0) + + x.get(0, 2) * weights.get(0, 1, 0, 0) + + x.get(1, 1) * weights.get(1, 0, 0, 0) + + x.get(1, 2) * weights.get(1, 1, 0, 0) + biases.get(0)); + + expect(y.get(1, 0, 0)) + .toBeCloseTo( + x.get(1, 0) * weights.get(0, 0, 0, 0) + + x.get(1, 1) * weights.get(0, 1, 0, 0) + + x.get(2, 0) * weights.get(1, 0, 0, 0) + + x.get(2, 1) * weights.get(1, 1, 0, 0) + biases.get(0)); + + expect(y.get(1, 1, 0)) + .toBeCloseTo( + x.get(1, 1) * weights.get(0, 0, 0, 0) + + x.get(1, 2) * weights.get(0, 1, 0, 0) + + x.get(2, 1) * weights.get(1, 0, 0, 0) + + x.get(2, 2) * weights.get(1, 1, 0, 0) + biases.get(0)); + + const dy3d = NDArray.randNormal([2, 2, 1]); + + gradients.set(yTensor, dy3d); + + conv.backProp(math, activations, gradients); + + const dx3d = gradients.get(xTensor); + + // Since depth (last dim) is 1, we reduce indexing by converting 3D -> 2D. + const dx = dx3d.as2D(3, 3); + const dy = dy3d.as2D(2, 2); + + // Test dX. + expect(dx.get(0, 0)).toBeCloseTo(dy.get(0, 0) * weights.get(0, 0, 0, 0)); + expect(dx.get(0, 1)) + .toBeCloseTo( + dy.get(0, 0) * weights.get(0, 1, 0, 0) + + dy.get(0, 1) * weights.get(0, 0, 0, 0)); + expect(dx.get(0, 2)).toBeCloseTo(dy.get(0, 1) * weights.get(0, 1, 0, 0)); + expect(dx.get(1, 1)) + .toBeCloseTo( + dy.get(0, 0) * weights.get(1, 1, 0, 0) + + dy.get(0, 1) * weights.get(1, 0, 0, 0) + + dy.get(1, 0) * weights.get(0, 1, 0, 0) + + dy.get(1, 1) * weights.get(0, 0, 0, 0)); + expect(dx.get(2, 1)) + .toBeCloseTo( + dy.get(1, 0) * weights.get(1, 1, 0, 0) + + dy.get(1, 1) * weights.get(1, 0, 0, 0)); + + + // Test dW. + const dw = gradients.get(wTensor); + + expect(dw.get(0, 0, 0, 0)) + .toBeCloseTo( + dy.get(0, 0) * x.get(0, 0) + dy.get(0, 1) * x.get(0, 1) + + dy.get(1, 0) * x.get(1, 0) + dy.get(1, 1) * x.get(1, 1)); + expect(dw.get(1, 1, 0, 0)) + .toBeCloseTo( + dy.get(0, 0) * x.get(1, 1) + dy.get(0, 1) * x.get(1, 2) + + dy.get(1, 0) * x.get(2, 1) + dy.get(1, 1) * x.get(2, 2)); + + // Test db (bias). + const db = gradients.get(bTensor).get(0); + + expect(db).toBeCloseTo( + dy.get(0, 0) + dy.get(0, 1) + dy.get(1, 0) + dy.get(1, 1)); + }); + + it('conv backprop with d1=3 d2=7', () => { + const fSize = 5; + const inputDepth = 3; + const outputDepth = 7; + const stride = 1; + const zeroPad = 1; + + const weights = + NDArray.randNormal([fSize, fSize, inputDepth, outputDepth]); + const biases = NDArray.randNormal([outputDepth]); + const x = NDArray.randNormal([10, 10, inputDepth]); + + wTensor = new Tensor(weights.shape); + xTensor = new Tensor(x.shape); + bTensor = new Tensor(biases.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, outputDepth, stride, zeroPad)); + + activations.set(wTensor, weights); + activations.set(xTensor, x); + activations.set(bTensor, biases); + + const conv = new Convolution2D( + wTensor, xTensor, bTensor, yTensor, fSize, outputDepth, stride, + zeroPad); + + conv.feedForward(math, activations); + + const result = activations.get(yTensor); + + assertNoNaNs(result); + + const dy = NDArray.randNormal(result.shape); + + gradients.set(yTensor, dy); + + conv.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + assertNoNaNs(dx); + }); +}); diff --git a/src/ops/divide.ts b/src/ops/divide.ts new file mode 100644 index 0000000000..c07dae7bc5 --- /dev/null +++ b/src/ops/divide.ts @@ -0,0 +1,114 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Divide extends Operation { + private ones: NDArray; + + /** + * Element-wise divide operation. Broadcasts if one of the tensors is + * scalar. + */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private yTensor: Tensor) { + super(); + util.assert( + util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), + 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const t1 = inferenceArrays.get(this.x1Tensor); + const t2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + let result: NDArray; + if (util.isScalarShape(t1.shape)) { + result = math.scalarDividedByArray(t1, t2); + } else if (util.isScalarShape(t2.shape)) { + result = math.arrayDividedByScalar(t1, t2); + } else { + result = math.divide(t1, t2); + } + inferenceArrays.set(this.yTensor, keep(result)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + const dy = gradientArrays.get(this.yTensor); + + const x1IsScalar = util.isScalarShape(x1.shape); + const x2IsScalar = util.isScalarShape(x2.shape); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.x1Tensor)) { + if (x1IsScalar) { + const div = math.divide(dy, x2); + + gradientArrays.set(this.x1Tensor, keep(math.sum(div))); + + div.dispose(); + } else if (x2IsScalar) { + gradientArrays.set( + this.x1Tensor, keep(math.arrayDividedByScalar(dy, x2))); + } else { + gradientArrays.set(this.x1Tensor, keep(math.divide(dy, x2))); + } + } + + if (graph_util.shouldBackProp(this.x2Tensor)) { + // dx2 = -1 * x1 * x2 ^ -2. + const x2Squared = math.elementWiseMul(x2, x2); + + let x1OverX2Squared: NDArray; + if (x2IsScalar) { + x1OverX2Squared = math.arrayDividedByScalar(x1, x2Squared); + } else if (x1IsScalar) { + x1OverX2Squared = math.scalarDividedByArray(x1, x2Squared); + } else { + x1OverX2Squared = math.divide(x1, x2Squared); + } + + const dx2 = math.neg(x1OverX2Squared); + const dyTimesDerivative = math.elementWiseMul(dy, dx2); + + if (x2IsScalar) { + gradientArrays.set(this.x2Tensor, keep(math.sum(dyTimesDerivative))); + } else { + gradientArrays.set(this.x2Tensor, keep(dyTimesDerivative)); + } + } + }); + } +} diff --git a/src/ops/divide_test.ts b/src/ops/divide_test.ts new file mode 100644 index 0000000000..411bd73de6 --- /dev/null +++ b/src/ops/divide_test.ts @@ -0,0 +1,158 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Divide} from './divide'; + +describe('divide operation', () => { + let math: NDArrayMathCPU; + + let x1Tensor: Tensor; + let x2Tensor: Tensor; + let yTensor: Tensor; + let divideOp: Divide; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(x1Tensor); + activations.disposeArray(x2Tensor); + activations.disposeArray(yTensor); + gradients.disposeArray(x1Tensor); + gradients.disposeArray(x2Tensor); + gradients.disposeArray(yTensor); + }); + + it('element wise divide', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array1D.new([2, 4, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + divideOp = new Divide(x1Tensor, x2Tensor, yTensor); + divideOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toBeCloseTo(1 / 2); + expect(y.get(1)).toBeCloseTo(2 / 4); + expect(y.get(2)).toBeCloseTo(3 / 6); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + divideOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor); + expect(dx1.get(0)).toBeCloseTo(dy.get(0) / x2.get(0)); + expect(dx1.get(1)).toBeCloseTo(dy.get(1) / x2.get(1)); + expect(dx1.get(2)).toBeCloseTo(dy.get(2) / x2.get(2)); + + const dx2 = gradients.get(x2Tensor); + expect(dx2.get(0)) + .toBeCloseTo(-1 * x1.get(0) * dy.get(0) * Math.pow(x2.get(0), -2)); + expect(dx2.get(1)) + .toBeCloseTo(-1 * x1.get(1) * dy.get(1) * Math.pow(x2.get(1), -2)); + expect(dx2.get(2)) + .toBeCloseTo(-1 * x1.get(2) * dy.get(2) * Math.pow(x2.get(2), -2)); + }); + + it('scalar divided by ndarray', () => { + const x1 = Scalar.new(2); + const x2 = Array1D.new([2, 4, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + divideOp = new Divide(x1Tensor, x2Tensor, yTensor); + divideOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toBeCloseTo(2 / 2); + expect(y.get(1)).toBeCloseTo(2 / 4); + expect(y.get(2)).toBeCloseTo(2 / 6); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + divideOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor).asScalar(); + expect(dx1.get()).toBeCloseTo( + dy.get(0) / x2.get(0) + dy.get(1) / x2.get(1) + dy.get(2) / x2.get(2)); + + const dx2 = gradients.get(x2Tensor); + expect(dx2.get(0)) + .toBeCloseTo(-1 * x1.get() * dy.get(0) * Math.pow(x2.get(0), -2)); + expect(dx2.get(1)) + .toBeCloseTo(-1 * x1.get() * dy.get(1) * Math.pow(x2.get(1), -2)); + expect(dx2.get(2)) + .toBeCloseTo(-1 * x1.get() * dy.get(2) * Math.pow(x2.get(2), -2)); + }); + + it('ndarray divided by scalar', () => { + const x1 = Array1D.new([2, 4, 6]); + const x2 = Scalar.new(2); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + divideOp = new Divide(x1Tensor, x2Tensor, yTensor); + divideOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toBeCloseTo(2 / 2); + expect(y.get(1)).toBeCloseTo(4 / 2); + expect(y.get(2)).toBeCloseTo(6 / 2); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + divideOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor); + expect(dx1.get(0)).toBeCloseTo(dy.get(0) / x2.get()); + expect(dx1.get(1)).toBeCloseTo(dy.get(1) / x2.get()); + expect(dx1.get(2)).toBeCloseTo(dy.get(2) / x2.get()); + + const dx2 = gradients.get(x2Tensor).asScalar(); + expect(dx2.get()).toBeCloseTo( + -1 * x1.get(0) * dy.get(0) * Math.pow(x2.get(), -2) + + -1 * x1.get(1) * dy.get(1) * Math.pow(x2.get(), -2) + + -1 * x1.get(2) * dy.get(2) * Math.pow(x2.get(), -2)); + }); +}); \ No newline at end of file diff --git a/src/ops/element_wise_activation.ts b/src/ops/element_wise_activation.ts new file mode 100644 index 0000000000..bef181b040 --- /dev/null +++ b/src/ops/element_wise_activation.ts @@ -0,0 +1,93 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {ActivationFunction, ReLUFunc, SigmoidFunc, SquareFunc, TanHFunc} from '../math/activation_functions'; +import {NDArrayMath} from '../math/math'; +import {NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class ElementWiseActivation extends Operation { + constructor( + protected xTensor: Tensor, protected yTensor: Tensor, + private func: ActivationFunction) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor); + + math.scope((keep) => { + inferenceArrays.set(this.yTensor, keep(this.func.output(math, x))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + // dE/dx_i = sum_j dE/dy_j * dy_j/dx_i + // = dE/dy_i * dy_i/dx_i + const x = inferenceArrays.get(this.xTensor); + const y = inferenceArrays.get(this.yTensor); + const dy = gradientArrays.get(this.yTensor); + + math.scope((keep) => { + const dydx = this.func.der(math, x, y); + gradientArrays.set(this.xTensor, keep(math.elementWiseMul(dy, dydx))); + dydx.dispose(); + }); + } +} + +/** + * @hidden + */ +export class ReLU extends ElementWiseActivation { + constructor(xTensor: Tensor, yTensor: Tensor) { + super(xTensor, yTensor, new ReLUFunc()); + } +} + +/** + * @hidden + */ +export class TanH extends ElementWiseActivation { + constructor(xTensor: Tensor, yTensor: Tensor) { + super(xTensor, yTensor, new TanHFunc()); + } +} + +/** + * @hidden + */ +export class Sigmoid extends ElementWiseActivation { + constructor(xTensor: Tensor, yTensor: Tensor) { + super(xTensor, yTensor, new SigmoidFunc()); + } +} + +/** + * @hidden + */ +export class Square extends ElementWiseActivation { + constructor(xTensor: Tensor, yTensor: Tensor) { + super(xTensor, yTensor, new SquareFunc()); + } +} diff --git a/src/ops/element_wise_activation_test.ts b/src/ops/element_wise_activation_test.ts new file mode 100644 index 0000000000..887524f4b3 --- /dev/null +++ b/src/ops/element_wise_activation_test.ts @@ -0,0 +1,145 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import {ReLU, Sigmoid, Square, TanH} from './element_wise_activation'; + +describe('Element wise activation', () => { + let math: NDArrayMathCPU; + let xTensor: Tensor; + let yTensor: Tensor; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(xTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(xTensor); + gradients.disposeArray(yTensor); + }); + + it('ReLU', () => { + const x = Array2D.new([2, 3], [3, 0, -1, 2, 9, -5]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + activations.set(xTensor, x); + + const op = new ReLU(xTensor, yTensor); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.getValues()).toEqual(new Float32Array([3, 0, 0, 2, 9, 0])); + + // Backprop. + const dy = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + + expect(dx.getValues()).toEqual(new Float32Array([1, 0, 0, 4, 5, 0])); + }); + + it('TanH', () => { + const x = Array1D.new([3, 0, -3]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + activations.set(xTensor, x); + + const op = new TanH(xTensor, yTensor); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + + expect(y.get(0)).toBeCloseTo(0.99505475, 6); + expect(y.get(1)).toBeCloseTo(0, 6); + expect(y.get(2)).toBeCloseTo(-0.99505475, 6); + + // Backprop. + const dy = Array1D.new([2, 4, 3]); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + expect(dx.get(0)).toBeCloseTo(2 * (1 - 0.99505475 * 0.99505475), 6); + expect(dx.get(1)).toBeCloseTo(4, 6); + expect(dx.get(2)).toBeCloseTo(3 * (1 - 0.99505475 * 0.99505475), 6); + }); + + it('Sigmoid', () => { + const x = Array1D.new([3, 0, -3]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + activations.set(xTensor, x); + + const op = new Sigmoid(xTensor, yTensor); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toBeCloseTo(0.9525741268, 6); + expect(y.get(1)).toBeCloseTo(0.5, 6); + expect(y.get(2)).toBeCloseTo(0.0474258731, 6); + + // Backprop. + const dy = Array1D.new([2, 4, 3]); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + expect(dx.get(0)).toBeCloseTo(2 * 0.9525741268 * (1 - 0.9525741268), 6); + expect(dx.get(1)).toBeCloseTo(4 * 0.5 * 0.5, 6); + expect(dx.get(2)).toBeCloseTo(3 * 0.0474258731 * (1 - 0.0474258731), 6); + }); + + it('Square', () => { + const x = Array1D.new([2, 0, -3]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + activations.set(xTensor, x); + + const op = new Square(xTensor, yTensor); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.getValues()).toEqual(new Float32Array([4, 0, 9])); + + // Backprop. + const dy = Array1D.new([1, 2, 3]); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + expect(dx.get(0)).toBe(2 * x.get(0) * dy.get(0)); + expect(dx.get(1)).toBe(2 * x.get(1) * dy.get(1)); + expect(dx.get(2)).toBe(2 * x.get(2) * dy.get(2)); + }); +}); diff --git a/src/ops/element_wise_cost.ts b/src/ops/element_wise_cost.ts new file mode 100644 index 0000000000..e407822cd3 --- /dev/null +++ b/src/ops/element_wise_cost.ts @@ -0,0 +1,80 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {ElementWiseCostFunction, SquareCostFunc} from '../math/cost_functions'; +import {NDArrayMath} from '../math/math'; +import {Array1D, NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class ElementWiseCost extends Operation { + private oneOverNScalar: Scalar; + + constructor( + protected x1Tensor: Tensor, protected x2Tensor: Tensor, + protected yTensor: Tensor, protected func: ElementWiseCostFunction) { + super(); + this.oneOverNScalar = Scalar.new(1 / util.sizeFromShape(x1Tensor.shape)); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + const elementWiseCost = this.func.cost(math, x1, x2); + const sum = math.sum(elementWiseCost); + const result = math.scalarTimesArray(this.oneOverNScalar, sum); + inferenceArrays.set(this.yTensor, keep(result)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.x1Tensor)) { + gradientArrays.set(this.x1Tensor, keep(this.func.der(math, x1, x2))); + } + if (graph_util.shouldBackProp(this.x2Tensor)) { + gradientArrays.set(this.x2Tensor, keep(this.func.der(math, x2, x1))); + } + }); + } + + dispose() { + this.func.dispose(); + this.oneOverNScalar.dispose(); + } +} + +/** + * @hidden + */ +export class MeanSquaredCost extends ElementWiseCost { + constructor(x1Tensor: Tensor, x2Tensor: Tensor, yTensor: Tensor) { + super(x1Tensor, x2Tensor, yTensor, new SquareCostFunc()); + } +} diff --git a/src/ops/element_wise_cost_test.ts b/src/ops/element_wise_cost_test.ts new file mode 100644 index 0000000000..ae0aae777f --- /dev/null +++ b/src/ops/element_wise_cost_test.ts @@ -0,0 +1,73 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {MeanSquaredCost} from './element_wise_cost'; + +describe('MeanSquaredCost', () => { + let math: NDArrayMathCPU; + + let x1Tensor: Tensor; + let x2Tensor: Tensor; + let yTensor: Tensor; + let meanSquaredCostOperation: MeanSquaredCost; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(x1Tensor); + activations.disposeArray(x2Tensor); + activations.disposeArray(yTensor); + }); + + it('mean squared cost, forward & backward', () => { + const axis = 0; + + const x1 = Array1D.new([1, 2, 3, 4]); + const x2 = Array1D.new([2, 4, 6, 8]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor([]); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + meanSquaredCostOperation = new MeanSquaredCost(x1Tensor, x2Tensor, yTensor); + meanSquaredCostOperation.feedForward(math, activations); + meanSquaredCostOperation.backProp(math, activations, gradients); + + const y = activations.get(yTensor); + expect(y.shape).toEqual([]); + expect(y.getValues()).toEqual(new Float32Array([30 / 8])); + + const dx1 = gradients.get(x1Tensor); + const dx2 = gradients.get(x2Tensor); + expect(dx1.shape).toEqual(x1.shape); + expect(dx2.shape).toEqual(x2.shape); + expect(dx1.getValues()).toEqual(new Float32Array([-1, -2, -3, -4])); + expect(dx2.getValues()).toEqual(new Float32Array([1, 2, 3, 4])); + }); +}); \ No newline at end of file diff --git a/src/ops/exp.ts b/src/ops/exp.ts new file mode 100644 index 0000000000..8018dd7bd0 --- /dev/null +++ b/src/ops/exp.ts @@ -0,0 +1,56 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Exp extends Operation { + /** + * Exponentation operation - e^x. + */ + constructor(private xTensor: Tensor, private yTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor); + + math.scope((keep) => { + inferenceArrays.set(this.yTensor, keep(math.exp(x))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const y = inferenceArrays.get(this.yTensor); + const dy = gradientArrays.get(this.yTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.xTensor)) { + gradientArrays.set(this.xTensor, keep(math.elementWiseMul(y, dy))); + } + }); + } +} diff --git a/src/ops/exp_test.ts b/src/ops/exp_test.ts new file mode 100644 index 0000000000..a86dd92a53 --- /dev/null +++ b/src/ops/exp_test.ts @@ -0,0 +1,74 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Exp} from './exp'; + +describe('exp operation', () => { + let math: NDArrayMathCPU; + + let xTensor: Tensor; + let yTensor: Tensor; + let expOp: Exp; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(xTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(xTensor); + gradients.disposeArray(yTensor); + }); + + it('simple exp', () => { + const x = Array1D.new([1, 2, 3]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + + activations.set(xTensor, x); + + expOp = new Exp(xTensor, yTensor); + expOp.feedForward(math, activations); + const y = activations.get(yTensor); + + expect(y.shape).toEqual([3]); + expect(y.get(0)).toBeCloseTo(Math.exp(x.get(0))); + expect(y.get(1)).toBeCloseTo(Math.exp(x.get(1))); + expect(y.get(2)).toBeCloseTo(Math.exp(x.get(2))); + + const dy = Array1D.new([1, 2, 3]); + gradients.set(yTensor, dy); + + expOp.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + + expect(dx.shape).toEqual(dx.shape); + expect(dx.get(0)).toBeCloseTo(y.get(0) * dy.get(0)); + expect(dx.get(1)).toBeCloseTo(y.get(1) * dy.get(1)); + expect(dx.get(2)).toBeCloseTo(y.get(2) * dy.get(2)); + }); +}); \ No newline at end of file diff --git a/src/ops/linear_combination.ts b/src/ops/linear_combination.ts new file mode 100644 index 0000000000..82e5e40112 --- /dev/null +++ b/src/ops/linear_combination.ts @@ -0,0 +1,83 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class LinearCombination extends Operation { + /** + * A 2-tensor linear combination operation. + * + * Combines tensors x1 and x2 (of the same shape) with weights c1 & c2; + * Computes c1*x1 + c2*x2. + */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private c1Tensor: Tensor, private c2Tensor: Tensor, + private outTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + const c1 = inferenceArrays.get(this.c1Tensor).asScalar(); + const c2 = inferenceArrays.get(this.c2Tensor).asScalar(); + + math.scope((keep) => { + inferenceArrays.set( + this.outTensor, keep(math.scaledArrayAdd(c1, x1, c2, x2))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + const c1 = inferenceArrays.get(this.c1Tensor); + const c2 = inferenceArrays.get(this.c2Tensor); + const dy = gradientArrays.get(this.outTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.x1Tensor)) { + gradientArrays.set(this.x1Tensor, keep(math.scalarTimesArray(c1, dy))); + } + + if (graph_util.shouldBackProp(this.x2Tensor)) { + gradientArrays.set(this.x2Tensor, keep(math.scalarTimesArray(c2, dy))); + } + + if (graph_util.shouldBackProp(this.c1Tensor)) { + const dotProduct1 = math.elementWiseMul(x1, dy); + gradientArrays.set(this.c1Tensor, keep(math.sum(dotProduct1))); + } + + if (graph_util.shouldBackProp(this.c2Tensor)) { + const dotProduct2 = math.elementWiseMul(x2, dy); + gradientArrays.set(this.c2Tensor, keep(math.sum(dotProduct2))); + } + }); + } +} diff --git a/src/ops/linear_combination_test.ts b/src/ops/linear_combination_test.ts new file mode 100644 index 0000000000..a65e06dbf8 --- /dev/null +++ b/src/ops/linear_combination_test.ts @@ -0,0 +1,99 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import {LinearCombination} from './linear_combination'; + +describe('Linear combination', () => { + let math: NDArrayMathCPU; + let x1Tensor: Tensor; + let x2Tensor: Tensor; + let c1Tensor: Tensor; + let c2Tensor: Tensor; + let yTensor: Tensor; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(x1Tensor); + activations.disposeArray(x2Tensor); + activations.disposeArray(c1Tensor); + activations.disposeArray(c2Tensor); + activations.disposeArray(yTensor); + gradients.disposeArray(x1Tensor); + gradients.disposeArray(x2Tensor); + gradients.disposeArray(c1Tensor); + gradients.disposeArray(c2Tensor); + gradients.disposeArray(yTensor); + }); + + it('Simple linear combination', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array1D.new([10, 20, 30]); + const c1 = Scalar.new(3); + const c2 = Scalar.new(2); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + c1Tensor = new Tensor(c1.shape); + c2Tensor = new Tensor(c2.shape); + yTensor = new Tensor([]); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + activations.set(c1Tensor, c1); + activations.set(c2Tensor, c2); + + const op = + new LinearCombination(x1Tensor, x2Tensor, c1Tensor, c2Tensor, yTensor); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toBe(x1.get(0) * c1.get() + x2.get(0) * c2.get()); + expect(y.get(1)).toBe(x1.get(1) * c1.get() + x2.get(1) * c2.get()); + expect(y.get(2)).toBe(x1.get(2) * c1.get() + x2.get(2) * c2.get()); + + const dy = Array1D.new([2, 4, 6]); + gradients.set(yTensor, dy); + op.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor); + expect(dx1.get(0)).toBe(c1.get() * dy.get(0)); + expect(dx1.get(1)).toBe(c1.get() * dy.get(1)); + expect(dx1.get(2)).toBe(c1.get() * dy.get(2)); + + const dx2 = gradients.get(x2Tensor); + expect(dx2.get(0)).toBe(c2.get() * dy.get(0)); + expect(dx2.get(1)).toBe(c2.get() * dy.get(1)); + expect(dx2.get(2)).toBe(c2.get() * dy.get(2)); + + const dc1 = gradients.get(c1Tensor); + expect(dc1.get()).toBe( + x1.get(0) * dy.get(0) + x1.get(1) * dy.get(1) + x1.get(2) * dy.get(2)); + + const dc2 = gradients.get(c2Tensor); + expect(dc2.get()).toBe( + x2.get(0) * dy.get(0) + x2.get(1) * dy.get(1) + x2.get(2) * dy.get(2)); + }); +}); \ No newline at end of file diff --git a/src/ops/log.ts b/src/ops/log.ts new file mode 100644 index 0000000000..d79742cf2d --- /dev/null +++ b/src/ops/log.ts @@ -0,0 +1,56 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Log extends Operation { + /** + * Natural log operation - ln(x) + */ + constructor(private xTensor: Tensor, private yTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor); + + math.scope((keep) => { + inferenceArrays.set(this.yTensor, keep(math.log(x))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor); + const dy = gradientArrays.get(this.yTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.xTensor)) { + gradientArrays.set(this.xTensor, keep(math.divide(dy, x))); + } + }); + } +} diff --git a/src/ops/log_test.ts b/src/ops/log_test.ts new file mode 100644 index 0000000000..a83d1470b5 --- /dev/null +++ b/src/ops/log_test.ts @@ -0,0 +1,74 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Log} from './log'; + +describe('log operation', () => { + let math: NDArrayMathCPU; + + let xTensor: Tensor; + let yTensor: Tensor; + let logOp: Log; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(xTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(xTensor); + gradients.disposeArray(yTensor); + }); + + it('simple log', () => { + const x = Array1D.new([1, 2, 3]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(x.shape); + + activations.set(xTensor, x); + + logOp = new Log(xTensor, yTensor); + logOp.feedForward(math, activations); + const y = activations.get(yTensor); + + expect(y.shape).toEqual([3]); + expect(y.get(0)).toBeCloseTo(Math.log(x.get(0))); + expect(y.get(1)).toBeCloseTo(Math.log(x.get(1))); + expect(y.get(2)).toBeCloseTo(Math.log(x.get(2))); + + const dy = Array1D.new([1, 2, 3]); + gradients.set(yTensor, dy); + + logOp.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + + expect(dx.shape).toEqual(dx.shape); + expect(dx.get(0)).toBeCloseTo(dy.get(0) / x.get(0)); + expect(dx.get(1)).toBeCloseTo(dy.get(1) / x.get(1)); + expect(dx.get(2)).toBeCloseTo(dy.get(2) / x.get(2)); + }); +}); \ No newline at end of file diff --git a/src/ops/matmul.ts b/src/ops/matmul.ts new file mode 100644 index 0000000000..a86e2ddd00 --- /dev/null +++ b/src/ops/matmul.ts @@ -0,0 +1,93 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {MatrixOrientation, NDArrayMath} from '../math/math'; +import {Array1D, Array2D, NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class MatMul extends Operation { + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private yTensor: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + if (x1.shape.length === 2 && x2.shape.length === 2) { + inferenceArrays.set( + this.yTensor, keep(math.matMul(x1 as Array2D, x2 as Array2D))); + } else if (x1.shape.length === 2 && x2.shape.length === 1) { + inferenceArrays.set( + this.yTensor, + keep(math.matrixTimesVector(x1 as Array2D, x2 as Array1D))); + } else if (x1.shape.length === 1 && x2.shape.length === 2) { + inferenceArrays.set( + this.yTensor, + keep(math.vectorTimesMatrix(x1 as Array1D, x2 as Array2D))); + } + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + let x1 = inferenceArrays.get(this.x1Tensor); + let x2 = inferenceArrays.get(this.x2Tensor); + let dy = gradientArrays.get(this.yTensor); + + if (x1.shape.length === 1) { + x1 = x1.reshape([1, x1.size]); + dy = dy.reshape([1, dy.size]); + } + if (x2.shape.length === 1) { + x2 = x2.reshape([x2.size, 1]); + dy = dy.reshape([dy.size, 1]); + } + + math.scope((keep) => { + // y = x1 * x2 + // dx1 = dy * x2T + // dx2 = x1T * dy + if (graph_util.shouldBackProp(this.x1Tensor)) { + const dx1 = math.matMul( + dy as Array2D, x2 as Array2D, MatrixOrientation.REGULAR, + MatrixOrientation.TRANSPOSED); + gradientArrays.set( + this.x1Tensor, + keep(this.x1Tensor.shape.length === 1 ? dx1.as1D() : dx1)); + } + if (graph_util.shouldBackProp(this.x2Tensor)) { + const dx2 = math.matMul( + x1 as Array2D, dy as Array2D, MatrixOrientation.TRANSPOSED, + MatrixOrientation.REGULAR); + gradientArrays.set( + this.x2Tensor, + keep(this.x2Tensor.shape.length === 1 ? dx2.as1D() : dx2)); + } + }); + } +} diff --git a/src/ops/matmul_test.ts b/src/ops/matmul_test.ts new file mode 100644 index 0000000000..03acde0117 --- /dev/null +++ b/src/ops/matmul_test.ts @@ -0,0 +1,204 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {MatMul} from './matmul'; + +describe('add operation', () => { + let math: NDArrayMathCPU; + + let t1: Tensor; + let t2: Tensor; + let y: Tensor; + let matmulOp: MatMul; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(t1); + activations.disposeArray(t2); + activations.disposeArray(y); + gradients.disposeArray(t1); + gradients.disposeArray(t2); + gradients.disposeArray(y); + }); + + it('matmul two NDArray2Ds', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 10, 20, 30]); + const x2 = Array2D.new([3, 2], [2, 3, 4, 1, 2, 3]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor([x1.shape[0], x2.shape[1]]); + + activations.set(t1, x1); + activations.set(t2, x2); + + matmulOp = new MatMul(t1, t2, y); + matmulOp.feedForward(math, activations); + const yVal = activations.get(y) as Array2D; + + expect(yVal.shape).toEqual([x1.shape[0], x2.shape[1]]); + expect(yVal.get(0, 0)) + .toEqual( + x1.get(0, 0) * x2.get(0, 0) + x1.get(0, 1) * x2.get(1, 0) + + x1.get(0, 2) * x2.get(2, 0)); + expect(yVal.get(0, 1)) + .toEqual( + x1.get(0, 0) * x2.get(0, 1) + x1.get(0, 1) * x2.get(1, 1) + + x1.get(0, 2) * x2.get(2, 1)); + expect(yVal.get(1, 0)) + .toEqual( + x1.get(1, 0) * x2.get(0, 0) + x1.get(1, 1) * x2.get(1, 0) + + x1.get(1, 2) * x2.get(2, 0)); + expect(yVal.get(1, 1)) + .toEqual( + x1.get(1, 0) * x2.get(0, 1) + x1.get(1, 1) * x2.get(1, 1) + + x1.get(1, 2) * x2.get(2, 1)); + + const dy = Array2D.new([2, 2], [1, 2, 3, 4]); + gradients.set(y, dy); + + matmulOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1) as Array2D; + + // dx1 = dy * x2T + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.get(0, 0)) + .toEqual(dy.get(0, 0) * x2.get(0, 0) + dy.get(0, 1) * x2.get(0, 1)); + expect(dx1.get(0, 1)) + .toEqual(dy.get(0, 0) * x2.get(1, 0) + dy.get(0, 1) * x2.get(1, 1)); + expect(dx1.get(0, 2)) + .toEqual(dy.get(0, 0) * x2.get(2, 0) + dy.get(0, 1) * x2.get(2, 1)); + expect(dx1.get(1, 0)) + .toEqual(dy.get(1, 0) * x2.get(0, 0) + dy.get(1, 1) * x2.get(0, 1)); + expect(dx1.get(1, 1)) + .toEqual(dy.get(1, 0) * x2.get(1, 0) + dy.get(1, 1) * x2.get(1, 1)); + expect(dx1.get(1, 2)) + .toEqual(dy.get(1, 0) * x2.get(2, 0) + dy.get(1, 1) * x2.get(2, 1)); + + const dx2 = gradients.get(t2) as Array2D; + + // dx2 = x1T * dy + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.get(0, 0)) + .toEqual(x1.get(0, 0) * dy.get(0, 0) + x1.get(1, 0) * dy.get(1, 0)); + expect(dx2.get(0, 1)) + .toEqual(x1.get(0, 0) * dy.get(0, 1) + x1.get(1, 0) * dy.get(1, 1)); + expect(dx2.get(1, 0)) + .toEqual(x1.get(0, 1) * dy.get(0, 0) + x1.get(1, 1) * dy.get(1, 0)); + expect(dx2.get(1, 1)) + .toEqual(x1.get(0, 1) * dy.get(0, 1) + x1.get(1, 1) * dy.get(1, 1)); + expect(dx2.get(2, 0)) + .toEqual(x1.get(0, 2) * dy.get(0, 0) + x1.get(1, 2) * dy.get(1, 0)); + expect(dx2.get(2, 1)) + .toEqual(x1.get(0, 2) * dy.get(0, 1) + x1.get(1, 2) * dy.get(1, 1)); + }); + + it('matrix times vector', () => { + const inputSize = 3; + const outputSize = 2; + const x1 = Array2D.new([outputSize, inputSize], [1, 2, 0, 4, 3, 2]); + const x2 = Array1D.new([1, 2, 3]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor([x1.shape[0], x2.shape[1]]); + + activations.set(t1, x1); + activations.set(t2, x2); + + const op = new MatMul(t1, t2, y); + op.feedForward(math, activations); + + const yVal = activations.get(y); + expect(yVal.get(0)).toBe(5); + expect(yVal.get(1)).toBe(16); + + // Back prop. + const dy = Array1D.new([2, 3]); + gradients.set(y, dy); + + op.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1).as2D(x1.shape[0], x1.shape[1]); + expect(dx1.get(0, 0)).toBe(dy.get(0) * x2.get(0)); + expect(dx1.get(0, 1)).toBe(dy.get(0) * x2.get(1)); + expect(dx1.get(0, 2)).toBe(dy.get(0) * x2.get(2)); + expect(dx1.get(1, 0)).toBe(dy.get(1) * x2.get(0)); + expect(dx1.get(1, 1)).toBe(dy.get(1) * x2.get(1)); + expect(dx1.get(1, 2)).toBe(dy.get(1) * x2.get(2)); + + const dx2 = gradients.get(t2).as1D(); + expect(dx2.get(0)) + .toBe(x1.get(0, 0) * dy.get(0) + x1.get(1, 0) * dy.get(1)); + expect(dx2.get(1)) + .toBe(x1.get(0, 1) * dy.get(0) + x1.get(1, 1) * dy.get(1)); + expect(dx2.get(2)) + .toBe(x1.get(0, 2) * dy.get(0) + x1.get(1, 2) * dy.get(1)); + }); + + it('vector times matrix', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array2D.new([3, 2], [1, 2, 0, 4, 3, 2]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor([x1.shape[0], x2.shape[1]]); + + activations.set(t1, x1); + activations.set(t2, x2); + + const op = new MatMul(t1, t2, y); + op.feedForward(math, activations); + + const yVal = activations.get(y); + expect(yVal.get(0)).toBe(10); + expect(yVal.get(1)).toBe(16); + + // Back prop. + const dy = Array1D.new([2, 3]); + gradients.set(y, dy); + + op.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1).as1D(); + expect(dx1.get(0)) + .toBe(dy.get(0) * x2.get(0, 0) + dy.get(1) * x2.get(0, 1)); + expect(dx1.get(1)) + .toBe(dy.get(0) * x2.get(1, 0) + dy.get(1) * x2.get(1, 1)); + expect(dx1.get(2)) + .toBe(dy.get(0) * x2.get(2, 0) + dy.get(1) * x2.get(2, 1)); + + const dx2 = gradients.get(t2).as2D(x2.shape[0], x2.shape[1]); + expect(dx2.get(0, 0)).toBe(x1.get(0) * dy.get(0)); + expect(dx2.get(0, 1)).toBe(x1.get(0) * dy.get(1)); + expect(dx2.get(1, 0)).toBe(x1.get(1) * dy.get(0)); + expect(dx2.get(1, 1)).toBe(x1.get(1) * dy.get(1)); + expect(dx2.get(2, 0)).toBe(x1.get(2) * dy.get(0)); + expect(dx2.get(2, 1)).toBe(x1.get(2) * dy.get(1)); + }); +}); \ No newline at end of file diff --git a/src/ops/max_pool.ts b/src/ops/max_pool.ts new file mode 100644 index 0000000000..01ec8e6454 --- /dev/null +++ b/src/ops/max_pool.ts @@ -0,0 +1,72 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as conv_util from '../math/conv_util'; +import {NDArrayMath} from '../math/math'; +import {Array2D, Array3D, NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class MaxPool extends Operation { + private pad: number; + + constructor( + private xTensor: Tensor, private yTensor: Tensor, + private fieldSize: number, private stride = 1, pad?: number) { + super(); + + if (pad != null) { + this.pad = pad; + } else { + this.pad = conv_util.computeDefaultPad( + xTensor.shape as [number, number, number], this.fieldSize, + this.stride); + } + + util.assert( + util.isInt(this.pad), + `The zero padding (${this.pad}) must be an integer. Change the ` + + `stride and/or zero pad parameters`); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor) as Array3D; + math.scope((keep) => { + inferenceArrays.set( + this.yTensor, + keep(math.maxPool(x, this.fieldSize, this.stride, this.pad))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor) as Array3D; + const dy = gradientArrays.get(this.yTensor) as Array3D; + + math.scope((keep) => { + gradientArrays.set( + this.xTensor, + keep(math.maxPoolBackprop( + dy, x, this.fieldSize, this.stride, this.pad))); + }); + } +} diff --git a/src/ops/max_pool_test.ts b/src/ops/max_pool_test.ts new file mode 100644 index 0000000000..ac256c32db --- /dev/null +++ b/src/ops/max_pool_test.ts @@ -0,0 +1,158 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as conv_util from '../math/conv_util'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array3D, NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as test_util from '../test_util'; + +import {MaxPool} from './max_pool'; + + +describe('Max pool', () => { + let math: NDArrayMathCPU; + let xTensor: Tensor; + let yTensor: Tensor; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(xTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(xTensor); + gradients.disposeArray(yTensor); + }); + + it('Simple MaxPool', () => { + const fSize = 2; + const stride = 1; + const pad = 0; + const depth = 1; + + const x = Array3D.new([3, 3, depth], [1, 2, 3, 4, 5, 6, 7, 9, 8]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], stride, pad)); + + activations.set(xTensor, x); + + const op = new MaxPool(xTensor, yTensor, fSize, stride, pad); + + op.feedForward(math, activations); + + // Feed forward. + const y = activations.get(yTensor); + const expectedResult = Array3D.new([2, 2, depth], [5, 6, 9, 9]); + expect(expectedResult.equals(y)).toBe(true); + + // Backprop. + const dy = Array3D.new([2, 2, depth], [50, 60, 90, 80]); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dx = gradients.get(xTensor); + const expectedBackprop = + Array3D.new([3, 3, depth], [0, 0, 0, 0, 50, 60, 0, 170, 0]); + expect(expectedBackprop.equals(dx)).toBe(true); + }); + + it('MaxPool depth = 2', () => { + const fSize = 2; + const stride = 2; + const pad = 0; + const depth = 2; + + const x = Array3D.new([4, 4, depth], [ + 1, 11, 2, 22, 3, 33, 4, 44, 5, 55, 6, 66, 7, 77, 8, 88, + 9, 99, 10, 100, 11, 110, 12, 120, 13, 130, 14, 140, 15, 150, 16, 160 + ]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], stride, pad)); + + activations.set(xTensor, x); + + const op = new MaxPool(xTensor, yTensor, fSize, stride, pad); + + op.feedForward(math, activations); + + // Feed forward. + const y = activations.get(yTensor); + const expectedResult = + Array3D.new([2, 2, 2], [6, 66, 8, 88, 14, 140, 16, 160]); + test_util.expectArraysClose( + y.getValues(), expectedResult.getValues(), 1e-6); + }); + + it('MaxPool depth = 2, with some negative numbers', () => { + const fSize = 2; + const stride = 2; + const pad = 0; + const depth = 2; + + const x = Array3D.new([4, 4, 2], [ + -1, 11, 2, 22, 3, 33, 4, 44, 5, 55, 6, -66, 7, -77, 8, 88, + 9, 99, 10, 100, -11, 110, 12, 120, 13, 130, 14, 140, 15, 150, 16, -160 + ]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], stride, pad)); + + activations.set(xTensor, x); + + const op = new MaxPool(xTensor, yTensor, fSize, stride, pad); + op.feedForward(math, activations); + + // Feed forward. + const y = activations.get(yTensor); + const expectedResult = + Array3D.new([2, 2, 2], [6, 55, 8, 88, 14, 140, 16, 150]); + + test_util.expectArraysClose( + y.getValues(), expectedResult.getValues(), 1e-6); + }); + + it('MaxPool downsampling depth is preserved', () => { + const fSize = 2; + const stride = 2; + const pad = 0; + + const x = NDArray.randNormal([6, 6, 5]); + + xTensor = new Tensor(x.shape); + yTensor = new Tensor(conv_util.computeOutputShape3D( + x.shape, fSize, x.shape[2], stride, pad)); + + activations.set(xTensor, x); + + const op = new MaxPool(xTensor, yTensor, fSize, stride, pad); + op.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.shape).toEqual([3, 3, 5]); + }); +}); diff --git a/src/ops/multiply.ts b/src/ops/multiply.ts new file mode 100644 index 0000000000..1cb3229d5d --- /dev/null +++ b/src/ops/multiply.ts @@ -0,0 +1,99 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class Multiply extends Operation { + /** + * Element-wise multiply operation. Broadcasts if one of the tensors is + * scalar. + */ + constructor( + private x1Tensor: Tensor, private x2Tensor: Tensor, + private yTensor: Tensor) { + super(); + util.assert( + util.sizeFromShape(x1Tensor.shape) === 1 || + util.sizeFromShape(x2Tensor.shape) === 1 || + util.arraysEqual(x1Tensor.shape, x2Tensor.shape), + 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const t1 = inferenceArrays.get(this.x1Tensor); + const t2 = inferenceArrays.get(this.x2Tensor); + + math.scope((keep) => { + let result: NDArray; + if (util.isScalarShape(t1.shape)) { + result = math.scalarTimesArray(t1, t2); + } else if (util.isScalarShape(t2.shape)) { + result = math.scalarTimesArray(t2, t1); + } else { + result = math.elementWiseMul(t1, t2); + } + inferenceArrays.set(this.yTensor, keep(result)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const x1 = inferenceArrays.get(this.x1Tensor); + const x2 = inferenceArrays.get(this.x2Tensor); + const dy = gradientArrays.get(this.yTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.x1Tensor)) { + if (util.isScalarShape(this.x1Tensor.shape)) { + const mul = math.elementWiseMul(dy, x2); + + gradientArrays.set(this.x1Tensor, keep(math.sum(mul))); + + } else if (util.isScalarShape(x2.shape)) { + gradientArrays.set( + this.x1Tensor, keep(math.scalarTimesArray(x2, dy))); + } else { + gradientArrays.set(this.x1Tensor, keep(math.elementWiseMul(x2, dy))); + } + } + + if (graph_util.shouldBackProp(this.x2Tensor)) { + if (util.isScalarShape(this.x2Tensor.shape)) { + const mul = math.elementWiseMul(dy, x1); + + gradientArrays.set(this.x2Tensor, keep(math.sum(mul))); + + } else if (util.isScalarShape(x1.shape)) { + gradientArrays.set( + this.x2Tensor, keep(math.scalarTimesArray(x1, dy))); + } else { + gradientArrays.set(this.x2Tensor, keep(math.elementWiseMul(x1, dy))); + } + } + }); + } +} diff --git a/src/ops/multiply_test.ts b/src/ops/multiply_test.ts new file mode 100644 index 0000000000..4e0659634d --- /dev/null +++ b/src/ops/multiply_test.ts @@ -0,0 +1,150 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Multiply} from './multiply'; + +describe('divide operation', () => { + let math: NDArrayMathCPU; + + let x1Tensor: Tensor; + let x2Tensor: Tensor; + let yTensor: Tensor; + let multiplyOp: Multiply; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(x1Tensor); + activations.disposeArray(x2Tensor); + activations.disposeArray(yTensor); + gradients.disposeArray(x1Tensor); + gradients.disposeArray(x2Tensor); + gradients.disposeArray(yTensor); + }); + + it('element wise multiply', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array1D.new([2, 4, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + multiplyOp = new Multiply(x1Tensor, x2Tensor, yTensor); + multiplyOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toEqual(1 * 2); + expect(y.get(1)).toEqual(2 * 4); + expect(y.get(2)).toEqual(3 * 6); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + multiplyOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor); + expect(dx1.get(0)).toEqual(x2.get(0) * dy.get(0)); + expect(dx1.get(1)).toEqual(x2.get(1) * dy.get(1)); + expect(dx1.get(2)).toEqual(x2.get(2) * dy.get(2)); + + const dx2 = gradients.get(x2Tensor); + expect(dx2.get(0)).toEqual(x1.get(0) * dy.get(0)); + expect(dx2.get(1)).toEqual(x1.get(1) * dy.get(1)); + expect(dx2.get(2)).toEqual(x1.get(2) * dy.get(2)); + }); + + it('scalar times ndarray', () => { + const x1 = Scalar.new(2); + const x2 = Array1D.new([2, 4, 6]); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + multiplyOp = new Multiply(x1Tensor, x2Tensor, yTensor); + multiplyOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toEqual(2 * 2); + expect(y.get(1)).toEqual(2 * 4); + expect(y.get(2)).toEqual(2 * 6); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + multiplyOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor).asScalar(); + expect(dx1.get()).toEqual( + x2.get(0) * dy.get(0) + x2.get(1) * dy.get(1) + x2.get(2) * dy.get(2)); + + const dx2 = gradients.get(x2Tensor); + expect(dx2.get(0)).toEqual(x1.get() * dy.get(0)); + expect(dx2.get(1)).toEqual(x1.get() * dy.get(1)); + expect(dx2.get(2)).toEqual(x1.get() * dy.get(2)); + }); + + it('ndarray times scalar', () => { + const x1 = Array1D.new([2, 4, 6]); + const x2 = Scalar.new(2); + + x1Tensor = new Tensor(x1.shape); + x2Tensor = new Tensor(x2.shape); + yTensor = new Tensor(x2.shape); + + activations.set(x1Tensor, x1); + activations.set(x2Tensor, x2); + + multiplyOp = new Multiply(x1Tensor, x2Tensor, yTensor); + multiplyOp.feedForward(math, activations); + + const y = activations.get(yTensor); + expect(y.get(0)).toEqual(2 * 2); + expect(y.get(1)).toEqual(2 * 4); + expect(y.get(2)).toEqual(2 * 6); + + const dy = Array1D.new([3, 4, 5]); + gradients.set(yTensor, dy); + + multiplyOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(x1Tensor); + expect(dx1.get(0)).toEqual(x2.get() * dy.get(0)); + expect(dx1.get(1)).toEqual(x2.get() * dy.get(1)); + expect(dx1.get(2)).toEqual(x2.get() * dy.get(2)); + + const dx2 = gradients.get(x2Tensor).asScalar(); + expect(dx2.get()).toEqual( + x1.get(0) * dy.get(0) + x1.get(1) * dy.get(1) + x1.get(2) * dy.get(2)); + }); +}); \ No newline at end of file diff --git a/src/ops/op.ts b/src/ops/op.ts new file mode 100644 index 0000000000..74a467bbf6 --- /dev/null +++ b/src/ops/op.ts @@ -0,0 +1,35 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +/** + * @hidden + */ +export abstract class Operation { + abstract feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap): + void; + + abstract backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap): void; + + disposeTransientArrays( + inferenceArrays: TensorArrayMap, gradientArrays: TensorArrayMap) {} + + dispose() {} +} diff --git a/src/ops/reduce_sum.ts b/src/ops/reduce_sum.ts new file mode 100644 index 0000000000..6e7c4b840f --- /dev/null +++ b/src/ops/reduce_sum.ts @@ -0,0 +1,62 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * @hidden + */ +export class ReduceSum extends Operation { + /** Element-wise add operation. Broadcasts if one of the tensors is scalar. */ + constructor(private x: Tensor, private outTensor: Tensor) { + super(); + util.assertShapesMatch(outTensor.shape, []); + } + + private ones: NDArray; + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.x); + + math.scope((keep) => { + inferenceArrays.set(this.outTensor, keep(math.sum(x))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + if (!graph_util.shouldBackProp(this.x)) { + return; + } + + math.scope((keep) => { + const dy = gradientArrays.get(this.outTensor); + if (this.ones == null) { + const xArray = inferenceArrays.get(this.x); + this.ones = NDArray.zerosLike(xArray); + this.ones.fill(1); + } + gradientArrays.set(this.x, keep(math.scalarTimesArray(dy, this.ones))); + }); + } +} diff --git a/src/ops/reduce_sum_test.ts b/src/ops/reduce_sum_test.ts new file mode 100644 index 0000000000..75acdd75b9 --- /dev/null +++ b/src/ops/reduce_sum_test.ts @@ -0,0 +1,79 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {ReduceSum} from './reduce_sum'; + +describe('Reduce sum operation', () => { + let math: NDArrayMathCPU; + let reduceSumOp: ReduceSum; + let activations: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + }); + + afterEach(() => { + reduceSumOp.dispose(); + activations.dispose(); + }); + + it('Reduces a scalar', () => { + const xVal = Scalar.new(-3); + const x = new Tensor(xVal.shape); + const y = new Tensor([]); + + activations.set(x, xVal); + reduceSumOp = new ReduceSum(x, y); + reduceSumOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.get()).toBe(-3); + }); + + it('Reduces a 1-D tensor', () => { + const xVal = Array1D.new([1, 2, 3]); + const x = new Tensor(xVal.shape); + const y = new Tensor([]); + + activations.set(x, xVal); + reduceSumOp = new ReduceSum(x, y); + reduceSumOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.get()).toBe(6); + }); + + it('Reduces a 2-D tensor', () => { + const xVal = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x = new Tensor(xVal.shape); + const y = new Tensor([]); + + activations.set(x, xVal); + reduceSumOp = new ReduceSum(x, y); + reduceSumOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([]); + expect(yVal.get()).toBe(21); + }); +}); diff --git a/src/ops/reshape.ts b/src/ops/reshape.ts new file mode 100644 index 0000000000..1a90af7262 --- /dev/null +++ b/src/ops/reshape.ts @@ -0,0 +1,53 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMath} from '../math/math'; +import {NDArray} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +export class Reshape extends Operation { + constructor(private xTensor: Tensor, private yTensor: Tensor) { + super(); + const xSize = util.sizeFromShape(xTensor.shape); + const ySize = util.sizeFromShape(yTensor.shape); + util.assert( + xSize === ySize, + `The input size (${xSize}) and output size (${ySize}) must match`); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const x = inferenceArrays.get(this.xTensor) as T1; + + math.scope((keep) => { + inferenceArrays.set( + this.yTensor, keep(math.reshape(x, this.yTensor.shape))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const dy = gradientArrays.get(this.yTensor) as T2; + + math.scope((keep) => { + gradientArrays.set( + this.xTensor, keep(math.reshape(dy, this.xTensor.shape))); + }); + } +} diff --git a/src/ops/softmax.ts b/src/ops/softmax.ts new file mode 100644 index 0000000000..2997b9fb0c --- /dev/null +++ b/src/ops/softmax.ts @@ -0,0 +1,99 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMath} from '../math/math'; +import {Array1D, NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +export class Softmax extends Operation { + constructor(private logitsTensor: Tensor, private output: Tensor) { + super(); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const logits = inferenceArrays.get(this.logitsTensor) as Array1D; + return math.scope((keep) => { + inferenceArrays.set(this.output, keep(math.softmax(logits))); + }); + } + + backProp() { + throw Error('Softmax backprop is not yet implemented'); + } +} + +export class SoftmaxCrossEntropyCost extends Operation { + constructor( + private logitsTensor: Tensor, private labelTensor: Tensor, + private yTensor: Tensor) { + super(); + this.softmaxTensor = new Tensor(logitsTensor.shape); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const logits = inferenceArrays.get(this.logitsTensor) as Array1D; + const label = inferenceArrays.get(this.labelTensor) as Array1D; + + math.scope((keep) => { + const softmaxResult = math.softmax(logits); + + inferenceArrays.set(this.softmaxTensor, keep(softmaxResult)); + inferenceArrays.set( + this.yTensor, + keep(crossEntropyCost(math, softmaxResult, label, this.epsilon))); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const softmax = inferenceArrays.get(this.softmaxTensor); + const label = inferenceArrays.get(this.labelTensor); + + math.scope((keep) => { + gradientArrays.set(this.logitsTensor, keep(math.sub(softmax, label))); + }); + } + + disposeTransientArrays( + inferenceArrays: TensorArrayMap, gradientArrays: TensorArrayMap) { + inferenceArrays.disposeArray(this.softmaxTensor); + } + + dispose() { + this.epsilon.dispose(); + } + + private softmaxTensor: Tensor; + private epsilon = Scalar.new(1e-5); +} + +export function crossEntropyCost( + math: NDArrayMath, y: Array1D, target: Array1D, epsilon: Scalar): Scalar { + util.assert( + y.size === target.size, 'The output and target must be the same size'); + + return math.scope(() => { + const yPlusEps = math.scalarPlusArray(epsilon, y); + const logOutput = math.log(yPlusEps); + const tarLogOutput = math.elementWiseMul(target, logOutput); + const costVector = math.neg(tarLogOutput); + return math.sum(costVector); + }); +} diff --git a/src/ops/softmax_test.ts b/src/ops/softmax_test.ts new file mode 100644 index 0000000000..39975169e3 --- /dev/null +++ b/src/ops/softmax_test.ts @@ -0,0 +1,79 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {SoftmaxCrossEntropyCost} from './softmax'; + +describe('softmax cross entropy cost', () => { + let math: NDArrayMathCPU; + let logitsTensor: Tensor; + let labelTensor: Tensor; + let yTensor: Tensor; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(logitsTensor); + activations.disposeArray(yTensor); + gradients.disposeArray(logitsTensor); + gradients.disposeArray(yTensor); + }); + + it('matches theory', () => { + // Verify that when having softmax + cross entropy, + // dE/dx = y - t, which is the theoretical result. + const logits = Array1D.new([1, 2, 3]); + const label = Array1D.new([0.3, 0.6, 0.1]); + const softmaxLogits = math.softmax(logits); + + logitsTensor = new Tensor(logits.shape); + labelTensor = new Tensor(label.shape); + yTensor = new Tensor([]); + + activations.set(logitsTensor, logits); + activations.set(labelTensor, label); + + const op = new SoftmaxCrossEntropyCost(logitsTensor, labelTensor, yTensor); + + op.feedForward(math, activations); + const y = activations.get(yTensor); + + expect(y.get(0)).toBeCloseTo( + -Math.log(softmaxLogits.get(0)) * label.get(0) + + -Math.log(softmaxLogits.get(1)) * label.get(1) + + -Math.log(softmaxLogits.get(2)) * label.get(2), + 3); + + const dy = Scalar.new(1); + gradients.set(yTensor, dy); + + op.backProp(math, activations, gradients); + + const dLogits = gradients.get(logitsTensor); + expect(dLogits.get(0)).toBeCloseTo(softmaxLogits.get(0) - label.get(0), 6); + expect(dLogits.get(1)).toBeCloseTo(softmaxLogits.get(1) - label.get(1), 6); + expect(dLogits.get(2)).toBeCloseTo(softmaxLogits.get(2) - label.get(2), 6); + }); +}); diff --git a/src/ops/split.ts b/src/ops/split.ts new file mode 100644 index 0000000000..daa8738c02 --- /dev/null +++ b/src/ops/split.ts @@ -0,0 +1,62 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +/** + * Split ops are used to accumulate backprop derivatives when a node's output + * tensor is consumed by multiple nodes. + */ +export class Split extends Operation { + constructor(private input: Tensor, private outputs: Tensor[]) { + super(); + outputs.forEach(output => { + util.assertShapesMatch(input.shape, output.shape); + }); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const inputArray = inferenceArrays.get(this.input); + this.outputs.forEach(output => { + inferenceArrays.set(output, inputArray); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + if (!graph_util.shouldBackProp(this.input)) { + return; + } + + math.scope((keep) => { + let dx = math.add( + gradientArrays.get(this.outputs[0]), + gradientArrays.get(this.outputs[1])); + // Sum across all the derivatives of the consumers of this node. + this.outputs.slice(2).forEach(output => { + dx = math.add(dx, gradientArrays.get(output)); + }); + gradientArrays.set(this.input, keep(dx)); + }); + } +} diff --git a/src/ops/split_test.ts b/src/ops/split_test.ts new file mode 100644 index 0000000000..1ac3b81e43 --- /dev/null +++ b/src/ops/split_test.ts @@ -0,0 +1,76 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as test_util from '../test_util'; + +import {Split} from './split'; + +describe('Split operation', () => { + let math: NDArrayMathCPU; + + let splitOp: Split; + let tensorArrayMap: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + tensorArrayMap = new TensorArrayMap(); + }); + + afterEach(() => { + splitOp.dispose(); + tensorArrayMap.dispose(); + }); + + it('Forward prop split', () => { + const xVal = Scalar.new(-3); + const x = new Tensor(xVal.shape); + const y1 = new Tensor(x.shape); + const y2 = new Tensor(x.shape); + tensorArrayMap.set(x, xVal); + splitOp = new Split(x, [y1, y2]); + splitOp.feedForward(math, tensorArrayMap); + const y1Val = tensorArrayMap.get(y1); + const y2Val = tensorArrayMap.get(y2); + test_util.expectArraysClose(y1Val.getValues(), xVal.getValues(), 1e-5); + test_util.expectArraysClose(y2Val.getValues(), xVal.getValues(), 1e-5); + }); + + it('Forward+backward prop split', () => { + const xVal = Array1D.new([4, 5, -6]); + const x = new Tensor(xVal.shape); + const y1 = new Tensor(x.shape); + const y2 = new Tensor(x.shape); + tensorArrayMap.set(x, xVal); + splitOp = new Split(x, [y1, y2]); + splitOp.feedForward(math, tensorArrayMap); + const y1Val = tensorArrayMap.get(y1); + const y2Val = tensorArrayMap.get(y2); + test_util.expectArraysClose(y1Val.getValues(), xVal.getValues(), 1e-5); + test_util.expectArraysClose(y2Val.getValues(), xVal.getValues(), 1e-5); + + const gradientArrayMap = new TensorArrayMap(); + gradientArrayMap.set(y1, Array1D.new([-1, 4, 3])); + gradientArrayMap.set(y2, Array1D.new([-2, 2, -3])); + splitOp.backProp(math, tensorArrayMap, gradientArrayMap); + const dx = gradientArrayMap.get(x); + const expected = new Float32Array([-3, 6, 0]); + test_util.expectArraysClose(dx.getValues(), expected, 1e-5); + gradientArrayMap.dispose(); + }); +}); diff --git a/src/ops/subtract.ts b/src/ops/subtract.ts new file mode 100644 index 0000000000..b4bd7038ac --- /dev/null +++ b/src/ops/subtract.ts @@ -0,0 +1,102 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import * as graph_util from '../graph_util'; +import {NDArrayMath} from '../math/math'; +import {NDArray, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; +import * as util from '../util'; + +import {Operation} from './op'; + +export class Subtract extends Operation { + private dySizeScalar: Scalar; + + /** + * Element-wise subtract operation. Broadcasts if one of the tensors is + * scalar. + */ + constructor( + private t1: Tensor, private t2: Tensor, private outTensor: Tensor) { + super(); + util.assert( + util.sizeFromShape(t1.shape) === 1 || + util.sizeFromShape(t2.shape) === 1 || + util.arraysEqual(t1.shape, t2.shape), + 'One of t1 or t2 must be a scalar, or t1 and t2 must have ' + + 'the same shape'); + } + + feedForward(math: NDArrayMath, inferenceArrays: TensorArrayMap) { + const t1 = inferenceArrays.get(this.t1); + const t2 = inferenceArrays.get(this.t2); + + math.scope((keep) => { + let result: NDArray; + if (util.isScalarShape(t1.shape)) { + result = math.scalarMinusArray(t1, t2); + } else if (util.isScalarShape(t2.shape)) { + result = math.arrayMinusScalar(t1, t2); + } else { + result = math.sub(t1, t2); + } + inferenceArrays.set(this.outTensor, keep(result)); + }); + } + + backProp( + math: NDArrayMath, inferenceArrays: TensorArrayMap, + gradientArrays: TensorArrayMap) { + const t1 = inferenceArrays.get(this.t1); + const t2 = inferenceArrays.get(this.t2); + const dy = gradientArrays.get(this.outTensor); + + math.scope((keep) => { + if (graph_util.shouldBackProp(this.t1)) { + if (util.isScalarShape(this.t1.shape)) { + const sum = math.sum(dy); + if (this.dySizeScalar == null) { + this.dySizeScalar = Scalar.new(dy.size); + } + gradientArrays.set( + this.t1, keep(math.divide(sum, this.dySizeScalar))); + } else { + gradientArrays.set(this.t1, keep(dy)); + } + } + + if (graph_util.shouldBackProp(this.t2)) { + if (util.isScalarShape(this.t2.shape)) { + const sum = math.sum(dy); + const negSum = math.neg(sum); + if (this.dySizeScalar == null) { + this.dySizeScalar = Scalar.new(dy.size); + } + gradientArrays.set( + this.t2, keep(math.divide(negSum, this.dySizeScalar))); + } else { + gradientArrays.set(this.t2, keep(math.neg(dy))); + } + } + }); + } + + dispose() { + if (this.dySizeScalar != null) { + this.dySizeScalar.dispose(); + } + } +} diff --git a/src/ops/subtract_test.ts b/src/ops/subtract_test.ts new file mode 100644 index 0000000000..8852592b6f --- /dev/null +++ b/src/ops/subtract_test.ts @@ -0,0 +1,197 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from '../graph'; +import {NDArrayMathCPU} from '../math/math_cpu'; +import {Array1D, Array2D, Scalar} from '../math/ndarray'; +import {TensorArrayMap} from '../tensor_array_map'; + +import {Subtract} from './subtract'; + +describe('add operation', () => { + let math: NDArrayMathCPU; + + let t1: Tensor; + let t2: Tensor; + let y: Tensor; + let subOp: Subtract; + let activations: TensorArrayMap; + let gradients: TensorArrayMap; + + beforeEach(() => { + math = new NDArrayMathCPU(); + activations = new TensorArrayMap(); + gradients = new TensorArrayMap(); + }); + + afterEach(() => { + activations.disposeArray(t1); + activations.disposeArray(t2); + activations.disposeArray(y); + gradients.disposeArray(t1); + gradients.disposeArray(t2); + gradients.disposeArray(y); + }); + + it('subtracts two 1-D tensors', () => { + const x1 = Array1D.new([1, 2, 3]); + const x2 = Array1D.new([3, 0, 3]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + subOp = new Subtract(t1, t2, y); + subOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([3]); + expect(yVal.getValues()).toEqual(new Float32Array([-2, 2, 0])); + + const dy = Array1D.new([6, 7, 8]); + gradients.set(y, dy); + + subOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(new Float32Array([-6, -7, -8])); + }); + + it('subtracts two 2-D tensors', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Array2D.new([2, 3], [9, 8, 7, 6, 5, 4]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + subOp = new Subtract(t1, t2, y); + subOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([-8, -6, -4, -2, 0, 2])); + + const dy = Array2D.new([2, 3], [10, 11, 12, 13, 14, 15]); + gradients.set(y, dy); + + subOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(new Float32Array([ + -10, -11, -12, -13, -14, -15 + ])); + }); + + it('ndarray - scalar', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Scalar.new(2); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + subOp = new Subtract(t1, t2, y); + subOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([-1, 0, 1, 2, 3, 4])); + + const dy = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + gradients.set(y, dy); + + subOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.getValues()).toEqual(dy.getValues()); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.get()).toEqual(-7); + }); + + it('scalar - ndarray', () => { + const x1 = Scalar.new(2); + const x2 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + subOp = new Subtract(t1, t2, y); + subOp.feedForward(math, activations); + const yVal = activations.get(y); + + expect(yVal.shape).toEqual([2, 3]); + expect(yVal.getValues()).toEqual(new Float32Array([1, 0, -1, -2, -3, -4])); + + const dy = Array2D.new([2, 3], [2, 4, 6, 8, 10, 12]); + gradients.set(y, dy); + + subOp.backProp(math, activations, gradients); + + const dx1 = gradients.get(t1); + const dx2 = gradients.get(t2); + + expect(dx1.shape).toEqual(x1.shape); + expect(dx1.get()).toEqual(7); + + expect(dx2.shape).toEqual(x2.shape); + expect(dx2.getValues()).toEqual(new Float32Array([ + -2, -4, -6, -8, -10, -12 + ])); + }); + + it('throws when shapes of X1 and X2 do not match', () => { + const x1 = Array2D.new([2, 3], [1, 2, 3, 4, 5, 6]); + const x2 = Array2D.new([3, 2], [1, 2, 3, 4, 5, 6]); + + t1 = new Tensor(x1.shape); + t2 = new Tensor(x2.shape); + y = new Tensor(x1.shape); + + activations.set(t1, x1); + activations.set(t2, x2); + + expect(() => new Subtract(t1, t2, y)).toThrowError(); + }); +}); diff --git a/src/optimizer.ts b/src/optimizer.ts new file mode 100644 index 0000000000..e4cd9b19ad --- /dev/null +++ b/src/optimizer.ts @@ -0,0 +1,47 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Node, Tensor, VariableNode} from './graph'; +import {NDArrayMath} from './math/math'; +import {SessionRuntime} from './session'; +import {TensorArrayMap} from './tensor_array_map'; + +export abstract class Optimizer { + protected variableNodes: VariableNode[]; + protected specifiedVariableNodes: VariableNode[]|null; + + constructor(specifiedVariableList?: Node[]) { + if (specifiedVariableList != null) { + this.specifiedVariableNodes = specifiedVariableList as VariableNode[]; + } + } + + abstract beforeBatch( + math: NDArrayMath, batchSize: number, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, + gradientArrayMap: TensorArrayMap): void; + + abstract afterExample( + math: NDArrayMath, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, + gradientArrayMap: TensorArrayMap): void; + + abstract afterBatch( + math: NDArrayMath, batchSize: number, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, + gradientArrayMap: TensorArrayMap): void; + + abstract dispose(): void; +} diff --git a/src/priority_queue.ts b/src/priority_queue.ts new file mode 100644 index 0000000000..3f0839c2c4 --- /dev/null +++ b/src/priority_queue.ts @@ -0,0 +1,223 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +/** + * Default comparison function for the priority queue. + * @param a The first element to compare. + * @param b The second element to compare. + * @return "a > b" returns > 0. "a < b" returns < 0. "a === b" returns 0. + */ +export function defaultCompare(a: T, b: T): number { + if (a === b) { + return 0; + } else if (a < b) { + return -1; + } else { + return 1; + } +} + +/** + * A Comparator is a user-provided function that compares two T instances. The + * convention for defaultCompare is expected to be followed to maintain the + * binary min-heap integrity. + * @param a The first element to compare. + * @param b The second element to compare. + */ +export type Comparator = (a: T, b: T) => number; + +/** + * IndexObserver is a user-provided callback that informs the caller when an + * element in the priority queue's binary min-heap has been relocated. + * @param t The element that was relocated. + * @param newIndex The new location in the binary min-heap of the element. + */ +export type IndexObserver = (t: T, newIndex: number) => void; + +/** + * A priority queue, implemented in terms of a binary min-heap. Lower priority + * numbers are considered higher priority. + * enqueue, dequeue, and update are all O(log N) with respect to the number of + * elements in the queue. + */ +export class PriorityQueue { + private heap: T[] = []; + + /** + * @param comparator A function that compares two queue elements. + * @param indexObserver An optional callback raised when the priority queue + * changes the order of elements in its min-heap. Useful for tracking the + * positions of elements that need updating. + */ + constructor( + private comparator: Comparator, + private indexObserver?: IndexObserver) {} + + /** + * Add an element to the priority queue. + * @param t The element to enqueue. + */ + enqueue(t: T) { + this.heap.push(t); + this.onIndexChanged(t, this.heap.length - 1); + this.siftUp(this.heap.length - 1); + } + + /** + * Remove an element from the priority queue. + * @return The element in the priority queue with the highest priority + * (lowest numeric priority value). + */ + dequeue(): T { + if (this.empty()) { + throw new Error('dequeue called on empty priority queue.'); + } + const t = this.heap[0]; + this.swap(0, this.heap.length - 1); + this.heap.pop(); + this.siftDown(0); + return t; + } + + /** + * Updates an element at the specified index. This can be a full element + * replacement, or it can be an in-place update. The priority is assumed to be + * changed, and the internal storage is updated. This function is only useful + * if the storage index of the updated element is known; construct the + * PriorityQueue with an IndexObserver to track element locations. + * @param newT The new element to replace in the priority queue. + * @param index The index to insert the new element into. + */ + update(newT: T, index: number) { + /* If the element is at the very end of the heap, no sifting is necessary, + * it can be safely removed. */ + const last = (index === this.heap.length - 1); + if (!last) { + this.swap(index, this.heap.length - 1); + } + this.heap.pop(); + if (!last) { + /* The element at 'index' has been removed, and replaced with whatever was + * at the end of the heap. Since that element might have come from a + * different subtree (and not be a direct descendant of the node at + * 'index'), we might need to sift this new value up instead of down. Test + * both directions, and sift to wherever the node needs to go. + */ + if (this.siftUpIndex(index) !== -1) { + this.siftUp(index); + } else if (this.siftDownIndex(index) !== -1) { + this.siftDown(index); + } + } + this.enqueue(newT); + } + + /** + * Predicate for testing whether the PriorityQueue is empty. + * @return True if the PriorityQueue is empty, otherwise False. + */ + empty(): boolean { + return this.heap.length === 0; + } + + private onIndexChanged(t: T, newIndex: number) { + if (this.indexObserver) { + this.indexObserver(t, newIndex); + } + } + + /* + * Standard zero-indexed binary heap array layout: + * Parent(N) = Floor((N - 1) / 2) + * LeftChild(N) = (N * 2) + 1 + * RightChild(N) = (N * 2) + 2 + */ + + private getParentIndex(index: number): number { + if (index === 0) { + return -1; + } + return Math.floor((index - 1) / 2); + } + + private getLeftChildIndex(index: number): number { + const candidate = index * 2 + 1; + return candidate < this.heap.length ? candidate : -1; + } + + private getRightChildIndex(index: number): number { + const candidate = index * 2 + 2; + return candidate < this.heap.length ? candidate : -1; + } + + private siftUpIndex(index: number): number { + const parentIndex = this.getParentIndex(index); + if (parentIndex === -1) { + return -1; + } + if (this.compare(parentIndex, index) > 0) { + return parentIndex; + } + return -1; + } + + private siftUp(index: number) { + let siftIndex = this.siftUpIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftUpIndex(index); + } + } + + private siftDownIndex(index: number): number { + if (index >= this.heap.length) { + return -1; + } + let largestChildIndex = index; + const leftChildIndex = this.getLeftChildIndex(index); + if ((leftChildIndex !== -1) && + (this.compare(leftChildIndex, largestChildIndex) < 0)) { + largestChildIndex = leftChildIndex; + } + const rightChildIndex = this.getRightChildIndex(index); + if ((rightChildIndex !== -1) && + (this.compare(rightChildIndex, largestChildIndex) < 0)) { + largestChildIndex = rightChildIndex; + } + return (largestChildIndex === index) ? -1 : largestChildIndex; + } + + private siftDown(index: number) { + let siftIndex = this.siftDownIndex(index); + while (siftIndex !== -1) { + this.swap(index, siftIndex); + index = siftIndex; + siftIndex = this.siftDownIndex(index); + } + } + + private compare(aIndex: number, bIndex: number): number { + return this.comparator(this.heap[aIndex], this.heap[bIndex]); + } + + private swap(a: number, b: number) { + const temp = this.heap[a]; + this.heap[a] = this.heap[b]; + this.heap[b] = temp; + this.onIndexChanged(this.heap[a], a); + this.onIndexChanged(this.heap[b], b); + } +} diff --git a/src/priority_queue_test.ts b/src/priority_queue_test.ts new file mode 100644 index 0000000000..b6eb656cfe --- /dev/null +++ b/src/priority_queue_test.ts @@ -0,0 +1,199 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as priority_queue from './priority_queue'; +import {PriorityQueue} from './priority_queue'; + +describe('defaultCompare', () => { + it('returns 0 if a === b', () => { + expect(priority_queue.defaultCompare(123, 123)).toEqual(0); + }); + + it('returns 1 if a > b', () => { + expect(priority_queue.defaultCompare(1000, 999)).toEqual(1); + }); + + it('returns -1 if a < b', () => { + expect(priority_queue.defaultCompare(999, 1000)).toEqual(-1); + }); +}); + +describe('PriorityQueue', () => { + let pq: PriorityQueue; + + beforeEach(() => { + pq = new PriorityQueue(priority_queue.defaultCompare); + }); + + it('is empty by default', () => { + expect(pq.empty()).toEqual(true); + }); + + it('isn\'t empty after enqueue call', () => { + pq.enqueue(0); + expect(pq.empty()).toEqual(false); + }); + + it('returns to empty after dequeueing only element', () => { + pq.enqueue(0); + pq.dequeue(); + expect(pq.empty()).toEqual(true); + }); + + it('returns to empty after dequeueing last element', () => { + for (let i = 0; i < 10; ++i) { + pq.enqueue(i); + } + for (let i = 0; i < 9; ++i) { + pq.dequeue(); + expect(pq.empty()).toEqual(false); + } + pq.dequeue(); + expect(pq.empty()).toEqual(true); + }); + + it('dequeue throws when queue is empty', () => { + expect(() => pq.dequeue()) + .toThrow(new Error('dequeue called on empty priority queue.')); + }); + + it('dequeues the only enqueued item', () => { + pq.enqueue(1); + expect(pq.dequeue()).toEqual(1); + }); + + it('dequeues the lowest-priority of 2 items', () => { + pq.enqueue(1000); + pq.enqueue(0); + expect(pq.dequeue()).toEqual(0); + }); + + it('dequeues items in min-priority order', () => { + pq.enqueue(5); + pq.enqueue(8); + pq.enqueue(2); + pq.enqueue(9); + pq.enqueue(3); + pq.enqueue(7); + pq.enqueue(4); + pq.enqueue(0); + pq.enqueue(6); + pq.enqueue(1); + const dequeued: number[] = []; + while (!pq.empty()) { + dequeued.push(pq.dequeue()); + } + expect(dequeued).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); +}); + +describe('PriorityQueue index observer', () => { + let pq: PriorityQueue; + let indices: {[value: number]: number}; + + beforeEach(() => { + pq = new PriorityQueue( + priority_queue.defaultCompare, + (value: number, newIndex: number) => indices[value] = newIndex); + indices = {}; + }); + + it('notifies of new index when enqueuing', () => { + pq.enqueue(0); + expect(indices[0]).not.toBe(null); + }); + + it('puts first enqueued element at root of heap (index 0)', () => { + pq.enqueue(0); + expect(indices[0]).toEqual(0); + }); + + it('puts second greater element at left child of root (index 1)', () => { + pq.enqueue(0); + pq.enqueue(1); + expect(indices[0]).toEqual(0); + expect(indices[1]).toEqual(1); + }); + + it('puts third greater element at right child of root (index 2)', () => { + pq.enqueue(0); + pq.enqueue(1); + pq.enqueue(2); + expect(indices[0]).toEqual(0); + expect(indices[1]).toEqual(1); + expect(indices[2]).toEqual(2); + }); + + it('swaps root with new min enqueued element', () => { + pq.enqueue(1000); + pq.enqueue(0); + expect(indices[1000]).toEqual(1); + expect(indices[0]).toEqual(0); + }); +}); + +class TestEntry { + constructor(public id: number, public priority: number) {} +} + +describe('PriorityQueue.update', () => { + let pq: PriorityQueue; + let indices: {[id: number]: number}; + + beforeEach(() => { + pq = new PriorityQueue( + (a: TestEntry, b: TestEntry) => + priority_queue.defaultCompare(a.priority, b.priority), + (entry: TestEntry, newIndex: number) => indices[entry.id] = newIndex); + indices = {}; + }); + + it('no longer dequeues original min element after priority change', () => { + const e0 = new TestEntry(0, 10); + const e1 = new TestEntry(1, 100); + pq.enqueue(e0); + pq.enqueue(e1); + e0.priority = 101; + pq.update(e0, 0); + expect(pq.dequeue()).toBe(e1); + expect(pq.dequeue()).toBe(e0); + }); + + it('doesn\'t change index when priority doesn\'t change', () => { + const e = new TestEntry(0, 0); + pq.enqueue(e); + expect(indices[0]).toEqual(0); + pq.update(e, 0); + expect(indices[0]).toEqual(0); + }); + + it('doesn\'t change index when priority doesn\'t trigger sift', () => { + const e = new TestEntry(0, 0); + pq.enqueue(e); + expect(indices[0]).toEqual(0); + e.priority = 1234; + pq.update(e, 0); + expect(indices[0]).toEqual(0); + }); + + it('changes index when priority change triggers sift', () => { + const e = new TestEntry(0, 10); + pq.enqueue(e); + pq.enqueue(new TestEntry(1, 100)); + e.priority = 1000; + pq.update(e, 0); + expect(indices[0]).toEqual(1); + }); +}); diff --git a/src/session.ts b/src/session.ts new file mode 100644 index 0000000000..b40a39c114 --- /dev/null +++ b/src/session.ts @@ -0,0 +1,285 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Graph, Node, Tensor} from './graph'; +import * as graph_util from './graph_util'; +import {InputProvider} from './input_provider'; +import {NDArrayMath} from './math/math'; +import {NDArray, Scalar} from './math/ndarray'; +import * as operation_emitter from './operation_emitter'; +import {Operation} from './ops/op'; +import {Optimizer} from './optimizer'; +import * as session_util from './session_util'; +import {TensorArrayMap} from './tensor_array_map'; +import * as util from './util'; + +/** + * FeedEntry associates a tensor with user-provided NDArray data. + */ +export type FeedEntry = { + tensor: Tensor, + data: NDArray|InputProvider +}; + +/** + * A FeedDictionary holds a map from tensors to user-provided NDArrays. Feed + * dictionaries represent the 'entry points' of evaluation, since graph nodes + * that are replaced by feeds don't need to have their input nodes evaluated. + * Feed dictionaries usually provide NDArray data for Placeholder nodes, but any + * node in the graph can be replaced by a feed dictionary entry. + * + * @hidden + */ +export class FeedDictionary { + dict: {[tensorID: number]: FeedEntry} = {}; + + /** + * Optionally construct a FeedDictionary from an array of entries. + * @param feedEntries Optional array of FeedEntry objects. + */ + constructor(feedEntries?: FeedEntry[]) { + if (feedEntries) { + feedEntries.forEach(entry => this.dict[entry.tensor.id] = entry); + } + } +} + +export enum CostReduction { + NONE, + SUM, + MEAN +} + +/** + * A Session maintains the runtime state required to efficiently evaluate nodes. + * On their own, graph objects are very lightweight logical topologies; they + * have no relationship with the GPU. Sessions encapsulate the evaluation of + * nodes, the management of GPU resources, the caching of evaluation paths, and + * anything else required to evaluate or train a network. + */ +export class Session { + /** + * @param graph The graph to associate with this Session. + * @param math The NDArrayMath interface that this Session should use. + */ + constructor(private graph: Graph, private math: NDArrayMath) {} + + /** + * Release all system resources associated with this Session. + */ + dispose() { + this.activationArrayMap.dispose(); + Object.keys(this.runtimeCache).forEach(key => { + const runtime = this.runtimeCache[key]; + if (runtime.operations) { + runtime.operations.forEach(op => op.dispose()); + } + }); + this.runtimeCache = {}; + if (this.batchSizeScalar != null) { + this.batchSizeScalar.dispose(); + } + this.oneScalar.dispose(); + } + + /** + * Evaluate a list of tensors, using the provided feed entries to provide + * upstream NDArray input. + * When using a `NDArrayMath` object in safe mode this must be used in a + * math.scope(). + * @param tensors The list of tensors to evaluate. + * @param feedEntries List of `FeedEntry` to read when replacing graph + * tensors with NDArrays. + * @return The computed values of the tensors. + */ + evalAll(tensors: Tensor[], feedEntries: FeedEntry[]): NDArray[] { + return this.math.scope(() => { + const feed = new FeedDictionary(feedEntries); + const runtime = this.getOrCreateRuntime(tensors, feed); + + const activations = this.activationArrayMap; + + session_util.disposeAndInitializeOperationOutputs( + runtime.nodes, activations); + session_util.disposeTransientOperationArrays( + runtime.operations, this.activationArrayMap, this.gradientArrayMap); + + session_util.addPersistentArraysToTensorArrayMap( + runtime.nodes, activations); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap( + feed, activations, this.math); + + runtime.operations.forEach(op => op.feedForward(this.math, activations)); + + const results = tensors.map(x => activations.get(x)); + tensors.forEach(x => activations.delete(x)); + + session_util.releaseFeedDictionaryInputsFromTensorArrayMap( + feed, activations, this.math); + + return results; + }); + } + + /** + * Evaluate a tensor, using the provided feed entries to provide + * upstream NDArray input. + * + * @param tensor The tensor to evaluate. + * @param feedEntries List of `FeedEntry` to read when replacing graph + * tensors with NDArrays. + * @return The computed value of the tensor. + */ + eval(tensor: Tensor, feedEntries: FeedEntry[]): NDArray { + return this.evalAll([tensor], feedEntries)[0]; + } + + /** + * Trains a batch. + * Returns a reduced cost if the costReduction parameter is set. + * When using a `NDArrayMath` object in safe mode this must be used in a + * math.scope(). + * @param costTensor A tensor representing the cost to optimize. Should be a + * scalar. + * @param feedEntries Feed entries for this train run. Provides inputs. + * @param batchSize Batch size for this train loop. + * @param optimizer An optimizer to perform weight updates. + * @param costReduction An option to allow the user to get a summed, averaged, + * or no cost back. + * @return The reduced cost, if cost reduction is not NONE. The user is + * responsible for disposing the cost NDArray between train loops. + */ + train( + costTensor: Tensor, feedEntries: FeedEntry[], batchSize: number, + optimizer: Optimizer, costReduction = CostReduction.NONE): Scalar { + util.assert( + util.isScalarShape(costTensor.shape), + 'Cost tensor for training must be a scalar value.'); + + if (this.prevBatchSize !== batchSize) { + this.prevBatchSize = batchSize; + this.batchSizeScalar = Scalar.new(batchSize); + } + + const feed = new FeedDictionary(feedEntries); + session_util.throwIfFeedDictionaryContainsNDArrays(feed); + + const runtime = this.getOrCreateRuntime([costTensor], feed); + const inferenceOperations = runtime.operations; + const backPropOperations = runtime.operations.slice().reverse(); + const activations = this.activationArrayMap; + const gradients = this.gradientArrayMap; + gradients.set(costTensor, this.oneScalar); + + session_util.addPersistentArraysToTensorArrayMap( + runtime.nodes, activations); + + optimizer.beforeBatch( + this.math, batchSize, runtime, activations, gradients); + + return this.math.scope((keep, track) => { + let cost = track(Scalar.new(0)); + + for (let i = 0; i < batchSize; ++i) { + session_util.disposeAndInitializeOperationOutputs( + runtime.nodes, activations); + session_util.disposeAndInitializeOperationInputGradients( + runtime.nodes, gradients); + session_util.disposeTransientOperationArrays( + runtime.operations, activations, gradients); + + session_util.loadInputsFromFeedDictionaryToTensorArrayMap( + feed, activations, this.math); + + inferenceOperations.forEach( + op => op.feedForward(this.math, activations)); + backPropOperations.forEach( + op => op.backProp(this.math, activations, gradients)); + + optimizer.afterExample(this.math, runtime, activations, gradients); + + session_util.releaseFeedDictionaryInputsFromTensorArrayMap( + feed, activations, this.math); + + cost = this.updateCostForExample( + cost, activations.get(costTensor), costReduction); + } + + optimizer.afterBatch( + this.math, batchSize, runtime, activations, gradients); + + return this.updateCostForBatch(cost, costReduction); + }); + } + + private updateCostForExample( + totalCost: Scalar, currCost: Scalar, + costReduction: CostReduction): Scalar { + if (costReduction === CostReduction.MEAN || + costReduction === CostReduction.SUM) { + return this.math.add(totalCost, currCost); + } + return totalCost; + } + + private updateCostForBatch(totalCost: Scalar, costReduction: CostReduction): + Scalar { + if (costReduction === CostReduction.MEAN) { + return this.math.divide(totalCost, this.batchSizeScalar); + } + return totalCost; + } + + private getOrCreateRuntime(tensors: Tensor[], feed: FeedDictionary): + SessionRuntime { + const key = this.makeRuntimeCacheKey(tensors, feed); + let runtime = this.runtimeCache[key]; + if (runtime === undefined) { + let nodes = + session_util.getOrderedEvaluationSetFromEvalTensor(tensors, feed); + // In inference mode split nodes are not needed, but their cost is + // negligible, and always adding them in allows for caching of 1 runtime + // for both train/eval. + nodes = session_util.addSplitNodes(nodes); + session_util.removeFeedDictionaryNodesFromEvaluationSet(feed, nodes); + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes(nodes); + const operations = operation_emitter.emitFromGraphNodes(nodes); + runtime = {nodes, operations}; + this.runtimeCache[key] = runtime; + } + + return runtime; + } + + private makeRuntimeCacheKey(tensors: Tensor[], feed: FeedDictionary): string { + return tensors.map(x => x.id).sort().join('_') + '__' + + Object.keys(feed.dict).sort().join('_'); + } + + /** Maps each output tensor of the graph to its activation value. */ + activationArrayMap = new TensorArrayMap(); + /** Maps each tensor of the graph to its derivative wrt the cost function. */ + gradientArrayMap = new TensorArrayMap(); + private runtimeCache: {[key: string]: SessionRuntime} = {}; + /** Batch size of the previous train() call. */ + private prevBatchSize: number; + private batchSizeScalar: Scalar; + private oneScalar = Scalar.new(1); +} + +/** @hidden */ +export type SessionRuntime = { + nodes: Node[]; operations: Operation[]; +}; diff --git a/src/session_test.ts b/src/session_test.ts new file mode 100644 index 0000000000..1597478803 --- /dev/null +++ b/src/session_test.ts @@ -0,0 +1,314 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Graph, Tensor} from './graph'; +import {InputProvider} from './input_provider'; +import {NDArrayMathCPU} from './math/math_cpu'; +import {NDArrayMathGPU} from './math/math_gpu'; +import {Array1D, NDArray, Scalar} from './math/ndarray'; +import {MeanSquaredCost} from './ops/element_wise_cost'; +import {FeedDictionary, FeedEntry, Session} from './session'; +import {SGDOptimizer} from './sgd_optimizer'; +import * as test_util from './test_util'; + + +describe('FeedDictionary', () => { + it('ctor leaves dict empty if no args are passed', () => { + expect(Object.keys(new FeedDictionary().dict).length).toEqual(0); + }); + + it('ctor populates dict from only feed entry', () => { + const e: FeedEntry = {tensor: new Tensor([]), data: NDArray.zeros([1])}; + const d = new FeedDictionary([e]); + expect(Object.keys(d.dict).length).toEqual(1); + expect(d.dict[e.tensor.id]).toBe(e); + }); + + it('ctor populates dict from many entries', () => { + const entries: FeedEntry[] = [ + {tensor: new Tensor([]), data: NDArray.zeros([1])}, + {tensor: new Tensor([]), data: NDArray.zeros([1])}, + {tensor: new Tensor([]), data: NDArray.zeros([1])}, + {tensor: new Tensor([]), data: NDArray.zeros([1])} + ]; + const d = new FeedDictionary(entries); + expect(Object.keys(d.dict).length).toEqual(entries.length); + entries.forEach(entry => expect(d.dict[entry.tensor.id]).toBe(entry)); + }); + + it('add adds entry to map keyed on tensor id', () => { + const t = new Tensor([]); + const nda = NDArray.zeros([1]); + const fd = new FeedDictionary([{tensor: t, data: nda}]); + expect(fd.dict[t.id].tensor).toBe(t); + expect(fd.dict[t.id].data).toBe(nda); + }); +}); + +describe('Session', () => { + let g: Graph; + + beforeEach(() => g = new Graph()); + + it('mnist fc', () => { + const input = g.placeholder('input', [28 * 28]); + const fc0W = g.variable('fc0W', NDArray.zeros([32, 28 * 28])); + const fc0B = g.variable('fc0B', NDArray.zeros([32])); + const fc0 = g.add(g.matmul(fc0W, input), fc0B); + const relu0 = g.relu(fc0); + const fc1W = g.variable('fc1W', NDArray.zeros([32, 32])); + const fc1B = g.variable('fc1B', NDArray.zeros([32])); + const fc1 = g.add(g.matmul(fc1W, relu0), fc1B); + const relu1 = g.relu(fc1); + const fc2W = g.variable('fc2W', NDArray.zeros([32, 32])); + const fc2B = g.variable('fc2B', NDArray.zeros([32])); + const fc2 = g.add(g.matmul(fc2W, relu1), fc2B); + const relu2 = g.relu(fc2); + const fc3W = g.variable('fc3W', NDArray.zeros([10, 32])); + const fc3B = g.variable('fc3B', NDArray.zeros([10])); + const fc3 = g.add(g.matmul(fc3W, relu2), fc3B); + + const session = new Session(g, new NDArrayMathCPU()); + session.eval(fc3, [{tensor: input, data: NDArray.zeros([28 * 28])}]); + }); + + it('y=x^2 + 3: CPU', () => { + const x = g.placeholder('x', [2]); + const y = g.add(g.square(x), g.constant(3)); + const session = new Session(g, new NDArrayMathCPU()); + const yVal = session.eval(y, [{tensor: x, data: Array1D.new([5, 4])}]); + const expected = new Float32Array([28, 19]); + test_util.expectArraysClose(yVal.getValues(), expected, 1e-5); + }); + + it('y=x^2 + 3: GPU', () => { + const x = g.placeholder('x', [2]); + const y = g.add(g.square(x), g.constant(3)); + const math = new NDArrayMathGPU(); + const session = new Session(g, math); + + math.scope(() => { + const yVal = session.eval(y, [{tensor: x, data: Array1D.new([5, 4])}]); + const expected = new Float32Array([28, 19]); + test_util.expectArraysClose(yVal.getValues(), expected, 1e-5); + }); + }); + + it('Non-placeholder feed: y=x^2 + 3 (feed x^2)', () => { + const x = g.placeholder('x', [2]); + const xSquared = g.square(x); + const y = g.add(xSquared, g.constant(3)); + const math = new NDArrayMathGPU(); + const session = new Session(g, math); + + math.scope(() => { + const yVal = + session.eval(y, [{tensor: xSquared, data: Array1D.new([25, 16])}]); + const expected = new Float32Array([28, 19]); + test_util.expectArraysClose(yVal.getValues(), expected, 1e-5); + }); + }); + + it('Eval multiple tensors that share graph: y=x^2 + 3, z=x^2 + 2', () => { + const x = g.placeholder('x', [2]); + const xSquared = g.square(x); + const y = g.add(xSquared, g.constant(3)); + const z = g.add(xSquared, g.constant(2)); + const math = new NDArrayMathGPU(); + const session = new Session(g, math); + + math.scope(() => { + const result = + session.evalAll([y, z], [{tensor: x, data: Array1D.new([5, 4])}]); + const expectedY = new Float32Array([28, 19]); + const expectedZ = new Float32Array([27, 18]); + test_util.expectArraysClose(result[0].getValues(), expectedY, 1e-5); + test_util.expectArraysClose(result[1].getValues(), expectedZ, 1e-5); + }); + }); + + it('Backprop through a split node, input is scalar', () => { + const x = g.placeholder('x', []); + const y = g.square(x); + const z = g.add(x, g.constant(3)); + const w = g.add(y, z); + + const optimizer = new SGDOptimizer(0.1); + const session = new Session(g, new NDArrayMathCPU()); + let idx = 0; + const xs: Scalar[] = [Scalar.TWO, Scalar.ONE, Scalar.NEG_ONE]; + const inputProvider: InputProvider = { + getNextCopy() { + return xs[idx++]; + }, + disposeCopy(math, example) {} + }; + + // w = x^2 + x + 3 + // dw/dx = 2x + 1 + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer); + let dwdx = session.gradientArrayMap.get(x).get(); + expect(dwdx).toBe(5); + + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer); + dwdx = session.gradientArrayMap.get(x).get(); + expect(dwdx).toBe(3); + + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer); + dwdx = session.gradientArrayMap.get(x).get(); + expect(dwdx).toBe(-1); + }); + + it('Backprop through a split node, input is Array1D', () => { + const x = g.placeholder('x', [2]); + const y = g.square(x); + const z = g.add(x, g.constant(3)); + const w = g.reduceSum(g.add(y, z)); + + const optimizer = new SGDOptimizer(0.1); + const session = new Session(g, new NDArrayMathCPU()); + const inputProvider: InputProvider = { + getNextCopy() { + return Array1D.new([2, 4]); + }, + disposeCopy(math, example) {} + }; + + // w = reduce_sum(x^2 + x + 3) + // dw/dx = [2*x_1 + 1, 2*x_2 + 1] + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer); + const dwdx = session.gradientArrayMap.get(x).getValues(); + test_util.expectArraysClose(dwdx, new Float32Array([5, 9]), 1e-5); + }); + + it('Specify which variables to update (var_list)', () => { + const x = g.placeholder('x', [2]); + const b0 = g.variable('b0', NDArray.zeros([2])); + const p = g.add(x, b0); + const q = g.square(p); + const b1 = g.variable('b1', NDArray.zeros([2])); + const r = g.add(q, b1); + const yPrediction = g.reduceSum(r); + const yTrue = g.constant(1); + const cost = g.meanSquaredCost(yTrue, yPrediction); + + const session = new Session(g, new NDArrayMathCPU()); + const inputProvider: InputProvider = { + getNextCopy() { + return Array1D.new([1, 2]); + }, + disposeCopy(math, example) {} + }; + + // prediction = reduce_sum((x + b0)^2 + b1) + // dE/db0 = (1 - prediction) * [- 2*x_1 - 2*b0_1, - 2*x_2 - 2*b0_2] + // dE/db0_{x=[1,2], b0=[0,0]} = (8, 16) + + // Update only b0 + const optimizerOnlyB0 = new SGDOptimizer(0.1, [b0.node]); + session.train( + cost, [{tensor: x, data: inputProvider}], 2, optimizerOnlyB0, + undefined); + const b0After1 = session.activationArrayMap.get(b0).getValues(); + const b1After1 = session.activationArrayMap.get(b1).getValues(); + + test_util.expectArraysClose(b0After1, new Float32Array([-0.8, -1.6]), 1e-5); + test_util.expectArraysClose(b1After1, new Float32Array([0, 0]), 1e-5); + + // Update both b0 and b1 + const optimizerAll = new SGDOptimizer(0.1); + session.train( + cost, [{tensor: x, data: inputProvider}], 2, optimizerAll, undefined); + const b0After2 = session.activationArrayMap.get(b0).getValues(); + const b1After2 = session.activationArrayMap.get(b1).getValues(); + + expect(b0After2 === b0After1).toEqual(false); + expect(b1After2 === b1After1).toEqual(false); + }); + + it('Safe mode math, no math scope eval throws', () => { + const safeMode = true; + const x = g.placeholder('x', [2]); + const y = g.square(x); + const math = new NDArrayMathCPU(safeMode); + const session = new Session(g, math); + + expect(() => session.eval(y, [{tensor: x, data: Array1D.new([5, 4])}])) + .toThrowError(); + }); + + it('Safe mode math, math scope eval does not throw', () => { + const safeMode = true; + const x = g.placeholder('x', [2]); + const y = g.square(x); + const math = new NDArrayMathCPU(safeMode); + const session = new Session(g, math); + + math.scope(() => { + const yVal = session.eval(y, [{tensor: x, data: Array1D.new([5, 4])}]); + const expected = new Float32Array([25, 16]); + test_util.expectArraysClose(yVal.getValues(), expected, 1e-5); + }); + }); + + it('Safe mode math, math scope train does not throw', () => { + const x = g.placeholder('x', [2]); + const y = g.square(x); + const z = g.add(x, g.constant(3)); + const w = g.reduceSum(g.add(y, z)); + + const safeMode = true; + const optimizer = new SGDOptimizer(0.1); + const math = new NDArrayMathCPU(safeMode); + const session = new Session(g, math); + const inputProvider: InputProvider = { + getNextCopy() { + return Array1D.new([2, 4]); + }, + disposeCopy(math, example) {} + }; + + math.scope(() => { + // w = reduce_sum(x^2 + x + 3) + // dw/dx = [2*x_1 + 1, 2*x_2 + 1] + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer); + const dwdx = session.gradientArrayMap.get(x).getValues(); + test_util.expectArraysClose(dwdx, new Float32Array([5, 9]), 1e-5); + }); + }); + + it('Safe mode math, no math scope train throws', () => { + const x = g.placeholder('x', [2]); + const y = g.square(x); + const z = g.add(x, g.constant(3)); + const w = g.reduceSum(g.add(y, z)); + + const safeMode = true; + const optimizer = new SGDOptimizer(0.1); + const math = new NDArrayMathCPU(safeMode); + const session = new Session(g, math); + const inputProvider: InputProvider = { + getNextCopy() { + return Array1D.new([2, 4]); + }, + disposeCopy(math, example) {} + }; + + expect( + () => + session.train(w, [{tensor: x, data: inputProvider}], 1, optimizer)) + .toThrowError(); + }); +}); \ No newline at end of file diff --git a/src/session_util.ts b/src/session_util.ts new file mode 100644 index 0000000000..9ef12b3c53 --- /dev/null +++ b/src/session_util.ts @@ -0,0 +1,304 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {ConstantNode, Node, PlaceholderNode, SplitNode, Tensor, VariableNode} from './graph'; +import * as graph_util from './graph_util'; +import {InputProvider} from './input_provider'; +import {NDArrayMath} from './math/math'; +import {NDArray} from './math/ndarray'; +import {Operation} from './ops/op'; +import {FeedDictionary} from './session'; +import {TensorArrayMap} from './tensor_array_map'; +import * as util from './util'; + +/** + * Creates an array of graph nodes that stop traversal, based on the contents + * of the provided FeedDictionary. This is a simple 1:1 extraction of nodes from + * the FeedDictionary. + * + * @hidden + * @param feedDictionary The FeedDictionary to scan for termination nodes. + * @return an array of Nodes which halt traversal when visited. + */ +export function getTerminatingNodesFromFeedDictionary( + feedDictionary: FeedDictionary): Node[] { + return Object.keys(feedDictionary.dict) + .map(tensorID => feedDictionary.dict[+tensorID].tensor.node); +} + +/** + * Given a tensor and a feed dictionary, computes the set of nodes that need to + * be evaluated to perform inference. + * + * @hidden + * @param evalTensors The list of tensors to eventually be evaluated. + * @param feedDictionary The populated feed dictionary. + * @return The set of nodes to evaluate, in evaluation order. + */ +export function getOrderedEvaluationSetFromEvalTensor( + evalTensors: Tensor[], feedDictionary: FeedDictionary): Node[] { + const terminatingNodes = + getTerminatingNodesFromFeedDictionary(feedDictionary); + const evalNodes = evalTensors.map(x => x.node); + const unorderedEvaluationSet = + graph_util.getUnorderedEvaluationSet(evalNodes, terminatingNodes); + const orderedEvaluationSet = + graph_util.getOrderedEvaluationSet(unorderedEvaluationSet); + return orderedEvaluationSet; +} + +/** + * Traverses the provided node array and adds all persistent node NDArrays to + * the provided TensorArrayMap. + * + * @hidden + * @param evaluationSet The array of nodes to scan. + * @param tensorArrayMap The map that receives the NDArrays from persistent + * nodes. + */ +export function addPersistentArraysToTensorArrayMap( + evaluationSet: Node[], tensorArrayMap: TensorArrayMap) { + evaluationSet.forEach(node => { + if (node instanceof VariableNode || node instanceof ConstantNode) { + tensorArrayMap.set(node.output, node.data); + } + }); +} + +/** + * @hidden + */ +export function getVariableNodesFromEvaluationSet(evaluationSet: Node[]): + VariableNode[] { + const nodes: VariableNode[] = []; + evaluationSet.forEach(node => { + if (node instanceof VariableNode) { + nodes.push(node); + } + }); + return nodes; +} + +/** + * @hidden + */ +export function throwIfFeedDictionaryContainsNDArrays( + feedDictionary: FeedDictionary) { + Object.keys(feedDictionary.dict).forEach(tensorID => { + if (feedDictionary.dict[+tensorID].data instanceof NDArray) { + throw new Error( + 'training requires FeedDictionary entries to be InputProviders' + + 'and not NDArrays.'); + } + }); +} + +/** + * @hidden + */ +export function loadInputsFromFeedDictionaryToTensorArrayMap( + batchFeed: FeedDictionary, activations: TensorArrayMap, math: NDArrayMath) { + Object.keys(batchFeed.dict).forEach(tensorID => { + const feedEntry = batchFeed.dict[+tensorID]; + + let data: NDArray; + if (feedEntry.data instanceof NDArray) { + data = feedEntry.data as NDArray; + } else { + const provider = feedEntry.data as InputProvider; + data = provider.getNextCopy(math); + } + + util.assert( + util.arraysEqual(feedEntry.tensor.shape, data.shape), + `Error loading FeedEntry: feeding NDArray of shape ${data.shape} ` + + `does not match Tensor (id: ${feedEntry.tensor.id}) shape: ` + + `${feedEntry.tensor.shape}.`); + activations.set(feedEntry.tensor, data); + }); +} + + +/** + * @hidden + */ +export function releaseFeedDictionaryInputsFromTensorArrayMap( + batchFeed: FeedDictionary, activations: TensorArrayMap, math: NDArrayMath) { + Object.keys(batchFeed.dict).forEach(tensorID => { + const feedEntry = batchFeed.dict[+tensorID]; + + if (!(feedEntry.data instanceof NDArray)) { + const provider = feedEntry.data as InputProvider; + + const feedEntryArray = activations.get(feedEntry.tensor); + provider.disposeCopy(math, feedEntryArray); + } + + activations.delete(feedEntry.tensor); + }); +} + +/** + * Removes all nodes from the provided Node array whose output tensors exist in + * the provided feed dictionary. After calling this, the Node array should + * contain zero Placeholder nodes, or the user has failed to provide a feed for + * a Placeholder node. + * + * @hidden + * @param feedDictionary The FeedDictionary to process. + * @param evaluationSet The array of nodes to remove input nodes from. + */ +export function removeFeedDictionaryNodesFromEvaluationSet( + feedDictionary: FeedDictionary, evaluationSet: Node[]) { + let i = 0; + while (i < evaluationSet.length) { + const node = evaluationSet[i]; + if (feedDictionary.dict[node.output.id] != null) { + evaluationSet.splice(i, 1); + } else { + ++i; + } + } +} + +/** + * Disposes any NDArrays on the tensorArrayMap from operation outputs and sets + * the value to null. + * + * @hidden + * @param evaluationSet The set of nodes to be evaluated. + * @param tensorArrayMap The map to dispose and initialize. + */ +export function disposeAndInitializeOperationOutputs( + evaluationSet: Node[], tensorArrayMap: TensorArrayMap) { + evaluationSet.forEach(node => { + if (!graph_util.isInputNode(node)) { + if (!graph_util.isPassthroughNode(node, tensorArrayMap)) { + tensorArrayMap.disposeArray(node.output); + } + tensorArrayMap.set(node.output, null); + } + }); +} + +/** + * Disposes any NDArrays on the tensorArrayMap from derivatives of operation + * inputs and sets the value to null. + * + * @hidden + * @param evaluationSet The set of nodes to be evaluated. + * @param gradients The gradient map to dispose and initialize. + */ +export function disposeAndInitializeOperationInputGradients( + evaluationSet: Node[], gradients: TensorArrayMap) { + evaluationSet.forEach(node => { + Object.keys(node.inputs).forEach(inputName => { + const input = node.inputs[inputName]; + if (gradients.get(input, true) !== gradients.get(node.output, true)) { + gradients.disposeArray(input); + } + gradients.set(input, null); + }); + }); +} + + +/** + * Calls underlying operation disposeTransientArrays methods which clean up any + * NDArrays which operations may have created during a run. + * + * @hidden + * @param operationNodes The array of Nodes to traverse. + * @param outputTensor The tensor being evaluated. + * @param map The TensorArrayMap to operate on. + */ +export function disposeTransientOperationArrays( + operations: Operation[], activations: TensorArrayMap, + gradients: TensorArrayMap) { + operations.forEach(op => op.disposeTransientArrays(activations, gradients)); +} + +/** + * Iterates the provided Node array and throws an exception if there are any + * Placeholder nodes present. Call after the evaluation set has been pruned with + * the accompanying FeedDictionary to ensure that all inputs have been resolved. + * + * @hidden + * @param evaluationSet The array of nodes to scan. + */ +export function throwErrorIfEvaluationSetContainsPlaceholderNodes( + evaluationSet: Node[]) { + evaluationSet.forEach(node => { + if (node instanceof PlaceholderNode) { + const shape = '[' + node.output.shape.join(', ') + ']'; + throw new Error( + 'Placeholder node "' + node.name + '" ' + shape + + ' not present in feed dictionary.'); + } + }); +} + +/** + * Injects splits nodes after every node that has multiple consumers. + * + * @hidden + * @param nodes The node list in evaluation order. + * @return The node list with split nodes injected. + */ +export function addSplitNodes(nodes: Node[]): Node[] { + const nodeIdToNumConsumers: number[] = []; + const nodeIdToSplitNode: {[nodeId: number]: SplitNode} = {}; + + // Find nodes that have multiple consumers. + nodes.forEach(node => { + const keys = Object.keys(node.inputs); + keys.forEach(key => { + const inputTensor = node.inputs[key]; + const input = inputTensor.node; + if (nodeIdToNumConsumers[input.id] == null) { + nodeIdToNumConsumers[input.id] = 0; + } + nodeIdToNumConsumers[input.id]++; + if (nodeIdToNumConsumers[input.id] > 1 && + nodeIdToSplitNode[input.id] == null) { + nodeIdToSplitNode[input.id] = new SplitNode(input.graph, inputTensor); + } + }); + }); + + // Inject a split node after each node that has multiple consumers and + // rewire the inputs of the consumers to consume the output tensors of the + // split node instead of the original node. Each consumer consumes a + // different output tensor so that derivatives are not overwritten. + // x-->y becomes x-->s-->y where y consumes the 1st output tensor of s + // |-->z |-->z and z consumes the 2nd output tensor of s + const newNodes: Node[] = []; + nodes.forEach(node => { + newNodes.push(node); + if (node.id in nodeIdToSplitNode) { + const splitNode = nodeIdToSplitNode[node.id]; + newNodes.push(splitNode); + } + const keys = Object.keys(node.inputs); + keys.forEach(key => { + const inputTensor = node.inputs[key]; + const inputId = inputTensor.node.id; + if (inputId in nodeIdToSplitNode) { + node.inputs[key] = nodeIdToSplitNode[inputId].getNewOutputTensor(); + } + }); + }); + return newNodes; +} diff --git a/src/session_util_test.ts b/src/session_util_test.ts new file mode 100644 index 0000000000..1c36f9b80f --- /dev/null +++ b/src/session_util_test.ts @@ -0,0 +1,464 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {ConstantNode, Graph, Node, PlaceholderNode, SplitNode, Tensor, VariableNode} from './graph'; +import {InputProvider} from './input_provider'; +import {NDArrayMathCPU} from './math/math_cpu'; +import {NDArray} from './math/ndarray'; +import {FeedDictionary, FeedEntry} from './session'; +import * as session_util from './session_util'; +import {TensorArrayMap} from './tensor_array_map'; + +class TestNode extends Node { + validate() {} +} + +describe('getTerminatingNodesFromFeedDictionary', () => { + + it('returns an empty node array from an empty FeedDictionary', () => { + expect(session_util.getTerminatingNodesFromFeedDictionary( + new FeedDictionary())) + .toEqual([]); + }); + + it('returns the only node in the feed dictionary', () => { + const node = new TestNode(new Graph(), '', {}, new Tensor([])); + const fd = + new FeedDictionary([{tensor: node.output, data: NDArray.zeros([1])}]); + expect(session_util.getTerminatingNodesFromFeedDictionary(fd)).toEqual([ + node + ]); + }); + + it('returns every node from the feed dictionary', () => { + const n0 = new TestNode(new Graph(), '', {}, new Tensor([])); + const n1 = new TestNode(new Graph(), '', {}, new Tensor([])); + const n2 = new TestNode(new Graph(), '', {}, new Tensor([])); + const n3 = new TestNode(new Graph(), '', {}, new Tensor([])); + const n4 = new TestNode(new Graph(), '', {}, new Tensor([])); + const feeds: FeedEntry[] = [ + {tensor: n0.output, data: NDArray.zeros([1])}, + {tensor: n1.output, data: NDArray.zeros([1])}, + {tensor: n2.output, data: NDArray.zeros([1])}, + {tensor: n3.output, data: NDArray.zeros([1])}, + {tensor: n4.output, data: NDArray.zeros([1])} + ]; + const fd = new FeedDictionary(feeds); + const nodes = session_util.getTerminatingNodesFromFeedDictionary(fd); + expect(nodes).toContain(n0); + expect(nodes).toContain(n1); + expect(nodes).toContain(n2); + expect(nodes).toContain(n3); + expect(nodes).toContain(n4); + }); +}); + +describe('addPersistentArraysToTensorArrayMap', () => { + let map: TensorArrayMap; + let g: Graph; + beforeEach(() => { + map = new TensorArrayMap(); + g = new Graph(); + }); + + it('does nothing with empty evaluationSet', () => { + session_util.addPersistentArraysToTensorArrayMap([], map); + expect(map.size()).toEqual(0); + }); + + it('adds the only VariableNode to the map', () => { + const v = new VariableNode(g, '', NDArray.zeros([1])); + session_util.addPersistentArraysToTensorArrayMap([v], map); + expect(map.get(v.output)).toBe(v.data); + }); + + it('adds the only ConstantNode to the map', () => { + const c = new ConstantNode(g, NDArray.zeros([1])); + session_util.addPersistentArraysToTensorArrayMap([c], map); + expect(map.get(c.output)).toBe(c.data); + }); + + it('does nothing with nodes that aren\'t VariableNodes or ConstantNodes', + () => { + const nodes = [new TestNode(g, '', {}, new Tensor([]))]; + session_util.addPersistentArraysToTensorArrayMap(nodes, map); + expect(map.size()).toEqual(0); + }); + + it('adds multiple VariableNodes to the map', () => { + const nodes = [ + new VariableNode(g, '', NDArray.zeros([1])), + new VariableNode(g, '', NDArray.zeros([1])), + new VariableNode(g, '', NDArray.zeros([1])) + ]; + session_util.addPersistentArraysToTensorArrayMap(nodes, map); + expect(map.get(nodes[0].output)).toBe(nodes[0].data); + expect(map.get(nodes[1].output)).toBe(nodes[1].data); + expect(map.get(nodes[2].output)).toBe(nodes[2].data); + }); + + it('adds multiple ConstantNodes to the map', () => { + const nodes = [ + new ConstantNode(g, NDArray.zeros([1])), + new ConstantNode(g, NDArray.zeros([1])), + new ConstantNode(g, NDArray.zeros([1])) + ]; + session_util.addPersistentArraysToTensorArrayMap(nodes, map); + expect(map.get(nodes[0].output)).toBe(nodes[0].data); + expect(map.get(nodes[1].output)).toBe(nodes[1].data); + expect(map.get(nodes[2].output)).toBe(nodes[2].data); + }); + + it('skips non-VariableNode or ConstantNode entries in the set', () => { + const nodes: Node[] = [ + new TestNode(g, '', {}, new Tensor([])), + new VariableNode(g, '', NDArray.zeros([1])), + new TestNode(g, '', {}, new Tensor([])), + new ConstantNode(g, NDArray.zeros([1])), + new TestNode(g, '', {}, new Tensor([])), + new VariableNode(g, '', NDArray.zeros([1])) + ]; + session_util.addPersistentArraysToTensorArrayMap(nodes, map); + expect(map.size()).toEqual(3); + expect(map.get(nodes[1].output)).toBe((nodes[1] as VariableNode).data); + expect(map.get(nodes[3].output)).toBe((nodes[3] as ConstantNode).data); + expect(map.get(nodes[5].output)).toBe((nodes[5] as VariableNode).data); + }); +}); + +describe('loadInputsFromFeedDictionaryToTensorArrayMap', () => { + let map: TensorArrayMap; + let math: NDArrayMathCPU; + + beforeEach(() => { + math = new NDArrayMathCPU(); + map = new TensorArrayMap(); + }); + + it('does nothing with empty feed dictionary', () => { + const fd = new FeedDictionary(); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(0); + }); + + it('adds the only NDArray feed dict entry to the map', () => { + const tensor = new Tensor([1]); + const fd = new FeedDictionary([{tensor, data: NDArray.zeros([1])}]); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(1); + expect(map.get(tensor)).toBe(fd.dict[tensor.id].data as NDArray); + }); + + it('adds the only provider feed dict entry to the map', () => { + const tensor = new Tensor([2]); + const ndarray = NDArray.zeros([2]); + const provider: InputProvider = { + getNextCopy(): NDArray { + // Don't return a copy in this case so we can test that we returned the + // right value. + return ndarray; + }, + // No need to dispose when not using NDArrayMathGPU. + disposeCopy() {} + }; + const fd = new FeedDictionary([{tensor, data: provider}]); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(1); + expect(map.get(tensor)).toBe(ndarray); + }); + + it('adds every NDArray feed dict entry to the map', () => { + const tensors = [ + new Tensor([1]), new Tensor([1]), new Tensor([1]), new Tensor([1]), + new Tensor([1]) + ]; + const feeds = tensors.map(tensor => { + return {tensor, data: NDArray.zeros([1])}; + }); + const fd = new FeedDictionary(feeds); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(tensors.length); + tensors.forEach( + tensor => + expect(map.get(tensor)).toBe(fd.dict[tensor.id].data as NDArray)); + }); + + it('adds every provider feed dict entry to the map', () => { + const tensors = [ + new Tensor([1]), new Tensor([1]), new Tensor([1]), new Tensor([1]), + new Tensor([1]) + ]; + const ndarrays: NDArray[] = []; + for (let i = 0; i < tensors.length; i++) { + ndarrays.push(NDArray.zeros([1])); + } + let idx = 0; + const provider: InputProvider = { + getNextCopy(): NDArray { + const ndarray = ndarrays[idx]; + idx++; + return ndarray; + }, + disposeCopy() {} + }; + + const feeds: FeedEntry[] = []; + for (let i = 0; i < tensors.length; i++) { + feeds.push({tensor: tensors[i], data: provider}); + } + + const fd = new FeedDictionary(feeds); + session_util.loadInputsFromFeedDictionaryToTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(tensors.length); + for (let i = 0; i < tensors.length; i++) { + expect(map.get(tensors[i])).toBe(ndarrays[i]); + } + }); + + it('throws when provides data that does not match tensor shape', () => { + const tensor = new Tensor([4, 5]); + const fd = new FeedDictionary([{tensor, data: NDArray.zeros([2, 3])}]); + expect( + () => session_util.loadInputsFromFeedDictionaryToTensorArrayMap( + fd, map, math)) + .toThrowError(); + }); +}); + +describe('releaseFeedDictionaryInputsFromTensorArrayMap', () => { + let map: TensorArrayMap; + let math: NDArrayMathCPU; + + beforeEach(() => { + map = new TensorArrayMap(); + math = new NDArrayMathCPU(); + }); + + it('doesn\'t remove anything when feed dictionary is empty', () => { + map.set(new Tensor([]), null); + const fd = new FeedDictionary(); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(1); + }); + + it('doesn\'t remove tensors from map that don\'t exist in feed', () => { + const fdTensor = new Tensor([]); + const nda = NDArray.zeros([1]); + const fd = + new FeedDictionary([{tensor: fdTensor, data: NDArray.zeros([1])}]); + const nonFDTensor = new Tensor([]); + map.set(nonFDTensor, nda); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(1); + expect(map.get(nonFDTensor)).toBe(nda); + }); + + it('removes only tensor in map and feed dict', () => { + const tensor = new Tensor([]); + const ndarray = NDArray.zeros([1]); + const fd = new FeedDictionary([{tensor, data: ndarray}]); + map.set(tensor, ndarray); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(0); + }); + + it('removes from map all tensors in feed dict', () => { + const tensors = [new Tensor([]), new Tensor([]), new Tensor([])]; + + const feeds = tensors.map(tensor => { + return {tensor, data: NDArray.zeros([1])}; + }); + const fd = new FeedDictionary(feeds); + tensors.forEach( + tensor => map.set(tensor, fd.dict[tensor.id].data as NDArray)); + session_util.releaseFeedDictionaryInputsFromTensorArrayMap(fd, map, math); + expect(map.size()).toEqual(0); + }); +}); + +describe('disposeAndInitializeOperationOutputs', () => { + let map: TensorArrayMap; + let g: Graph; + beforeEach(() => { + map = new TensorArrayMap(); + g = new Graph(); + }); + + it('does nothing to map if set is empty', () => { + session_util.disposeAndInitializeOperationOutputs([], map); + expect(map.size()).toEqual(0); + }); + + it('does nothing to map if set has no input nodes', () => { + const nodes = [ + new VariableNode(g, '', NDArray.zeros([1])), + new PlaceholderNode(g, '', [1]) + ]; + session_util.disposeAndInitializeOperationOutputs(nodes, map); + expect(map.size()).toEqual(0); + }); + + it('adds output tensor from only operation node', () => { + const input = new Tensor([]); + const t = new Tensor([]); + session_util.disposeAndInitializeOperationOutputs( + [new TestNode(g, '', {'in': input}, t)], map); + expect(map.size()).toEqual(1); + expect(map.hasNullArray(t)).toEqual(true); + }); + + it('adds output tensors from all operation nodes', () => { + const input = new Tensor([]); + const tensors = [new Tensor([]), new Tensor([]), new Tensor([])]; + const nodes: Node[] = []; + tensors.forEach( + tensor => nodes.push(new TestNode(g, '', {'in': input}, tensor))); + session_util.disposeAndInitializeOperationOutputs(nodes, map); + expect(map.size()).toEqual(nodes.length); + tensors.forEach(tensor => expect(map.hasNullArray(tensor)).toEqual(true)); + }); +}); + +describe('removeFeedDictionaryNodesFromEvaluationSet', () => { + let set: Node[]; + + beforeEach(() => { + set = []; + }); + + it('does nothing when feed dictionary is empty', () => { + const node = new TestNode(new Graph(), '', {}, new Tensor([])); + set.push(node); + const fd = new FeedDictionary(); + session_util.removeFeedDictionaryNodesFromEvaluationSet(fd, set); + expect(set.length).toEqual(1); + expect(set[0]).toBe(node); + }); + + it('removes only feed dict node from set', () => { + set.push(new TestNode(new Graph(), '', {}, new Tensor([]))); + const fd = + new FeedDictionary([{tensor: set[0].output, data: NDArray.zeros([1])}]); + session_util.removeFeedDictionaryNodesFromEvaluationSet(fd, set); + expect(set.length).toEqual(0); + }); + + it('removes only feed dict nodes from set', () => { + const g = new Graph(); + const remainingNodes = [ + new TestNode(g, '', {}, new Tensor([])), + new TestNode(g, '', {}, new Tensor([])), + new TestNode(g, '', {}, new Tensor([])) + ]; + + set.push(remainingNodes[0]); + set.push(new TestNode(g, '', {}, new Tensor([]))); + const feeds: FeedEntry[] = []; + feeds.push({tensor: set[set.length - 1].output, data: NDArray.zeros([1])}); + set.push(remainingNodes[1]); + set.push(new TestNode(g, '', {}, new Tensor([]))); + feeds.push({tensor: set[set.length - 1].output, data: NDArray.zeros([1])}); + set.push(remainingNodes[2]); + + const fd = new FeedDictionary(feeds); + session_util.removeFeedDictionaryNodesFromEvaluationSet(fd, set); + expect(set).toEqual(remainingNodes); + }); +}); + +describe('throwErrorIfEvaluationSetContainsPlaceholderNodes', () => { + let g: Graph; + beforeEach(() => g = new Graph()); + + it('doesn\'t throw on an empty node array', () => { + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes([]); + }); + + it('doesn\'t throw if array contains non-placeholder nodes', () => { + session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes( + [new TestNode(g, '', {}, new Tensor([]))]); + }); + + it('throws if the array only contains a placeholder node', () => { + expect( + () => session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes( + [new PlaceholderNode(g, '', [])])) + .toThrowError(/Placeholder node/); + }); + + it('thrown error contains the tensor shape', () => { + expect( + () => session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes( + [new PlaceholderNode(g, '', [1, 2, 3, 4, 5])])) + .toThrowError(/[1, 2, 3, 4, 5]/); + }); + + it('throws if the non-first element in the array is a placeholder', () => { + expect( + () => session_util.throwErrorIfEvaluationSetContainsPlaceholderNodes([ + new TestNode(g, '', {}, new Tensor([])), + new PlaceholderNode(g, '', []) + ])) + .toThrowError(/Placeholder node/); + }); +}); + +describe('Add split nodes', () => { + let g: Graph; + let nodes: Node[]; + + beforeEach(() => { + g = new Graph(); + nodes = []; + }); + + it('does not add split nodes', () => { + const a = new TestNode(g, 'A', {}, new Tensor([])); + const b = new TestNode(g, 'B', {'a': a.output}, new Tensor([])); + nodes.push(a); + nodes.push(b); + const newNodes = session_util.addSplitNodes(nodes); + expect(newNodes.length).toBe(2); + }); + + it('does add split a node before A', () => { + const a = new TestNode(g, 'A', {}, new Tensor([])); + const b = new TestNode(g, 'B', {'a': a.output}, new Tensor([])); + const c = new TestNode(g, 'C', {'a': a.output}, new Tensor([])); + nodes.push(a); + nodes.push(b); + nodes.push(c); + const newNodes = session_util.addSplitNodes(nodes); + expect(newNodes.length).toBe(4); + }); + + it('adds a split node in the right location with correct input/output', + () => { + const a = new TestNode(g, 'A', {}, new Tensor([])); + const b = new TestNode(g, 'B', {'a': a.output}, new Tensor([])); + const c = new TestNode(g, 'C', {'a': a.output}, new Tensor([])); + nodes.push(a); + nodes.push(b); + nodes.push(c); + const newNodes = session_util.addSplitNodes(nodes); + expect(newNodes.length).toBe(4); + const splitNode = newNodes[1] as SplitNode; + expect(splitNode instanceof SplitNode); + expect(splitNode.inputs[SplitNode.X] === a.output); + expect(splitNode.outputs.length).toBe(2); + expect(b.inputs['a'].id in splitNode.outputs.map(o => o.id)); + expect(c.inputs['a'].id in splitNode.outputs.map(o => o.id)); + }); +}); diff --git a/src/sgd_optimizer.ts b/src/sgd_optimizer.ts new file mode 100644 index 0000000000..a98e42b7b1 --- /dev/null +++ b/src/sgd_optimizer.ts @@ -0,0 +1,93 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Node, Tensor, VariableNode} from './graph'; +import {NDArrayMath} from './math/math'; +import {NDArray, Scalar} from './math/ndarray'; +import {Optimizer} from './optimizer'; +import {SessionRuntime} from './session'; +import * as session_util from './session_util'; +import {TensorArrayMap} from './tensor_array_map'; + +export class SGDOptimizer extends Optimizer { + constructor(private learningRate: number, specifiedVariableList?: Node[]) { + super(specifiedVariableList); + } + + beforeBatch( + math: NDArrayMath, batchSize: number, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, gradientArrayMap: TensorArrayMap) { + this.variableNodes = this.specifiedVariableNodes == null ? + session_util.getVariableNodesFromEvaluationSet(runtime.nodes) : + this.specifiedVariableNodes; + if (batchSize !== this.prevBatchSize) { + this.prevBatchSize = batchSize; + this.c = Scalar.new(-this.learningRate / batchSize); + } + this.variableNodes.forEach( + node => this.variableGradients.set( + node.output, NDArray.zeros(node.output.shape))); + } + + afterExample( + math: NDArrayMath, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, gradientArrayMap: TensorArrayMap) { + math.scope((keep) => { + this.variableNodes!.forEach(node => { + const gradient = gradientArrayMap.get(node.output); + const accumulatedGradient = this.variableGradients.get(node.output); + this.variableGradients.set( + node.output, keep(math.add(gradient, accumulatedGradient))); + accumulatedGradient.dispose(); + }); + }); + } + + afterBatch( + math: NDArrayMath, batchSize: number, runtime: SessionRuntime, + activationArrayMap: TensorArrayMap, gradientArrayMap: TensorArrayMap) { + math.scope((keep) => { + this.variableNodes!.forEach(node => { + const oldVariable = activationArrayMap.get(node.output); + const gradient = this.variableGradients.get(node.output); + const variable = + math.scaledArrayAdd(this.c!, gradient, this.one!, oldVariable); + activationArrayMap.set(node.output, keep(variable)); + node.data = variable; + + oldVariable.dispose(); + }); + }); + + this.variableGradients.dispose(); + this.variableGradients = new TensorArrayMap(); + } + + dispose() { + if (this.c != null) { + this.c.dispose(); + } + this.one.dispose(); + } + + setLearningRate(learningRate: number) { + this.learningRate = learningRate; + } + + private variableGradients = new TensorArrayMap(); + private prevBatchSize: number; + private one = Scalar.new(1); + private c: Scalar; +} diff --git a/src/tensor_array_map.ts b/src/tensor_array_map.ts new file mode 100644 index 0000000000..057506f804 --- /dev/null +++ b/src/tensor_array_map.ts @@ -0,0 +1,107 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from './graph'; +import {NDArray} from './math/ndarray'; + +/** + * TensorArrayMap is an internal map from Tensor IDs to NDArrays. Since NDArrays + * can be backed by WebGL textures, the TensorArrayMap is only used inside of a + * Session. + */ +export class TensorArrayMap { + /** + * Add or replace an entry in the map. + * @param tensor The tensor key. + * @param array The NDArray value, can be null. + */ + set(tensor: Tensor, array: NDArray|null) { + this.dict[tensor.id] = array; + } + + /** + * Returns the NDArray associated with the provided tensor. Will throw an + * exception if the tensor is not a key in the map, or if the associated + * NDArray is null. + * @param tensor The tensor key. + * @param skipChecks False by default. If true will skip all checks. + * @return The NDArray associated with the tensor. + */ + get(tensor: Tensor, skipChecks = false): NDArray { + if (!skipChecks && this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + const nda = this.dict[tensor.id]; + if (!skipChecks && nda === null) { + throw new Error('tensor ' + tensor.id + ' has null array.'); + } + return nda!; + } + + /** + * Removes a tensor/NDArray pair from the map. + * @param tensor The tensor key. + */ + delete(tensor: Tensor) { + delete this.dict[tensor.id]; + } + + disposeArray(tensor: Tensor) { + if (this.dict[tensor.id] === undefined) { + return; + } + const nda = this.dict[tensor.id]; + if (nda === null) { + return; + } + nda.dispose(); + this.dict[tensor.id] = null; + } + + /** + * @return The number of tensor/NDArray pairs in the map. + */ + size(): number { + return Object.keys(this.dict).length; + } + + /** + * Iterate over all contained NDArray values and dispose them. + */ + dispose() { + Object.keys(this.dict).forEach(tensorID => { + const nda = this.dict[+tensorID]; + if (nda) { + nda.dispose(); + } + }); + this.dict = {}; + } + + /** + * Tests to see if a tensor has a null associated with it. Throws + * if the tensor is not a key in the map. + * @param tensor The tensor key. + * @return True if the associated NDArray is null, else False. + */ + hasNullArray(tensor: Tensor): boolean { + if (this.dict[tensor.id] === undefined) { + throw new Error('tensor ' + tensor.id + ' not in array map.'); + } + return this.dict[tensor.id] === null; + } + + private dict: {[tensorID: number]: NDArray | null} = {}; +} diff --git a/src/tensor_array_map_test.ts b/src/tensor_array_map_test.ts new file mode 100644 index 0000000000..b58d0cfdc4 --- /dev/null +++ b/src/tensor_array_map_test.ts @@ -0,0 +1,108 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import {Tensor} from './graph'; +import {NDArray} from './math/ndarray'; +import {TensorArrayMap} from './tensor_array_map'; + +describe('TensorArrayMap.size', () => { + it('is 0 at construction', () => { + expect((new TensorArrayMap()).size()).toEqual(0); + }); + + it('is 1 after add', () => { + const map = new TensorArrayMap(); + map.set(new Tensor([]), NDArray.zeros([1])); + expect(map.size()).toEqual(1); + }); + + it('increments for every add', () => { + const map = new TensorArrayMap(); + for (let i = 0; i < 9; ++i) { + map.set(new Tensor([]), NDArray.zeros([1])); + } + expect(map.size()).toEqual(9); + }); +}); + +describe('TensorArrayMap.hasNullArray', () => { + let map: TensorArrayMap; + let t: Tensor; + beforeEach(() => { + map = new TensorArrayMap(); + t = new Tensor([]); + }); + + it('returns true for null NDArray entries', () => { + map.set(t, null); + expect(map.hasNullArray(t)).toBe(true); + }); + + it('returns false for non-null NDArray entries', () => { + map.set(t, NDArray.zeros([1])); + expect(map.hasNullArray(t)).toBe(false); + }); + + it('throws for missing keys', () => { + expect(() => map.hasNullArray(t)).toThrowError(/not in array map/); + }); +}); + +describe('TensorArrayMap.get', () => { + let map: TensorArrayMap; + let t: Tensor; + beforeEach(() => { + map = new TensorArrayMap(); + t = new Tensor([]); + }); + + it('returns the associated NDArray', () => { + const nda = NDArray.zeros([1]); + map.set(t, nda); + expect(map.get(t)).toBe(nda); + }); + + it('throws if associated NDArray is null', () => { + map.set(t, null); + expect(() => map.get(t)).toThrowError(/has null array/); + }); + + it('throws for missing key', () => { + expect(() => map.get(t)).toThrowError(/not in array map/); + }); +}); + +describe('TensorArrayMap.delete', () => { + let map: TensorArrayMap; + let t: Tensor; + beforeEach(() => { + map = new TensorArrayMap(); + t = new Tensor([]); + }); + + it('deletes the key from the map', () => { + map.set(t, null); + map.delete(t); + expect(() => map.get(t)).toThrow(); + }); + + it('is tolerant of deleting nonexistent keys', () => { + map.set(t, null); + map.delete(t); + map.delete(t); + map.delete(t); + map.delete(t); + }); +}); diff --git a/src/test_util.ts b/src/test_util.ts new file mode 100644 index 0000000000..d7682df6e8 --- /dev/null +++ b/src/test_util.ts @@ -0,0 +1,92 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export function expectArraysClose( + actual: Float32Array, expected: Float32Array, epsilon: number) { + if (actual.length !== expected.length) { + throw new Error( + 'Matrices have different lengths (' + actual.length + ' vs ' + + expected.length + ').'); + } + for (let i = 0; i < expected.length; ++i) { + const a = actual[i]; + const e = expected[i]; + if (isNaN(a) && isNaN(e)) { + continue; + } + if (isNaN(a) || isNaN(e) || Math.abs(a - e) > epsilon) { + const actualStr = 'actual[' + i + '] === ' + a; + const expectedStr = 'expected[' + i + '] === ' + e; + throw new Error('Arrays differ: ' + actualStr + ', ' + expectedStr); + } + } +} + +export function randomArrayInRange( + n: number, minValue: number, maxValue: number): Float32Array { + const v = new Float32Array(n); + const range = maxValue - minValue; + for (let i = 0; i < n; ++i) { + v[i] = (Math.random() * range) + minValue; + } + return v; +} + +export function makeIdentity(n: number): Float32Array { + const i = new Float32Array(n * n); + for (let j = 0; j < n; ++j) { + i[(j * n) + j] = 1; + } + return i; +} + +export function setValue( + m: Float32Array, mNumRows: number, mNumCols: number, v: number, row: number, + column: number) { + if (row >= mNumRows) { + throw new Error('row (' + row + ') must be in [0 ' + mNumRows + '].'); + } + if (column >= mNumCols) { + throw new Error('column (' + column + ') must be in [0 ' + mNumCols + '].'); + } + m[(row * mNumCols) + column] = v; +} + +export function cpuMultiplyMatrix( + a: Float32Array, aRow: number, aCol: number, b: Float32Array, bRow: number, + bCol: number) { + const result = new Float32Array(aRow * bCol); + for (let r = 0; r < aRow; ++r) { + for (let c = 0; c < bCol; ++c) { + let d = 0; + for (let k = 0; k < aCol; ++k) { + d += a[(r * aCol) + k] * b[(k * bCol) + c]; + } + result[(r * bCol) + c] = d; + } + } + return result; +} + +export function cpuDotProduct(a: Float32Array, b: Float32Array): number { + if (a.length !== b.length) { + throw new Error('cpuDotProduct: incompatible vectors.'); + } + let d = 0; + for (let i = 0; i < a.length; ++i) { + d += a[i] * b[i]; + } + return d; +} diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000000..a130c1524c --- /dev/null +++ b/src/util.ts @@ -0,0 +1,182 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +export type Vector = number[] | Float64Array | Float32Array | Int32Array | + Int8Array | Int16Array; + +/** Shuffles the array using Fisher-Yates algorithm. */ +// tslint:disable-next-line:no-any +export function shuffle(array: any[]|Uint32Array|Int32Array| + Float32Array): void { + let counter = array.length; + let temp = 0; + let index = 0; + // While there are elements in the array + while (counter > 0) { + // Pick a random index + index = (Math.random() * counter) | 0; + // Decrease counter by 1 + counter--; + // And swap the last element with it + temp = array[counter]; + array[counter] = array[index]; + array[index] = temp; + } +} + +/** Clamps a value to a specified range. */ +export function clamp(min: number, x: number, max: number): number { + return Math.max(min, Math.min(x, max)); +} + +/** Returns a sample from a uniform [a, b] distribution. */ +export function randUniform(a: number, b: number) { + return Math.random() * (b - a) + a; +} + +/** + * Samples from a gaussian distribution. + * + * @param mean The mean. Default is 0. + * @param stdDev The standard deviation. Default is 1. + */ +export function randGauss(mean = 0, stdDev = 1, truncated = false): number { + let v1: number, v2: number, s: number; + do { + v1 = 2 * Math.random() - 1; + v2 = 2 * Math.random() - 1; + s = v1 * v1 + v2 * v2; + } while (s > 1); + + const result = Math.sqrt(-2 * Math.log(s) / s) * v1; + if (truncated && result > 2) { + return randGauss(mean, stdDev, true); + } + return mean + stdDev * result; +} + +/** Returns squared eucledian distance between two vectors. */ +export function distSquared(a: Vector, b: Vector): number { + let result = 0; + for (let i = 0; i < a.length; i++) { + const diff = a[i] - b[i]; + result += diff * diff; + } + return result; +} + +export function assert(expr: boolean, msg: string) { + if (!expr) { + throw new Error(msg); + } +} + +export function assertShapesMatch( + shapeA: number[], shapeB: number[], errorMessagePrefix = ''): void { + assert( + arraysEqual(shapeA, shapeB), + errorMessagePrefix + `Shapes ${shapeA} and ${shapeB} must match`); +} + +// tslint:disable-next-line:no-any +export function flatten(arr: any[], ret?: number[]): number[] { + ret = (ret === undefined ? [] : ret); + for (let i = 0; i < arr.length; ++i) { + if (Array.isArray(arr[i])) { + flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +} + +export type ArrayData = number|number[]|number[][]|number[][][]|number[][][][]; + +export function inferShape(arr: ArrayData): number[] { + const shape: number[] = []; + while (arr instanceof Array) { + shape.push(arr.length); + arr = arr[0]; + } + return shape; +} + +export function sizeFromShape(shape: number[]): number { + if (shape.length === 0) { + // Scalar. + return 1; + } + let size = shape[0]; + for (let i = 1; i < shape.length; i++) { + size *= shape[i]; + } + return size; +} + +export function isScalarShape(shape: number[]): boolean { + return shape.length === 0; +} + +// tslint:disable-next-line:no-any +export function arraysEqual(n1: any[]|Float32Array, n2: any[]|Float32Array) { + if (n1.length !== n2.length) { + return false; + } + for (let i = 0; i < n1.length; i++) { + if (n1[i] !== n2[i]) { + return false; + } + } + return true; +} + +export function isInt(a: number): boolean { + return a % 1 === 0; +} + +export function tanh(x: number): number { + // tslint:disable-next-line:no-any + if ((Math as any).tanh != null) { + // tslint:disable-next-line:no-any + return (Math as any).tanh(x); + } + if (x === Infinity) { + return 1; + } else if (x === -Infinity) { + return -1; + } else { + const e2x = Math.exp(2 * x); + return (e2x - 1) / (e2x + 1); + } +} + +export function sizeToSquarishShape(size: number): [number, number] { + for (let a = Math.floor(Math.sqrt(size)); a > 1; --a) { + if (size % a === 0) { + return [a, size / a]; + } + } + return [1, size]; +} + +export function createShuffledIndices(n: number): Uint32Array { + const shuffledIndices = new Uint32Array(n); + for (let i = 0; i < n; ++i) { + shuffledIndices[i] = i; + } + shuffle(shuffledIndices); + return shuffledIndices; +} diff --git a/src/util_test.ts b/src/util_test.ts new file mode 100644 index 0000000000..77e713c7f4 --- /dev/null +++ b/src/util_test.ts @@ -0,0 +1,89 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +import * as util from './util'; + +describe('Util', () => { + + it('Flatten arrays', () => { + expect(util.flatten([[1, 2, 3], [4, 5, 6]])).toEqual([1, 2, 3, 4, 5, 6]); + expect(util.flatten([[[1, 2], [3, 4], [5, 6], [7, 8]]])).toEqual([ + 1, 2, 3, 4, 5, 6, 7, 8 + ]); + expect(util.flatten([1, 2, 3, 4, 5, 6])).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('Correctly gets size from shape', () => { + expect(util.sizeFromShape([1, 2, 3, 4])).toEqual(24); + }); + + it('Correctly identifies scalars', () => { + expect(util.isScalarShape([])).toBe(true); + expect(util.isScalarShape([1, 2])).toBe(false); + expect(util.isScalarShape([1])).toBe(false); + }); + + it('Number arrays equal', () => { + expect(util.arraysEqual([1, 2, 3, 6], [1, 2, 3, 6])).toBe(true); + expect(util.arraysEqual([1, 2], [1, 2, 3])).toBe(false); + expect(util.arraysEqual([1, 2, 5], [1, 2])).toBe(false); + }); + + it('Is integer', () => { + expect(util.isInt(0.5)).toBe(false); + expect(util.isInt(1)).toBe(true); + }); + + it('Size to squarish shape (perfect square)', () => { + expect(util.sizeToSquarishShape(9)).toEqual([3, 3]); + }); + + it('Size to squarish shape (prime number)', () => { + expect(util.sizeToSquarishShape(11)).toEqual([1, 11]); + }); + + it('Size to squarish shape (almost square)', () => { + expect(util.sizeToSquarishShape(35)).toEqual([5, 7]); + }); + + it('Size of 1 to squarish shape', () => { + expect(util.sizeToSquarishShape(1)).toEqual([1, 1]); + }); + + it('infer shape single number', () => { + expect(util.inferShape(4)).toEqual([]); + }); + + it('infer shape 1d array', () => { + expect(util.inferShape([1, 2, 5])).toEqual([3]); + }); + + it('infer shape 2d array', () => { + expect(util.inferShape([[1, 2, 5], [5, 4, 1]])).toEqual([2, 3]); + }); + + it('infer shape 3d array', () => { + const a = [[[1, 2], [2, 3], [5, 6]], [[5, 6], [4, 5], [1, 2]]]; + expect(util.inferShape(a)).toEqual([2, 3, 2]); + }); + + it('infer shape 4d array', () => { + const a = [ + [[[1], [2]], [[2], [3]], [[5], [6]]], + [[[5], [6]], [[4], [5]], [[1], [2]]] + ]; + expect(util.inferShape(a)).toEqual([2, 3, 2, 1]); + }); +}); diff --git a/tsconfig-doc.json b/tsconfig-doc.json new file mode 100644 index 0000000000..9f0896db5f --- /dev/null +++ b/tsconfig-doc.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "preserveConstEnums": true, + "sourceMap": true, + "target": "es5", + "lib": ["es2015", "dom"] + }, + "include": [ + "src/dataset.ts", + "src/math/conv_util.ts", + "src/math/webgl/gpgpu_util.ts", + "src/math/webgl/render_ndarray_gpu_util.ts", + "src/math/webgl/webgl_util.ts", + "src/util.ts", + "src/checkpoint_loader.ts", + "src/graph.ts", + "src/graph_runner.ts", + "src/initializers.ts", + "src/input_provider.ts", + "src/math/math.ts", + "src/math/math_cpu.ts", + "src/math/math_gpu.ts", + "src/math/ndarray.ts", + "src/math/webgl/gpgpu_context.ts", + "src/optimizer.ts", + "src/session.ts", + "src/sgd_optimizer.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..31144e8bb4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitAny": true, + "sourceMap": true, + "removeComments": true, + "preserveConstEnums": true, + "declaration": true, + "target": "es5", + "lib": ["es2015", "dom"], + "outDir": "./dist" + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000000..95dc400777 --- /dev/null +++ b/tslint.json @@ -0,0 +1,55 @@ +{ + "rules": { + "array-type": [true, "array-simple"], + "arrow-return-shorthand": true, + "ban": [true, + ["fit"], + ["fdescribe"], + ["xit"], + ["xdescribe"], + ["fitAsync"], + ["xitAsync"], + ["fitFakeAsync"], + ["xitFakeAsync"] + ], + "ban-types": [true, + ["Object", "Use {} instead."], + ["String", "Use 'string' instead."], + ["Number", "Use 'number' instead."], + ["Boolean", "Use 'boolean' instead."] + ], + "class-name": true, + "interface-name": [true, "never-prefix"], + "jsdoc-format": true, + "label-position": true, + "new-parens": true, + "no-angle-bracket-type-assertion": true, + "no-any": true, + "no-construct": true, + "no-debugger": true, + "no-default-export": true, + "no-inferrable-types": true, + "no-namespace": [true, "allow-declarations"], + "no-reference": true, + "no-require-imports": true, + "no-string-throw": true, + "no-unused-expression": true, + "no-unused-variable": false, + "no-var-keyword": true, + "object-literal-shorthand": true, + "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"], + "prefer-const": true, + "radix": true, + "semicolon": [true, "always", "ignore-bound-class-methods"], + "switch-default": true, + "triple-equals": [true, "allow-null-check"], + "use-isnan": true, + "variable-name": [ + true, + "check-format", + "ban-keywords", + "allow-leading-underscore", + "allow-trailing-underscore" + ] + } +}

n|B5D$9BuY1z7g)eN(09+zrk_FUrKoOMDXp|9g>LBdKJK`-E_)Iy7C#m;1`%%EocTu; z!JT{qZuhD(3;V0zvbfNRl4ftwY3R@~_as8Y^ zT(^8aJN+>T=L`gR=vM_MN1t#{GgW!*7ZdQ(-0^6V91gRp)9Lx8K5&;5oaI4+KT$^; z&n=yaN7j_G>^h+<@?s%mwh7t9;$1k`UlZeo%5$qa_lTeMUge{n*s}L?q;L*9Oj0X@ z*{CDIq;|p-U4%2!wVc&ZH)$=Vg`B3gUU}wo>;s6$j%HrE&0tWogSyHOg2{z$?$Z_} z{`{qRM8KJ`dE$D#XeJiNe`T&GSZ(y-E=g_^XpCt2EO2|2tVV|@MH?PGIw~uUs^ysOq zGb#mgGk%EMcSlz#-y6R6;f@?)&%_t!@-UVj>znnf^^(gwOE>^tsi z$HR-MaZu?Kj@}1zLBe4t_rzA4wbZ*)RQeV60kZ@CLaJeMGM&G;s5Nd$Oro)48YLorGsr z8Y&t(aAmGbFx0x1R0Bscul9wM`d7$$Zj2Y7k{--lYbL|zb}2ftx|uhdJ_@fXyWkqT zrKBHFOUI`h;MIl!%+@XfugrZ!9RZ{0eOfdBxaT_eX^!A(7%wafHl< zTFfsvT1ka7ZOG`h46l)rD|DL?&vy0j^;((G)?$ZxP9l6)Q%aeQ2AClm%fesOb0rfz zA<$G6>|U7S(#_+c-(m>XzwD=PJI&df4rx|4vX)6E%Hi_($rOTnP}8Q2SNX1iVeZ@6 zq;=a+_4HmL!#NjLx@?4V3u0*E`duvlz%_x_Q32sflb~HNGu?Q+fEkp;gKeQKo~=%x zuVce-#ph%6`DZ%XUHJg#?F=aFt|_^YBCk+;2+OpE-1fp&x<6$CoAEQ5X$V}>(nFHi z8n%!r2k&>#UZBs^txEB@&Td%qQpo3A5;$7Z+?o2(Skjyk3TqDxVMA6ArK{O#RMmcg z8~Qq)i_uSD8JE@A-2T6merO;r7~qQ2eB60o`$QNNm&8X-e?m|8T!9b#2}<5(iSeGh z!L>J@#D;22d4(E_6>`IhMOhSZ;ss$xDmJgGhD{5V$X+XnmsaGtUSl;}l{Swl8)mTa z!FzFf+hEN4V#+d)ETi-NPx!Doli-qlHys{w3qIZr0o98R(4jn*KiYVfc3FF)nMOC< zl6_97pIbFGaRBFA{1t+X;y`+c3>>-ON_`hT!XS$?^g-()kc1)i-nK%W5jiNDIh=L4 zuVW{TC(yUyE;v4WES20o29bx;1)ld*+~Bs7WWIfcyV#9^;gXzpLB~zA zs<$(pX;yU>T`d}cf3|A_n7Lr^s$kB}U>A(g%cs!#6ydD#9uB7w_E``sZsKM%s)Y0efB$)KG1B@ocyKw=Pa{%~V7I#_`nR5;AN?1oCweY_`9lmGs@D@A%TEQ%NG%vRVFzsr zR7Sf)o5AzcXr}L!4Rw9rXo=||Om}+#XZ2PwKl=j|WxIimZt%tzn+@ULjA&RB&;vdF z>Ns0VOtI!VERZ^2qKXt12L9s%e$_#2-!AkV-cAn_MhjftA#iYeIL?_UeCLPl;S@Wy zSb*bQSj)-X9%oUc_EL`O(z_~aioGI9zCIiF=m+{D}7zO z(<~mUtuMf-%plh3_nF!(R?^z^7vkozr#a^cGgx@eiDVQMaAvI=3^SU|7tDJ`e-z*H zS1vvzuG0t33R%V6`HW9mI~d~^n)5BbU2rYuD1A*-f}S=b_;xxTowENpoLZ;Le#R}t zfc6eLIv@eh-aU$OCPUERp%fgx=8l?GA?)?Hr_j4Ep8D&i;){K55L=N24jQ?%N1~bI zPkL~3FBZdOr(Qlyn1vkqYRI0pjl=Nb0Z?lEmR}wikIC0VAlT{`IIly|vsa!pc33L^ zBHj$QeFK6?t8D6nDta^AU^xpkM zp~KF=ZYyUn|2BoiJ9hE1$DEjZfC3A8rprt_z3ITcBB~jC4<7Y2(Yj6fsQtZ@+qUX2 zTy`J8iUm&5r#liXyznZ7c%Gy4Vb^Jta0e;ZzDF_Va_Fz}4=`&|g^^!Jv67tyaA|xi zOpL7}tDDOB`sN%!=N_b_}Bl7+@u0bi<)VzWkm)c9q3?fwkX-9QdULFR zmJ3tPh^Z6!@guGYeHmchcXYXNS+4jl`zUOycLh$ygDYtpA)YxQoq5KGV@7TqrRBCm z`ezH4^-di}e|QZg+oRF-{CQ4;sp6M2NZ+PKajDu%X+mBfq^mnn(&DoY){+C+v@P4f zF36P~RNafEdZ+OI1Y720RRFz($0@F27o7F8!TBd{@eb2v*yn*V?2d6L3=(o8jr;a8 zTjh2dTXne44-Slp!!Y7?Kq_;1?SF;pMp z+?08Blkx0k*Csr2(gH~jOM-D@mKBj{Kaa>=Q5_r#bLG3Ropci_KQMXTZu2ohO_$ryVM!~DkATgJK8P9>4|Zn5=xVjzdyq6}CruGA}fzpoba!s%cV~ z(0_<7)jr`W?Q-Cui#ewHuNF1U9Rdp;Dq+87JRBR{14g+meA)zG*mRE<9dmZUyDrvf z6`)S7pC<@T!*8@QO!!Prq%lvO42&Nx>`!Desq0uKT~xXXN-yt&#`8P0xz-DvJxxWE zW`t9<&sG``<^hYP&XdZsM0A;z%taM$WC!(5a&g&+Lp7q|&0A@9cv%Tl&1s`$m!5;Q z{BlY*H^y$if$Xh%pwJ6x<)@upMmDoPQn=D{Zt^W5D>ky7A1ru@hb@xCo7=PK%4mY4 z)l1P_asb=oeG_ayM6j!S`e4gTJ9zu#0+p@H5sbeZuxpbl@x!{stH*axu+1VkWD$p> z6}8#uWo6*I0^c(3Vaz_CDJ7WK!Wv{5}+<<|29 zp6IjR`=8N8qfRm^7|HgfpA>falhDd&1Qu8yqhK>jwj%czSoLPZn#t?X%wOpBc8DST zuR89XeFEN0(1EX;CAs{I5x7tO0xdc<7^aIJLE1!BS~Gq%uAh}l?I%G93a z4V%KxuH4AjhOHQTT;Qbc{Yz8(K2o%KGEJk2khk7m(>qUk)CkI9|(RC=0*m4;yzliu=sU>HZ*1Jn5~1oANbqvfeL5)~&D-g6;#`oUINGNsxbh5?p=Uw{WLTz?exy1}N)dLT?@!V; z`8{+ZTf}^4H^SzWPJTjhI*XLZ#tWAQvo#uWRD1plxNd$6<8vqAhJvBgcXJoJUtx)@ z+w-~KgBEak4y7b^dIle}0>#bR%GkViKJNYGM=e_NtjDYt61F+9p=&enSJiQHSp1!2 z;%D*ULSC~i%8jZgJc5sxLg~MzWZ`Zsffs#N;n?NzFfQDSqu55ev7;M2h6hrJWe(M5 zs^K+_aK13km&L*sbhA)`mh{`O`N;{|943YA&Lp^hU@zV)Tn_L#1dmx%@fA@CpdvT` z<_qu3L2rc2*tlR^uzv^qPEf##In%H>wGcuFD$_~Z%R)x}9$XD}g>!;C{#Snhzv+1q z?X}zOFfXZ)mn_zXK)cbjxqT+9+{{3ilgG#dx)`4u#MV#Ahl}ZD@KRu-oNLo!hqq?I z#bxWs;btlwTUARTGmYtLnIZdOl?Rvgiuml>R`Syg|3ABDor5)~Mm%Dygbpf8}7s2|e+9buD(cC6D>+s zO}kC;Ka#k|cZP7C4+hd5M-NW)E{5MxI1}|c(g3~&;DL+Lyy1Q|c0f)UPx!U*SwB<6 zJGbSCJ%X3vm%%b<-*6g+PS2pCf|)e^{wt{YG>Kbol80yYr&QH1lSe}nOJ=gn8Q1nD zbA~!8U@YWn!pFT5f6Lp!msNE^vD$a|D_1~@p1}f7Cry-K@q>=17SQl8X;ciD49e33 zVCkIMr2PAb=tW)=)cNt?!{*W0^i%}vJRGjL24u%4qSob^^sI0+y;9iEa zg7N%k2vv;d^r8$gQnQ7My{*{IU9 z-EG|`s_VPV@4Y&WS8Lb_s%D}1sMZRfP5K1B#n$IOD5Zk<9)< z2HNI&qU@&>a&vVRyZD5H`XUdC4V;6sxii9jUk|LRJ%udkWIR~o0O4vHT<&2hbW;d~ zTQyIq;bt0cO}|KM+JyIMgA~56Oc6(}y$@sWn8AaHS*&4F4W-M5!D}HeW_-)0$~!fH z6ED3E8RdIKZsQEFv`vkS#}>oN@IaQEXNm6Nb3pI08rB`v<9!l5;MvYW?7{eB;Ox~T zYEn~YoBXRd(X6?cRdY^s`iLd-2pYs(rnlIJA-ei0jgHD2g?#h_=wVT`C*gwCdk5V>NSj7AyPg4}Yf! ze@iz7c4t>Y<9>O8yIn>d@3JWPV+F)L{U)|d?xT+bJ;~g(3VvUz6Mi#7CcerWYNU~^ zsC*ze0758F+W{j#rNKsRBj&U6BAwGd%5SyyVqa2zfW2$2=-tCD{Nw(+^rdwXMSs`> z?F(k&n1j;j==l_a$P1#RZSeS_6MPWHvXpJw?A9bVSnw=?!jztpe2)`ud9K2W$7gf5 zo*JQtUpsBs`&m>wK9zfT#E-p`x&oI)N!%(8OLi3}vmbJsG4t|EJ}Y7bt2lFjz8Q9c zubq&%oKeMlr%1C)(Q&Lk<~ejJt-~#A7UImzr_gj-0T13kDq11%?Czv$a(}n=P)YA8 zmUUcLM9Bj7MY;2l|rFrQ?&!p1W0UAzWw ztGJNKfFxfy_awhoE}xPoq);k1mmfHCF)p85M`P4#xIM|{G$Npm&vcm}iuyU9Z?St) z^=sq~uJHUnc=1dDA1H04!t!mXk~5IyO`Hl6&pwjMngH5ge4e-c{1K)cABLCTWMk0P z@mR2IChz-E7xt7+VLzS(vC7E*nC;wH80MvmTa@Hc(^d&B1-6%0@^5&&L5G#am_eWK z6|m*=v3Xzs9F^Q#;G)+MUtu?+#0>j7(EimM(qFNtHin^RD0 zgc0b5XYm!YlPNl@lfnv9QB5?KA}U0<-%*R^zWYm_dtBJ*-<@P-f#NqC-h)e+6+7I)!}w;ia>+s(VZnL$6Qb1BHNh|60bg-4{c z_;ZR5%%@J7G$XUc>#xsc3kOs}xzj<;KVd)ZKkQFs4+8N?u;2wgID{R@+{x~3dMir0 z)(Lm76ocMKGnTrzj^wiT!8N%ZqCkt2uvkHgxosFH%w3AO#lm|or*}7Zc+C~qq%4E` z%i{3ywHpqV-KHpdUd7#fkVKv zJT^k7W5P5?s|{vNZ>0v=V)&+0%)U%@XEN(W7&vM)^O!cD4izb)g;5}XES}C%rH2QN6UokB50b>yc~|$j zyX}pH=U;lc6uhNhqL1ODSeCCoE{zz&65cGw8%1%PQz_$$AqImIeIP9R2`!f#%>Hg) zP3F2@FvB;V_WaaCNqc$bu*wED%9{(!$zFcOs(7l{77d$MZ)6`rf^pWXYkYOy2n;FN z%kNM837>ul9r=`l+{L^y@wb^Fs1cyX2Kj5T@V<_USqm*FU~v||J=+imP4)$=6;jX{ z>%sElcH-Fnv-ClCAKNZ;_L>|**|z3VX!w)}Svvk$R4mNuJ)`+S+#;4V>?hqFY>DER zTJRyejB|Bff&JZ!*|oX$EdQ*B~p+#L7A=Cxd96kUvLuh1g}DWhDdsG2I~-9lwAdFTv_xKoH$@D+dpR;OO)SC^OpTM+*JEcd@UYc$43o;|G33qF4 z)-gpHZwBw6`*fJUv0dmUY|s;af0{IJ&Jy;l!x3KX+#@bm%|Ny01WM9MU{j5UqP6E6 zNd6o~`eWyETbcys;2UH1ws;tmh>E6930L@M1A;Ik*?~zOx&rG3#)gKHG?T5r3OO4s z&}W(m{m=58dB$4MbLF|~PmB0K%^f7x7csN=CG1sFf`fA4d&uZ_;V&C`Vt4;Fhomj7 z+`2W_IIE+N1z)KN6}F#)T=iOL`78^5^W&ISSOo-s_ywO_CNU1ppvCYZnJ2R2p%^eK}o9+1B8-`$8`yp}~mn_VQ(!oCd3U%Jx0F5Jy z#0I-2urrQws48jBu6^%>M^QYl;*ku=qb9TW?GbotW+LbxoJ}1LcR25*(x}q!j7_y3 z_+(Km(`{&_7wyKJ>9VU3ZIT2l^9Hil3zSfLL?cz-T+SUGHjNzjEJv5VK9U|Fjkmv# zVqIqGT%i7TklM2V$0ru@gL&mHl%5Al_b$?}KvlYXMIKKD&jb5+`D7I^ z2=Axs;?dBFEI=*>#@U(n*#chLtMmbdIlLe=(A3*!`DN@QS zg+DtEkhjo{a+b-y^tfRx__RX6%XNGg|4o2c|J^iWGJq zr~a*n`VeEWTRW!gg)l5AE+Z zum>5g&^+df*h7M2H9u3K{#w$?&ynoVny-Aq z(|UfM)Gd(87)d36?!l$pKXh~QaA^1*2N$ld#Dgto=)epOJRz!whrh>Q!g6^wJj$8t zINnAtoD|{tc~7==63>U0XrP{i3k!=GfH#)hhTL!eC?~)JetRTPx7<$1x@O7!*BS<% zuRUP5Ru#$HFXyjBS>WZLLowB+84g%XWxv=4rd+oH6MsJBpB$Ty-xiFgbJex*@23ZQ zXL1iBtcEb(iJNhanmubW`%ca4rZE4$%P?kEAxs`M5gwj&LetG#acF@$a!NtsISz;U z#c@u!J;N8b9o`Q|=7i#hLB>q~pdNkLB(Q#GYO=_Ip)};1kl#`A#Z2u5tj!_-j_i-+ zZ%Q2Jzs!sWGtJ$2`?V9U-LnmjOx=VssX5#v3qa2lSGIX)F1Pl}29%B05Xs~(K1EQ(_ND)e(&y^{JL4fS@*{2=Q?t4MT;G9S z#!X(w@f6L-T!Wdu&mcakfsBXO@QuyKIi=J|wEa^ePS(|@_zVqfHfrMhv}F} z9VkzU#AOEEqNpuyEO+L83eh;in<|-5S&tFjF{^=!Gy|BiuNJ1;Zo)A;vZ=?${5{h7vm+%jDpm;IRcCC@3ZfaJ98NpItmQv*}gF2raYKWoXAQ_&q3Xdd`K3~%mrcN zaGKvaD2Nq&Lfik?CtvP^Z;|`BQ)*|p-Suve(EJz5e&)lD*oD1n{gAg{O;olbyhRUVUJ-&#zk?= zAcD4?e@IHH0y4f`AeXip$kH^&^GO>q_xwfjAEeK+G#%NkUr#}rhG0~s;0*rnoZv_u z2&s!AXwtQ1;{H3*;?#RB?pC1i^#T-r zOcmq~^P+H58eq-imq7Xi#><$n4Xv=Jwt zJBaaBx483-;Sh8DDVTkeU{8)m! zdBg|I_)k<9^okmurwQwkdhYJ9hw!#T;JdgE#D*99_>7{#xZp6rlGS_R-s}j*jE+-` zkQ;jHqlRH!rTq4>?fk^ti!dm3E8D0#k29Q{C6bCNgH4aG)7J&P)O^*REekWGP^QoJ z&P-&3ch>P+VsCKgtkl^X3tw@`-IFkEmX@&4kYKUXaw&SWE-P3(3d#olg{jWjv_j4k zAK!N1#up0gT~Q$G>K3>H?rF5B{uv*ly$jZhobadRX#1za?xwGNHHAluXoSlo)-9O> z-`4BEb|)b#xO@$htd79)UBbIT=n$OCx1|H;G+^zoNFFv1W=^KDY^FyGxb2>R?FF@b zx2`o<7g}J-0)I}a(u}svS3|RSSK4;|9_Og1%mN3mrhf+%Q9GuFeC!snjBDKxH0V1F z^RmRrG4EkQQVH!5d>KW;UE`|jG&~=d&flBwOu7R9$M?`Ccx<~3#@zeBOQ|$*EiV@e zyRf~e_4@;@sTxMxtNwA3jahK{@&ssYTT9M28-!=M8tQMb#%(=zR5MnG;uc2JYe!x1 zdErCXq@M_~DS_*#uL>Fi!^uKzJ-#mxa(UlJVngpR@TfeDYyG#tzTFc@4=(d}1B^-W zQ3fn7{z?r^>cxBicw@#5q0eD{juxv826xwTXtZ7jnhyu@P8A7kQT0slDsY3>0%PlM z_6#7eu_!mFO0?v&3pmD4WS`E;v9Zerv*!zjv3DKs_@GD?CR*JDX`5}>ag!>pF8dIT zX~>7}Cq?w|<}~INvI4C|N=!}f2H!H^CTX3MX1>!bQP%J`m}Ko`HcL6aQtuwMmoLR% z+5qEQjQH_C_R$rCfvod@(6!88gher#5aU!$#fj1A|8XGv}#gi=& zW?qJRqgcxc6Bu=O8EY{Z&o0aj!le)U=-LHqXfS)kzq#N|D}(35prc>GBG~|USO!7k zeFr?jiC{JVnkIG`P<>!M9eEkeG>5zpLE%^y@n9dxH~Esn01dhp+zH9UH843;6-p;> z0=S(AWjbq^@Uf=-)3yOO(o1kXmO*IdJ}M2^0;?i!k>y7Xv=Z25c@tWJ%-@KDUxbjQ z8rOGAWswC-X8TKEa2O0_-+u*&8tE$~4^t2f*yaY`H3m@gn@Vt4 z5rd&4K600;d_b}1h zz7D$Ir2q$#H{k-q`JmMIiMBtNWv5zhP|d(H2n>#8g~z{xf6y#;BsLrF8cCzZ?n(-% z_(JpUc~ZmpT)J)12(4#6)0D7{tfz7oD$db_+}lmGBI^(>yWkDdHfPAuFb`Ie1YW(- z24$TItZZvCq{>vlm&;?se_z>v(bHb;>?LI`^w?%JHnGa-V;*Xz? zWzm|8L(s)z6w^5?$rk%L3K`brI7E6I=qTBOCoIPSp5Ia1EJT2y;wxxb{34K4?CGHV-M(9wP%ZY!#D#{)-Ix9nhQaz$`O{ z!?(UEl%(`ICGgVU+PM zcCAL1%^akOt?u>$3#EcAgpBQyp%aDAp)$5#xd?jt3*dF@J9y8KX+ z>lSPhXJtGB_~L3m*?kB*zJD1=t{KlBD9NMob4$F{UJO!8Oc_~U7JT)IaJ?ar+B-k; z!_S0blHPn?Rmm8&cFQqU&7&Bxb}gPRS;fp+W-z0jD=>4!dRFuCFgNwr51Kqq=*?^u zyt&MsYEt9j&|M)9I!%qt3YB5eF@M3kF@irfXFNI<3}VNpkHB%WXR$|L5~%R>NE+lg zf-EYQv#^joWV1Psw)|)npZeU)UF!bMofdc;7yRc_r``be;Ay#dz}Ur{zHle0`!Wo^ zEu732st(1?V_nd|tCs(?KMR`rQ)ptI4r`aIp~X$gu%t@J{e0zwXRHkKO1(pIWA-s| zh$`?$Kft?{*7*Ff3f)@03`0WB!r7qZd|K!*b}}NdIOVu4R!^9W9y%tpJ3$PFhNUDk zKwijcDU;L#Wj2ggVj<>HAjw&X7EU@3PO`?_hqbN1ZE}W%Rch;no930^qBI4 zTO>MfNnN^jto4y48m#ok+&P0;Qo|$BH~)#4dgYm@azYqe8Y1|2#|>vYyq3f8tA2;E<6gVUY5B>KpKrI_y%h*Ha3nz0=21(Rvne-j5nTHq!pLMYJ;@TO6ah1!Z!! zQDVtBx*;2eX+=tS-&l=3`eDXYt`5eUC2s8XDhsUai>2{>^Qmp$OZ(kwEuc5@B)`$Y z1q*@(V8j%`6<8y<1yjEXuB{^I*gAkMnIm*DX1cRk=ibo2$E#Vw`I9v1=|P%3@-Kg6 zzaq=}mMwUOh5q@9x#IMzm9)|{m8)&iMf<-uz$1C~&H5ZScI(L_nz#KF*VCHC9W(G| zi|_kTrO<{s)S-$$Z$vT8(BCxb-drrYVZITnij?*=gM!)1K)Ct)8RGoDq?i{dNh5FAh6!rW}0<$Cf|{z{N17y=J~7j=Gm|{yyB{*yk*c>NFSn34UMNb>ASPh zXZb?5)8P!P5Lk9cFUaD@NjzPVaYql!ByxVS2yePg!QR_-bg%Y5(d7<1svI+d{LPMY zOFM%}xz_|YM@`4SyKT_WwVAJL>7s%;!&su78hqKG3yUxBfar;qD5IdpY81+#n6n_K z+(o>?noK%;GTwe!g)S78)X?ZSDlC zNbH*eWmSKleY)K(voz7z~1p-ZI|yp4-^qDwyCk3#JWfgM>m zjeGC)2C|PF1I6Fg%-Gr$|1*_f`F7J-T4*`Fur7hyD^!@Le*z^Gcym|tc{ zM+5iO5Ni7Z{AFXI`_Dak=A{7lvku_iF)FNIZx?(UYJhboFTz3L*$+C_yy=+}jH?|@ z*Aim+r0sX$!pQ`}Pm}1`H7yWHqHlBrf>sqOEQ=GWcV~qgsF?dMaw5=Qi$_4I_{*32P1l@ zc0oBP`t=gI&m{ZDk7$3T2*(*7gc#4!n4R_oYT~B~f3tKdI5wZ1_j6%KB6|eSYbAuM zxAI*!mn9+dc(6z{n8NZy%9^8?{`a4T-FN?-j)yeqvgEqZiosD1G zci>!MFFY?t4xQ)eVfwkPqQd@xsIlAv=cr6Z{Y@i4B_V+AvD<^?JKXW0;ALIszYF}z z{YYZWa{T?OgD&;C!OJs2*bsLZb*yI7t3X+(*=2#v{$-&3yM(^%zYXz&Heqv+6B}bR z91DLqqWyP8ez%4d8k!iff|4~t1KSY(ZQRRUI3&ruo+*=pf+s8!m=Kwh$3w{O5i~ zetzwQ)KQCBuHF$?ynhm$_d9_fV)wG;(`_JExaV+1i8%R#uq$aj54gF7EEIoH>63gM ztRQ?Qc1mN*St(kOV$B}d7Ew*#OkBTLxTp2)gSB!^)RF%cqLv@R`!?OYsm%~(Fjf_c zg?+fnJ5%m%{|4Nar_lBJQk|G(U0D08sKSp=mYV;9c~IAMsEEkKFqOX=6v@ z5W_m!U4M>$C#lVTcWdI_YwhABwM_0p;}CvCuW&x>DYGQJ$3@?&$eOfmdY}Zcry|xq_pJj9+HV4!*p7`F-M3(&8WFeB{@e} zqVrOAa%nbSaiEHwtwkK(wvu^vuVp~g54$Xm8y6xP&HentwD~;jbyium=n)qK^DY?XrXAP!tY{dD+I8x>=7bGS0S8Jj% z=j~~`9TFn^Ry+rNVyBCn?~cSlp9`?{w>9M7SB9|caOnHCj@9|_%>S?t1g3>k=1zO= zjok_y_~bwIwJN05Cp@@Sx4hWey>e{RjRfpiXM_#ysj$5Q*sLg9_RP!>H}6k_<0f_@ z`|TR6@1Z%iPTb9&NpHt({pV@O%_;O@_W)+=V1qv18_0Ca5xDh2l1tj>OV{#d;ba4K zw*G(;vpPPINs2zbtJ;No@p9_g(nQbo-%!S+EOK)#X4@kzSkogP41Ihb z_HKXARmmA}-=n-?e@`49IOPj_arabqp(5n#ne_!V85p1z>}eu@&2Z2P;U|ZN6xGG9Qg~V zHMoqExI2Y%_vvEU?gJR`MiI~A;ovMJ-RN_a1uW<94X zNnZHsMHf7YEQjvYbG(-CX8uxQ1pC!B2H)u`kWy$3Us#yW-VSJ^xf?_{FZVd8_scV9 zvl1-7>;u->JK;*$73|p=ju)4nB+0Tz-27vS7?AXj7F4Oy>BNofy5|@0wHv{vhSiJQ z8Us=G>})KOGsFd}|AQ6Mbr5#gmM!*Hhmis+#PI%TyyPc|kB3g8nvwDBt~H0vEr>HK z2D39huj$@%cXp!J4kWj`Lf0!@ZvTQth{G7lT74X1`5T;qj2cwB3jX0DFY@!9g*8hT zGr#(Q;z=`V`2}-7fqsf2cx5}X!+YitKWVSP?Fpp%s}pdrgK$P#v=Zl73<00hW7y2} zF<5?a1b*0Mz`o51qwch&?0~8q%5HqjPkktdqa(kP|9Csvf3#b)x5tq@gLC-7Qd6*o zmtZa5G=abMA5~s|O&iW;;_&UdI81O&zq#2!OG__9rnv(vEY*a>iXnu~e?jNX8$Mq6 zeqTE<53;Aff^SE>G0*xwIPS}%>8@wR6=yW~oX#%3+42D0`udMkx&4;>z6k8b=Mikr zOAoL(?0|oJV)|N=HtyQwof)?fwYpl89puPRGVL z6{g*4hAA0+=riFUg;yQo23{PFfpYC!+u%C5pZb%+HTT2Ay1B3{v5V#kJJOgKp{Es8 z#|H%Jv01)l;MQh=*)ta5Du*g~g@f4AbKQJMwGQ6>6^$m(ZqdWv-vm}Haj&L(Gmq1g znDxpDn6pijeb0-7Is7n)xIGb7vQwyk;yZ90Q^jA3_aceV<1j}!cYij>ri7h}>~p0z zj#zMtf1a*LZ=C|!3#I!Id?^Wok85#XQXf#QNS3LOy-y|^E4j?4!@$a|2CN$7A!WV` z$-I~hyDD8ExhaCRowyImvXbo5KyB3jxs9)t(Zh$vU%Bs2qv6(xJWLmM_0RJc@na_M zryvnWQW6s|_03*fux}Yoo!v`H9Qud7fJ3!GIJy3kv1ykLwft&9%^u98OdHqwx-M(@Zu(XhK-Z}ye*PX_+ zf1@y5SfTZ;HOI&Il0^Ngce%Bi$EZM7g=M88#NgNF&pRp{R&;nE>Y-W27kZB;>Qu=Szf|veslb3a8DV)W;fYj z^o>-i^S?lWf{HOD&lG-tR%L3>GilV7eQ2w_oxAkT4=pnfkWBq{W}^`cBUY`(X;opk zpjMLkdxnthsz@>t=8m5ALI>o+SXSaPj$irrg1tv3Pu|VJ>`m)Y{;i8C_Ix{n(gE{m z&fg3u?3oMw*~0AJzn!>u&%}?9Pl7jBOT|}vdTG${xo9=_8%cCl!PZd%H*2dEshiyp zeOdjMDkk{wnbWLTlbjrlD&Pd(qAWYMcRZF|^gyY%iI{No6sk;}jgPL$rBm3dv5Ppfnt!A|yN@2JPY0W@cwtX~T*VdxEa`XwQDzqQ zqGNKn!(uIlw+FJd-|}F?>{57V{}s~0as(EVENf1ki{qwm68IxW;IeJ3z0dmn^vLNM zY|>u?YgK!1SnqfUA4aFcxkYihdC7ZD8ii_$ z_QRr^4>*(4qj2)$M%c5%kd5}Yh8gLJm|?XFM-{uVPhB3^I(-~AwtnNSWw(N!>;`%h zbdAqj)lN4ikA$NOr%Se_YB)J(w#aJaX*xK5HXp+EbJu$%;aT!-xag;ggByck+lNY8FYJQ+ zwrmhRs~Us_foU{ya0%xl$_BO3OK^IaAv29TME4fHf}55x%;JOzYq_&bbWU$NGwztn z^baPZcV04H3@L;zBX>4c@TyEJJx(6k%ONFAi=qofFgzg@Z&**o^fhNW%Tr1?WCPEg zd2k3%_Fjj*F<%6%b1Z%~JPorS{x2t!iwU2kFfr^Jm*chx2CN>8Z~t!RJr*d6OOHH) z-~;1u+`jR2TH&R5!3YoB%?)FjAA}xB+D-UVJ6&XZWjF>Le#&pWsl=Lv9g)=iI&ylk zA0C+Kfpfwv*H zAGOz?+AUf@XWnq;Dwo7f*k_IrY8lWwZW`V<9fyWr_n_*(BV_BlmHkw=V{7}?)!3V$P0aFIonLrHWi8|N(hy&9Y^4XX&p%izoEgmvtZ3B_u{nm7PzMACro|3 z3bTbhzPm{_sCYDx-TWYw81IAAfx}|UKeT4dbN;2i8GrihIa2P~N!#+{C!$LzIdYlU zJZ&d=H*tbzR$vO3^@FrUwm8;xA1e$>rw1Vi(SF7rvy3vOLEOJ& z7yjZ0Gd3X^*Vo>ld{tSzUEK#NTl?rvt~GCHBaN~*=ivskAJll+jCC~}sn^R+$anLx{kB5h0K6pLd|+>t$@o z4q;E^>;N(+m*S!Peeh0~pf5t2@>hb0gq!S@6B?$10HvFl^8=Vx2&vpYct)~Jh|2D##{4+XF) zEF7zxQs|>FAGk2z334wqi+-exg!C0Qc+pFkZ*82)6yEQ`lbt7F<^f&0@ZUTVw>z_2 z^Y?S7(hYdChCcc$@bP@+NwUu`_s~qY2(UU7%un3%3A$w+pg(OYJ8sZ2F<7UERF56P zYjgk6zSLZ(7v}Vz?v;x#o{`0!QfzL0f_%9AuxYokkP4C2>f|uh>(rkpRC`{oOp^0rU)V#_7c{YKi z{>z6|tL|X&@iejFwB@jIvnjUu8nG|un_zK+IeobpjdMTG7V=hM%xcqkwA4Qcp<}Yx zv4BUAnP!g7GyXtiSvefHUIZ^Ah0e9oE*w{NL^zX7Wce{kVBTW@MR3Xi`83mF>TI^f!i{kZ{_LJH<9d>TND_zcyWL6h^`1{|h z#E)NB!RXY-loOP|SF6`glvmn~BVASLj!F+qIG>LHok>7#i=8Z`v65bEzXi=i8@ z&#s^O&E5W9XTNW>FD7=hk@FdWEA?(BE7hC_6BK8#={MB5FYhOyUxhBEts6yUfw=-l z$^urDnzF2RHS9V4gI^k5PP-AblhHDd{@86}5H_UhwP`>FWU zqJuYgG)Jp+HZz7u#~J4tu$k4JvHb!($Lum4Z*b z-iX#arcl7uD9&5(%1kxaV2|8%AwqsSdor<(f6{S?mroDlcc!19ZMBC$WtJ|V&=SF# z7pAb%Ru9^26vKAN9)|}>xF|}hG|~&u_;e*x$PMQ zbLSp`vBUJ4q41s=qU^w4>^Ei+)6Vm6zg?oPBZ9wBT@t9E(jg66Pl!+SVCm@lgh8jakEsS;8BRG zz#xg{#5eVXyy|>7xHXEMs|yF`8&S}k>&fX1O%l~QeIwn@WXSO_rqwxxBsE7GEW&St z{Raj;bCPJzj{)%8#Fgsj&xFZEPUNrofc~jGg_2cGq}`SdjWgeoL3A4L^VtjE771M9 zhPT{WB@?Pxvzc0kwNa{6ADAS+r?KJ;XdIS97U65*y^BZjh(irDS!E_}i|mKnyDtjt zNM-QsDd*4YFC-U#OZKqd5PcOhL19k;Z@O^`bGq;mEM^~Ik3!<%Pr5(mmpe1PearF4 zGaH^#O(S~LofY~iS-`3lC$dvs%rO#*-*^r;Ka4R^e zI)P)*9!xQ^7rLIpeBRd)cA2l{x{sL9-*?YNR_S|46upQ|eR7bkC=>hyU5oKsZ3#8I zmvcA1UggK%@8Os82cgi_g_r2I!*=K5$KAd%JI7Oo$jzND> z9*1oTG=EMpR~aEe%Ozv@>hMLZ_4ESv;k7H>T{jq~Run>VK{ST(m&qq*COfdjlN_wi zaNX-qQG&W0v(oM17tXy8jVGg-bA28>j9SNLc6}8*P^TcjBNV@F8Nf=FsvzH78=DHg zQ*ogQ=Ottzj}BkMav~Sv)4umSEx%6t`d?G+-6?24(2emQE6L;c5$w4s+IL3rE3@@krg8+! zJF}CtBbU&$vMr!&T@4!V-^0R84rIAD_-m|(Gn$W);!9~35s?I&Dsly{XC`;lCy6U~ zp#{Z39<(!44L*d$VfsfYeEG$ObzY2NiuT9gYDN+h`I}SVhSlt_PP6#G#IL05<|*3w z-3#KLB;fjI#@IVw0|S2!Wsg*^LuF(wuk3LQswN!~OQow|d*@*62-(91s~lurx6HXB z4+|W=QH|4y_zb&eKIC`(*#=7WqgdJ@21RFXbKPre(q1@xy?fmZt9CiqN&aw%2$R_72Xei&~EHYTQz6tQ%{0My#_Uc6e@dULTHe?vF)%TTA%0V5;&6)ZATZ?nfn&XU` z9hl!SfpvNctf#Yzm@gKbO7mu+>7{Zw>sUw2`c}dEAD`gs=7DU+x>owNNQk!xt_A6D zLlL)}Bc~68aiOJ>Fz0YZdyCtk_rsoyX02vZW}e`eD379-r**`glVu^hLk<_+n1%kW z!p|~pA`1|F<6i#%apNre_zHz+NPRSnMQ?m2_-BO7y4*oJdCV1q&IQufXr3E3G8RUE z9>KOwES+fQHywIc2eFS{nwS{c!7nrkV<$pp;%vucF7k{nOJ6T!+D!dv>xRcvd(?rw z|NMi_%gto(`?lawAtO2Us6PIcHHEzFsqC_5JMa7T1b1Ixk;vW4g1P682+w#sTpsb2 z)12?m&RcVkwnGXtKWxN=C2J{IMUj0N7LFATBlv$h-u!6W0%~rFrY$@C@XW*=Q0>qJ z8gKtXRe2h_@$@g3-MtLuFU-Ze_AhDW&3iCA<{f~8o9JqcKAhCCppYpe8JFyV$GW

Jj%wB2w5{y0NS(IsEOXh6mqRQ|()4rgJ+7vVW>zxmP)t z_4gb9LTmSGTToW;M~+NO2e}`m zP$_4CPx_BQNXiAc^VbIcjfsMwCMC=}V1}$vPFy-phV5zDi_bWJL~*OFu6P(x;tbBe_8E!h|4qqs%Lhg%+a0Jn!k(D6I-;F|SO$n~t{eMTVql!G6_|Dm&pKl1cF7x6Ze z-F!a~C4PRUfeNSEXcweOaAeEcqgqNybE2!4}!|HE|?MjRNV1cj^r|A`8Q`? z(QJ1&wz^;}lU_WWjr6?A%~bylRhz$3d~_+4a&sxkDuT2OFK`cR*41bQr&XyO04Ll1U)=(OkZcfq=_p zyoB?V}08_ z*dgzV_qmmxeFfWZGcj5G1cj=2Nbe`C+>X|^~-qU z^~C!$=lBc?{;LlLcgtx}#B?@SeH-i?b^so15IX%lN0ypX(9Xt=yZki{jh>~^EFn7~ zEA&4ac0c3plusq|D&hE;{SsQqlQkFl(3$hn=onZ76TH@OGp2r`>S8_S9y|&yz=Kn6&biD*#5cyzS|%u{LNC5vwGu`cx>p;`i0lnp7QJD9pB zJFyp<>nVAN1B$id>B5H>Dtj_I!qU=HK^VO-=mR`h8!IJpaR)V*~y%WNw47>w5ANI5Pg##FRHbY5~3OoJF z3sc0;VC12l_+-atdg6Q<)GTb#PBI@9%i55+n)`!IA~3xDkJ zd~UEsA~#S{4NPNoQ70}AJzb3WDS|^G@$D)W-^oC7$8Oe~K4vCqN9b4q^6x^4r zr>^rI^d-(3eJ?)b_a|-Q-Nn89^JkJ+ntd6P`)nV8Lamc=%Yk#Q67qXgV34ffUiWyyGHvAmzJz$UjHrn9hbvlB# zM$GAPNJdL$!d?9#B=@z1R!y4AM%%=(r8&l^Q!omq?~LIG?k(aZ3^RH66Pdz&QP@2j ztYo8}DKX3Sws^EXRV3q4D|+MS&$<)$vait*IQr2`T5lppyFQ#IxVR6El2+mY)gIcc zeix+wy9BSwIVv-liXIJq*xS7vt^cfp;*@l5)!103|J5GOa|bBD-(BcAXL0{I3pq`7 z5oV@Y<0{7lX74l=J5H`dmHah0|3J9l^;`-|&Zsf{bz>mBv5l8r?E?YkV_Du|ABu@G z$9EM%U&j10ExUY;f2;R`zn`@lq((_{1=Zu(R)rWYb3+p3uGD9T=4oQcka4VT$X}?v zG7!~&4Q9#PD`|S#-r}gAk`!=iKO1N~6XT8LAyPAt5{t16n~89KRSA z+-e4+_NQ?1Wdl6@WW%Bqq*2uFk6G7^*rqm1?yUGSXQmv)#(V>+crl$K=Z}G^^GE5= zj^q4fZj|`ug^Al{(kbk(3@R>4T)A|v{zWELR*))jPzT3wZG!P6I z82_b3pXg@k5q_a*0Q@YEqaHhBO1+y5<6ghzhDms^wx3ri)4~v!J{pe~{p4`vZw+)b zjKt=Z*FYh13`q2+^9sv@$Wh0dNnD?XlO8akbGe(%o-D!LFP_3itlEx^+X?I&ze12h z8ZS6^s6XCYF{ib^wN)0(G_*3m!gN0%&+pp8m8j)!78-EVF*#l3LwqYkZq{ORW1y;neDg*ofDuD!WNy#N|3bx3#p zUVQo2hK;o!2a;xADRhn&>Am*h-`ac;d`Z*6vQmZ38+e8bE$i~56ueoN z04}k4dm7`SJ99 zf<1%@G~z*Y_A$K8WU4F|BC zI)*Ls)&(`mCQ5Ps!`*%~iuduG$^YIhOL>Vg?EBEaTt}QXQ)zn&e+}F*y-~qFC1C*l z-u4r$IxS$@rWvH@-pt8F%fid~pDA9L`9k_UvQ=sjeK=;qX4*c5A)igzmRJ8c$><-V zg9+ooNNm7fJ^Ts_cm3d(o0zj-4_`r%Y(GRVdr1}17ohE2E6KGk<5rqa#_YQ3tf6Nu zM(&=(bhaMl+h(SNnXW9;YQN3>5i&tRj}P;!`n2KS-{aivpUL7|>kP@Kxdpz}H^57^ zB(iL^$KB`jSk4MLCYyYU^t*yMjWeoD#(y$Q8S(}e)}Ddw8iKPcWeU3!y^P&F9L)~Q z{Q=|NO5lhXz{4Wlpe8+n}8 z0qPOntkFv7UnGuacAE|1`_?05K6WmrJusPjzdx8sCN|ONSK)lZT`Sh_y#P!^hWKUc zG-fDzo!wrvh9;L<@)>H0=-a9S<$ngEm-=E*JUR-CvmIEd?F{_1Kah5I9;BUX zOXzp2GF#EVp7CC<#R@7npyx(4e961R>#b>_zJq_bC0~|O;MzF;Pv;U=a$d(?yVISX zUTP)h4I!*^sfZ<6jApOA|3U!!K*Kjy7u%KJg`q`+II6gWJWoOh8%bXs|!_G zSfQAtW=zB9#YaUJq6y3+q#lxA$>HJhCG5Z5ZuDz(3;c|hK~MD;G<(K0c0WNKI~M6f zSH2o6XR|1NMI>E6`v+#G8t`w&dVx_yHyoa@Uf>^UV0Z0rp*v-am1!>Q<=#Lhv5)85 zo;%UQOGB8UXe%rK(FThON3i36Z0VAk0&|(PkWEhcK|xc}Vf+hcn(tCVT`9eyMqyT( zeZ`8d4}HR?X7|9kQVSNHm=DS&cAR1RHvIM?nYP{5hO8&r?B}xs=vA=}UPWk7|E&<( zd|DpYEV>1MtbfDM^kn|mlFgJ`VUH1pU-%y_D%euvz>fPgQ_cFJ*z#Tun+(<1pSbUQ zc+*IBcD54@xJZ1>`Gx%PUG-=e00g0Dl>o&P20Lk+zixNe&g z1?ryTGuPY^j~JrH&K!z|n%}j2qNzG}RR8qNC&E5PLDhzZ&#mO+-fV>hCwvI)OwljZ zm2vVZFt++S-PjTUZ`a$gu?szzQ=>ctcTL0>!W}2^%MM7o^BnR{sKN}TA~063&R!u<7^_)p(5)?oCRG6t*C7O^^dOh3sj?o)uFe}>ZGJPuX~x!32uA&~16$%;Jo zKz5!ywps&o5ISnk#s4^a#krW!W`(1-$ud<^VDI|Q!|xp>(0^qfRk#W+pNb^D;-vxm z({&6Se%~j>(m?w`_q#doS!ejgbK@a%tvo(EKA7#xb6^X9o+IxY?$Fkd%w1DGDY9<6 zRy^{M6}y=7)82PwDMkOW#Fmya{^k%z%%_)hGWP-R5p9mXnkLMsIEIDXlw|?MQp`j% zjXFL_;*ITtk#b%_Smiyi5^onfCS^eP;Wilexq!Qy>bqJ739>>@{mB8)@=OEONC~zhG7eI-lwrfItJMAdhW+c1PB5HZ zO&fgdDAz>-yd@)<#{RKv;O*aF_$`3`O}Ajdq1Du8Ew}?0Eu@*3Te*qR=crauf)a=Q z;7gX8P|Ug+Y{@x$VHQ3JPw$(|;(iN$J>^o7wAD1crIjqM8a|kfY&K)_8WibBKppM) zuM$Rw9s&7nQER`{?C#UC9R)_CQ$tW0?l*|Y89>!-`#oSTPd+>ux6u-XV zh|6#2QHJveIO07HwI>YZhX}k8#g*CoSDjO^cJ@EsJ7zJvn{u1;Ui6DUcvr{>pBs!h z35rmnb)Hsl^`y)>9?a;23|76|O`C;he8H0;?9!f50;BvnxRvRNepEf6(VtB4b<1d0 zR`ZAYH%w+v2dU!xoRjcVQ3GyH8Ua#0%>t7+8ebijVr%0JS)PM7dw$)8P9Igr1*$6W zMbCx}E2#s=lBv{wSA%}t9zhP-zey**hX#~P!RBIJ%p7lzHJ(v8{mpfHZ6(YnR!oNf zAO~z`B$KYVfRy)H(jxDL=*D@lQ#-`27x4pV7zjJ#S%$jVtP`7O~~Qdzg>wC3@%+ zfwz5B=)PJiO>@X2o5|kzr|Um(^q(tMnsAvq`-7NATsMECGm&5AKOWNlsnXx8kNIJW z=B#&w9xEQHNt#nv@Rlp(FtJaH`7E(!8zigv)TyRi*~>Y6b;cxon>dsmw(6(D4{FFP za;n%gOXxoRnTQTUPT-cREx6l4iP^ykGL7h@ko}`s78v6|izYai`0u8BvJ*~PGKsx8 zkxpM{G(gb7Jn^JYny8v%07J&!EpB+9z|4-fk?~v=HtDmmut&cD+GRsn?7Hb-R;xxG zuaaTe$!bt|H;Y-$k%Dz{W_aZA71Ca3&93!5qeb#w*#7buHzr{r&YNk9yK+O&PtTbD z!QODe?Ss%{oD=l_yd*mA`Uc$mepANAaKT5VOI5%6;mwltyJ0A${@xTAa z(0Taf^ha^Lp&?Br8qy-P5RK=aql}g!QlgTPY#~2Mq+J@MB8i5Pk&&&Qd%lFyG?JZ> zm6VExk?_0!L9eIhe(&===ks~L6KT!G*-#Mj9Tq*^%oUvOq!&-+*qe+NxHD=hf%p<^ znAZS-H_l_##5|JjF=aDvibU+AJXq--fDGe8kagY5OHY%*ny;ql5WkOKTT=@2_+b=k zvx_Djbr$;mewePJ%P%rarA<80CXx*TpD!zjGg5ga3X!Ew?TF6euTIQnfA z|6%VG9CJL3R@YDGYU`Wn^*^D9HNKX6|7{h8*Jy}0R4inV{tU(=@w?gL3k>!*PokQ& z5-j@l3>=pk#gB-a#R|_^p@n%J1q<(~$<7YEzIHJ^o!1P-5ygC6Tqiy1Du-308)4pm zVbGElVfQ{S78}Z^va#8tF#;2y6!I+)Ersf-PJ9gw0bCgZtYT+PTLGTuC#{TX+kKfJb${>v+YVP(;9_pJ-d zJ+TNY{#J4|Yqe3PtCN>;9Kog@T?vt?=eX?)s$tKJ3uwAH1Ll5t2mHfD@ZWxCvFa@y zl=w4??a%+p&umytk>-jhF;D?hAK&BLUpcUKTo691T*=;5&g2`Iz%^N`N_Unt(WYD} zX7N0ZKQ(D6H~h$OHoUcn{}ZLpq)#`CDl=Zecb3m0LL)$RMnCm*Ey6*= z#HD|J!QWsT?)(&2^7p&Mh1$%d=*f|K?36#Zn12=#n>Tf7=3uYrcs0Sj6Dr=N){$V;*^bI}Qu3#L_p} zK+)SW6MV98ClB6BVDZ_bw9WbgH^rg~ng-sWod=rWS(`5#P%{jZK2M~*rV5;NWerT> zt_$q&6tX#XmnNLhVfLT4pwByV2$LX~Ka4Q#SuERixtcbwILr4|cSBgU3ari+SPzT0 zlJf|2{QAxdH`k|#_WcfHU(Q7_{r@!Cbmh%hqP&`=4QQpaS4(+xpU*m~1h(T+Mf&<# z9!%SfS=HvQQrq+i{E+4uO!w;qEPcG08CVOfT<=24$RET`G@PUT4gbVh`muar<8qc_ zVTAFARg>$%3Tgt_E-bzA1${LisG?soUJfNZ%TRHQcL*R30 z6W2A^icQG743R5)U|y32$X8p4m*kCOXCx-lui`@#bwCQ554XYhotd29EPK{_!W`B; zFBJXybqaL%E3*m*Ddy4W%>tT`O+4t#%sz$-^B6sRKfXw8^<*O||Ca4A7(JQ z@MAE4Mkt}(68-@_h}OS64=O=ckF5NT_rd^Gl%aN zCa1}E2(wy)@vcI7&p^N5y#- znkXf+7d}o<yE+g5%-D+wvlQ_A z!h_J>_zQH~^hv=q3a3~avw=dNPqSSUcY4pGDuK%s^2m+(2|KGb7WV9n_#|f~G$$YAGyX^WL_G;&4_o=B+5f z?HIN+r;+ktSQ)p((&C9)Zp)r7ap9#`Nqz8`|*AlqYqr0hc(a>u1_9A5}{e42w%Tc zWxeqMRQanF8vXCVgYs~US$>6@$ptz#c?u;eeMVP&HSUeh9t(@eWax zy}2D8+cwaj6JJrn&4L-UALn|11>)%)^<>^1Lzl~5a;sObVUp%y_%h=R7~YW3wT%@DG*4pIJvwz8?*F0O?_!L;f9z!NQNshIYPUZck zyW@zIx13DR8F3Umpy5Hm;_$6%Z2XM?wl(Gj1jTEU>!>}<|I}0%cS@TX{TBqA!oG`6 z{QJj6Sp}k2qzdV6mt`ft1%{?-78Ffgj~6%Uva(lQ5NCUi^H_D1Exq{_8Z`rOT7LpX z2AS}urx`GvK^EBR-3k@gyQz7DG3R1f&*xj9+unfN|)^ty3MileBT|Y zQc0wX@6U4XIwd6W>Jk;F?uRwzk6>7SEqH9s;il&yIDQ+%W^C}K^}lxD-!pX_DM(_- z{H?UWcpP2Z(@H}J{YPiDV@dz{3ic+`TyQzAV7`?lus~go4?3~|pMAc`FBmx)TX&9O zVULXj&rC5+%A89!`zq{AzQ3g9`D5_qJHc(MmdV@L=1^4rY0!IO!}ss1q5Jo9=tIm3 zk(KQd+PpuFzh>md!|^C~jJZPeXjw27X1GzWzwuygDR?B_Q1`686uq$>w#&$)Mf54k z$Oz@e#tuaz?_f4G+n(L-{7r?XEwJv;J8CSD29Fn#Y((h+zUB6Cmj0>-I3-Os!quGB z9(2XccScd`0d?^?T1>0Qs-n~qUqq|rY-z{^G#0zFFc(F(rus5;7ES{R-Uz?ydP2b5 zEE@TG8+8^WPj)=-7K1Q4K7lrkd_$F&+~LKk3?>oo zjN|WXQ6(H_fNIIg5b>LnspB4W|TpXsXo&#{37%&BUs&fOL{MUOtG^s^O4gXVM(M2 z#tQrT4;TF4^~lwD@nt$`j~QqunZ1yLey>Fp=Eu_QLvY^G2fU9~D%5NK;rqPa!dA5h z^j7@}`F~4;`jSJ~`yqh-g=<2#FvmaSzMI;a;5^^ti@9!(1Xj~#n67%1bUH>O>(IvI zuAXG71Z+!%J9Aq%0nHLBIsVH@TJ_2hqoo$Hqu2MaRsIC+#yPmyznk_>FTbXAN11(1 zABcN@okK_U61Z=Zhga(e*X5g|TFiMeiM4=;ZAzf*x&uj`^N_ppHSfCI7pF-X zvcB_k;J)`2+SJzqd#4M%foey1{m>Y~+BtTwb{xwYF`i}ny0c%us=({dPH?>!jboCY z(}>*$sQFNmVvZceM)5$FtULg9df!8VuxmYb;x-M{I>YW2RMHyNv0!^<05!;tCEt^y zV9AFxoY+4O#1nv@f&*R$YUOCa?Fum zGz`n`Y+^Bov}wUEW#%;_4Nf*|Gqpu7T&7_Tt$sa-MedFj@BApsoGi3i)k0%b-+dSg z_7sUn{42NHJ~Ig_N<#!*sXB8wb%)m9izPK3f0V|TR3_|5R^}_RHz#+XZMrrFE2i*D z6_)fsL124S%i*M2D|BeAftq8JMb$mtWN}dzcVCXd56|}C1LrLeQ5MNA^_$@7{UzMh z?XS6E8~&6&9Nj@n{&i5`yh6PDV*vF3t{|T!$8f=co4kBRFn{Ud7?z$k9Cx4nMo&(v zut&`&`Ei%`GwZG@{8=}hGJG$?>-DS9MBwnwDc48aHVKGZ)D24t^$wAN_`XB7g@PIe_eA$YJ5zw~P zkTP46@yYuLk~(RPkETS>$N8S5AJ!y(k^L1uOZtQHNi`VO_6o`?p7GPSC4pO|0#lLd z6&rT_;9VwOB9}*#srptr#2z?I@{)T9~C@gv_ zE!y2*E>f3ng7c13@v^I_;GX{r3Myi*U|9o~mXyG+GZHc$!Pi6%10(pN6LxT;`7)?* zcVW?rr&LniMt3u<8J1lWs|d5VDgEAD?sp$}n3P6bejLhgTgxg3kArjrb2`^KmcO2? z4*KQuDMRBfzv{jk#t$9}jq{2jK$y3my8VfIcmCw&N?7opza0UW{#v;1vlT-F%EgWc zq!8V|a?YmobEkIFh{3BU#HWiR92{;!btA&nN@Du4rlcFb+UW>|1SOy$Y% zxPcP^$>ut|ShA6_GzE5iTRsE~d;*0Qh;w%}i}r51X>#!byFW7u5nq@%u4x z^=xRq)(mQ!MQC(y8qN-$g@5AHVbjqB=#!Bbo0X+-8mYEu^C*sfPks#pyjQX{@1>b* z`AK@4LM)E2jC-{K<_gXu|VK$9I0sM7HvC9D>IeY#@KCmEI*5$?2Km*pQl0o z=^EJoEfIU8;_*YXEUD}|#eW!K&OY>xV-BM)(EGL9xyDdSu28-i%EnHojt|M`U%nJ0 zm|~d$9=evujqPlNO%^;Vq~rpts^A*; z#z6erhurx+SD@Z>Ho6?@fs}rMN3_lZFO?pKRCS8$3A=_t7-0_#PY*@5A7)>v5<=DP3w6ICU0l(Qe{Pk}93d z4(^)(XHLmtxs)&$^bzI(cO-F%r<(Ye+$^*o*2K3Yynzb&B5L?J1D8#!gnI&~@#@KS z>_9|3pR4nQZ@%x(uNpQ9#PPD|4m-hO#xPKfI#3$2eJgt`blIA!C~Udvj|XN5xd>q=zsj(Zs#hlnef#4maX*O`XN`eQKX*2CNdU?;MdMHV9!^eh zY^xuz$1#w|rCUWp%5yPw{0gNl|7qb-2|MOGPXm9?swAtYawQwS-vwP?ANaA(2379| z(9$K=EH&1>6!W6k)nhTNcEfC3ES|#uIMPkin{LA7rRJ29UJpkrzR@7brI>qq1)eQS zWoAmcj6&q`R8A7yY~;nmp>uxVl9%ncmP)w*B}52tMD; zZBHM?RJV51gU~5hzUU%->D`vkja(grd?ySzBlE|y# zfrn#}8GGQdf_m-*c(6_4pF*c#I&-jl&7D*}&MjOl%eLk#P?r%;+81xo$=mDr2@3gk zIrHbB^Eee@XPbmQ3lEV|yb;^@I+4WvDrmoGFt=mtW<1ug5ih>mO~#1<@VMg)sct<; zL#Encslx-x%`#_cacf}nv*V&oxjXr*T_Z7y{|dupI7~3qAkFq&Fv>;}Y(IvORE`3( z&jHk?Mird*AskQteFmFN7lMNRTbLtZj>b;uyhidAZn*I&yLKNlRBJV%QTjX;?PA)>xwGQ|zu-!tp+@^`U4#S;e_O`z*C*c~!-+bxOGt|7ml1=43N-HaG zkj3`%c!{LCD$$Sa`3d2NcEdXtwstw^ihIj|5p6I`~;PnfBu)1jlz za7M_RN?dc{kDlv=7n4i)5i3G?x9}Nk`d1A;Ve${S@N_blnS4Za_SR^sI$8^Zf1V{X z;OO05eX1~=ff4=(ATN6>^GUFz7d5fCINuYOT79JCqld}-o*2drR$w{;lXZ`uKDYI| z5)0I9p?9w(nBBu{oYK0T?a_YC4e=AOjgBGUQmx3w@O9K6?jc3-2l`vL1d|{IT`w%c zH>V$SGQQHRG)|p$*sGHMU{5T`6S~Zp2OADQ1>>W&xTh!vIX6k}w9b3ckX_bj-aLox zKJCiCTiVM{n|Kr_1^ixz1CENk;;qOc9vn)Hy6-?<(B9AP0^q&)_Vd#lr4$VL~tb0iZ5>+wyzjxr3KAV#C#&B-yrs8qkNBqtyDQH%u%Pi`~ z;Pxes0?WFejFj8Ne-Apa=rjkx&m1mr57$$1@(^@(&p=scb?$YGz@0K^q4%lEs);F(_)0q}`@j@W6OB zeq@K>WLF2pMLeak>7HabJqf)&jDjQ5ouHc3M1C`U$xSg5tcNObArJm>-~Y~lj!!SC z(l?6zksAkwpL;0OXFp`b9-|ROQLLdl8T)VT#=FbR(Baop%A7io9o{<}e78TM5u+8b zW0c^rH!J}MSx;(x_ljzyZozkH73O6(2SOw`h4%^w%zVE!%LmiK3lTdwj> z7f!+k4J$VEdM@0me#kWEG)e^!tP)(h=Ia}k4x-|6v?-O)vD;3z5quAQiQc;7l3?3?bLN@jT zKv`OFT6XAg{nPDfsN*#B+2qEqMO}wfO-JT>s);-A5kv)rQ<&OKCv1|bpmDRWg2JN? z!iB(ozqo_is)@AXg)9!gQNG%JH$&qVJdE;Xug*C! zDX%GbCCH3zy|i6$Z>-=yH(V+i@qQg@RsjZ@^^)b4HeM@e3e!*eKpa(Nb%*A2@b`9SZ7R+`gTfZZ(-;Cj#n`(_D#>6RO!u^YBSrJOBO zzP$h&Vr}sK^Y=8$eFUw0VTL!KDdHoiS+HxJC7Y8j$%f_EK+Iqj{-=@^4pRDE+8;BJ z<>seDtClLaX%dQiUQdD%BQs&P+EG}r6`7WdGwdjqVS0y=Mm$`@`^9_&@qk%0wk`y> zwYh_*)>+72_YZCe9nv^8c^rLpr?^AU2RoK;r{61_@JYG>er=6{IFky#Nns*gz8-~d zyM~~V>`Kg4ct^e?RN3aTQG$Ol55i1tf~Rd3mOCrr{4;^L=f+^cnSVyOFZ*y;J9goa zC%>C%c_m zH$LL8XT^{)9Dxj7bvWiGyJPM?LYN%~}YJ%G*mIb3+( z=D>jbHMA)>9upQBa#8Db+1EKk@b4rwSc@{ar}8%39%vK;IPx6kg&r4u7I=JBlHFS3Sh-Jx0qn|E4m>YOpqG zgHxWxzrmkA?52Ec<|*SJ{Pk6&ubjf)q68oZF9mZE`?P!b<@qRN=nw8N~dGX zA?$M&tqqgIABE3AO<%|>*`~rT!6E)HMCijRDuBhZG1R?eAPldWiYJ@3NK@8_)|wxo zuhui!%ok(XoB7IUdB_Kjd^2PDLzP(HVJW7WDTcz?Yhjt|f4FgJCG7}ttbg8k)<+%{!b{k?%M`CD$JU*Kf&dv16;cc@GFuSgks$M*! zQ(i}4*iJ17>Nj9-!Xj|5eg>wz(P5{SofWUX_MXDWOk>ObNU)CCPV9jHNow{T%)fhf zkFMo$}kBESgqqRi-TP<+Okn3P=l>!5ev|wk` z2uzhZ#kXb^u^V@>xI*oo#^x}V{|6t!fT!-#2|+n`=Z&N@zq+ehGM zudyiIW6Oe~I_cGbH?Zk<6im^51Yb<%(dT$omTsoW$}Vo8&KD)*_re-FmKKO3p3cQV zW?SIY7lD)ftq!s}1&87kBaGNJ9ec;5fadG#aAH#nG@a|_zq~6Xy*vCTqAs{#aRJ7T zT+iv=t^&uM8Qd!I9*P;1%B7}0qthO4+@$V*yx#pAu*t;0qoL?vCU zdGLwmygR^!@0!PcIo~0*+bxvtG!sutjD!g<<=O2=Dsa$S8=a@uft*i2Jzg&DwgD}grvf0X(WGk~@7Q69z%osG9WsTE?oQIwA6`oT(Kz`o@7MPL@-V3s1_mXxp z{{&4GjZ9+_5yjMVO_Me6auOUHYV2iAF11?eWBaU`80wx62LmVLbm5u*yugH|Y?Osr z>)ydipJ=AGUmjQV{)a&qjli1ofYt76C~BYvY`mcXExQeQ?SyB%>!y{sCB+FY%{j;) zSZ9sZtK?YQmI-vo?1m__qk*gJYlEI`MNt3n2VM9yk)@w5<~mj^ArEB}Y?=eiPK0c~hexzUt0ya;=udeiEKhvo@%?5yM7{XR^?^!Mt)}8QoRY zWvc53Ly*t`lzBOe|D$EhFT1dVs-(|xqY_ei#mCYVvqF_Ewf!ajF{y`s`g*gM+cPQh z{wTa~PKFtO?W4uXhv-V!dnheELn~%k0-r0xnSXBQXKvd>&*S_{rJii!>~EZb#)I!b zSycnuzJy`k0Uk0{X5rEG51@IQz;=BRC*Ihw0_U9{j9(v1gVCwYFuM3W4Vcnd#=VDKS(*H4*||*FYcgAA`T7s@Aia&zF z!DtxA0@)YuPqey6aPfA>GwEb$*7)Knzdxx2bp4zV^D4z#e$)$lcfi=VUeO*~p5V}GvGP7gRU9Y)>(P>l}&=0Gxb4;V`1BA6*T{A?PQd8LLwR>TVPbz7D z^}^Ju(Zr`NgV+(4IHuAJOgv6dv%4IhMs8U5*^`2W{+!&dTJG-gb8xT5gO9o-crX{K zGo1yyS^F*%Zm5|hvyby(=|LMIp#2tf-)e${uUSyuH-zc@&Lo+khdJ9_yRdcKM>w!y z5v`985_ZmiApNi#la(k1N#_JCN*`b8?coUkK;>LN>>Kq>y7DO0qWEAf+%!c(0%0=9p|{jpK#; z?Q1uHrq}TA(;7%9Ttod=*FgQhX7IK@!pq3Zu%(&TA?E0G7FiR|{&V?B^D{2c2*ndn zVmO(hvo`(89!MrbCcy0q;?tw7@!3&d)>obd7e!}iZHo)?iC0N!%WKX>Y{Vi47~#)` zRV+O=42p%0<)J8Rx@C5duXg<-8dfln=G!Lnd0qb?=J{**x~D+k_!WZT-`nJ2q$04= z5A*Z2N7J?CMpSxy1l@S;ijyrrh?A3w$+%e_&Zo+=#}(t)%Xu!KAHN*+n~FpmH%M~l zpEpu{(n$8i@VY2jlVeH$T?U^gX3WO%a$G3iIEp!W7oa^2Aea&k#@ zHJD)iVgnr7JcOBN-yoe-7na|0Tpao6CM9^DrRNiJXxnSzv&Pu5tREwgwVUHSbiu5= zU*dzS*Hhf?vv6{ix6t8=LM}mz$seu+Z+v8zVM6>z>j89TZ46x){s0Ecs<4IiIf8pe z1J9ou&$flV6Zl$Eup!<6g34zK4z`n&Z#{sS)tL%Rb8mEYbZ6~WUwN3efm|L;W=%`Z z(BXCdFm@UP&&4rVop`up*7tXGASf6w%}ho;H(}l!I0(nuJ)rZ6bJ-T}aCGWZO%Qr*!`++nJR|$NDS;+31vbi2Z;pXI!x8w#d9j!*A zWRzIgonQ3#Pai0>wef7M8BCPd!=v>NxSN~J^Q_@fWiaBGqb+T{JA$G)dvP^LS| zbtDNsd4i(1EfAqvM14YDpp6xW7u1TK*FZ+1TOzkQpkDm&0xuH;L^ojd+Xgw?~@S*SB z6WE$iYrbyl5O&$-KD4i_5QSbzWFw!9hI(lsmp||c?aN#XA${+lM#wUSOOInRv7YR` zpADP7*os|!Ai*ZuYq5NjaL(n~0PfLzAy2m?kY*$ZyxAAu;X#=*RzC@%`i?04*||<6 z`)3!|eZ`JW8TtW!j)S(BL#gilRJLr$5l;HdTnH~5$hBDOv2Al|;oUo9lsk~dXL|Ut zn%T+VI5=Ej1g6rc-TE}_uL~>O^_-jCA+UU-1n+C4J9F?=gjd3BBbkrpQ@&a=pSqds z*P}iv7~;?BGFFIFjy@nQCu?RS!Ncl>f?G;FkT>;`W}7neAh#om>G8$3*|&t~j^Qx8 zyiyiF4v}FkuZN*s?I-cdweCz&S%)nwN~6P#n<;VYUKkd;hf*Dr@Ip}_&JBr#u5AL# zB`}3T6B9+YiwrS4au!$(h=vH+8_+m;A9#$@!P5uku)0)7_UhL@IKFf!>%JR??gj&} ztXqp!^c{z;_!7wJjsPFepYXLN4W7IV7tNXfho3Jsl9~N9&b0Nko9RK zQ@RaX*=87F@rrCpH9=Ee-Ogv}eGbQ-fUEB7i1SDy$E?F}NJw2v+ze#wu^A~ZyT|Jp z7J{|55>1P>;u}Xj1s#{^ptE)#NDey$OYXah!zJ`!|BKx?by6E$_nyj{)w5`g!v$D3 z!JoTusu=b;ET%1XFKBbBJ!d*^C#r4x!OdK21}W_lnE&Y!?44`QMs3Z4NSVRd)!@n3 zhC1K|ZC_UA-2sOzoT(;jk8n1>fGWupGA{W?`xKCRbB@!?KqIVL`JE!m1diK6Y1|=a#BaFT#hKb)=MLVE0ss9s=&w;n>DmKb zq%?Fi4$g4{shC_)Nfxo6dyU|3U>XcLzK7(;s&k3*#92Si2SuM~Xo+!VN(w*W(dU=& zR@@_M&e;owd(7F7SWEt<_&+@V_OfVH+DY!6rjQ?EpZQY-I!ry}2w6Lw0hw>xnW#|$ z*`8>c6(*vU(=LPa-OqHlU2r=+%7Se^7VN+}T{54(6Z9slvwszS^f2WE4cBnTe5;F; zA#}ehWIMPYL2@kEAs!k+Ea}&<%e)xfhHXI%Q<7BRahiR&vIq5JYKR{Xbx-;*kIP-e}A zHX-J6sNg;=aJ6Dav(4Cssf$Qu@icDYwnyS&_hnf--J-xP^31Tpn|2F#rgzD|V9S^N zG`KGwm6Yva@t<_k(lUnruns=K`4K5i`2wSYWY~vq$3Rj#fSGbJoR?S?PLIz693M>@ zz9%3qX(yi^HWJr4{5<$`LVsa#Z9H*mK;!L9(oi4v-b=3%W+cXbf1)^QRC@FEP3SVO1%=K~X58sjZw z;kU{iu&c`FBwa1>#|dj#eO?i~5(HUQd%$a_Gm*T99vxV$&7spsRQ( z9qE`&{WI@~sQw@<-}+j8un%wqgxJ&#E) z9mhm-q+q$hcEFaY*!XoJ9G$n5wv{@%$Q^23?*Q9xZOVOZSqMHX7tUUU#d9}yIbq_2KV-6h zHo4uIL)MdHNoisdosn_D3toS~Psjswcjv+4#A#4}X*6_1kEiGH$OW5kbM zXg)^NJ6i$GJZ1SuNe!UiDr9eaVp!Q61B#Fyh}G+>1--Qr?m2J`(qxsyb6TFkwP;CP zsHcfC^Nrb!6Q)?aMT4%~l)&#YQ(5hdTCRKkJZzh=ibc6!p%AA>pr!r<7JIppqJ=l4 z4>ke+L$x4&6N8BYvnRi*lu9-lWA!6_W}rMqaOvr?Io3&(CY8x}&C7J}#Wt)^+>WWC zaUk2ag-tU8n7!y8UG2Wga|LEJxnc+#|4a>CM_Hkdz_2r-2Dqm(n`MpZqrFGQ;xfMp zm{x6$vk%6y)FrlW~S8)&Lt*3&)4{42+3;R?ijjI>x;=&t! z1f#Y?!j)`L__q!!cXh*34K17!u7N>%W&G)j1L#mk3hZ696^?BD!%Y;8Wvz!wpwCHz zrFov>rPPmsy5>&18PBx^#+-035SY4)V_Kl!R{~bFf2NN!zw`g1EojK)VfbcCDvYu9 z#N5oyc&H?SO8v)URGa~uBX5Oy%d$k1GbZ7A-iW2D9N;p`j6iGBaF+9LA{%?3(cj&D zWF?R)OPN+MtIdj@(^PlO+{_L~H+q-HUb>4CUXdlmcC1frr}WqjhD?W|7X0l#fp zH>j?94@$LlBs13*$3_c0fKGwO;@VEXZuZg2(}EjkR6M&n@DPmIA!LDu*6{DzCg8zO zf}_`19V2Eg#DsThXf%8Z=}(-DnyMBUdd-amzkLtmDt42P{5^^_w}YVH@_5K*0{> z57n<%!L0|+_}=BhIpk*x*8e!RbaXuxcc_qU{s3kl##0m(Q|gyvw6NnP_pWXzbD0s( zR31h!%Te*7QJ*^ba<74+7u9xnb;LgIyTt@%E%X za~Jfz^{0i#ZY;!I4$Y<9@i8wjEeF+8&md(s?r9=9rAx!MPba`)pdU*WvcYF0-q4Lz zv86*KF2mH;4463NKiHpINE01{@q1dOSbxPcDEc@9ls_A???3lZ+jK)b{eA?_mtBU6 z3qHZ^hzrz}QVkccBvE&qE9Cy|=c2Sd+0nhG$ZJN^sE*zA$zV8lZlMgK!7t9KeiN=v zeImN8_lVw3G^X7{?CFW$Y<~8J0H{Ax4+cdQr5^+@Ql;iSzH`_B&dMtbzN@T83q^tP zZ=#L$&x2r=f*GyOxh{H@phSy@1!I7LI-0b4u#)j?QusCg zxcXq$yJMlVvSes(*f@$esG#FPrXw}+d7;`%H}*+T-?rFPmE#>#*U(qqt@aL ze;uK#TnawJ!?Av@Hp;u~VIE6w(c0|iU{m0OgNF{sv<>^XBvUajzv&Qnu{aH#zn$gE z2fASJ^HFTjyz!XfzXNZdltRDKE;0@?0P%Qt`epSMvOX#@efQ}Y*%L_*rKh3iWN+b_ z7G^ve3t95sM3`5?Q(JF0Z#BM`u;K8Ji9Br zo24!r^LHkVU7HPGa|EWPLn1lbDL}yf*+TZ&4fKs0=tqb*t_zW8U1Np{*Zq4CU2+u7 zmn*>5WrNrYtq2NJR$`&5$W$ipffo;zV}f?-xUR@L;;WPXjl~=&>h#-?@dUd#L2`EFoK$E^60%z<+1^ zK=IBq(WuMmI5FuN=XW=ic6zsSmV?~kTdgFU`^%CI_qjtB-;QG2lwr(kX8}IU(?mCs zH58cy;!Po=Yfpg~y@;x5{z88-P%r+|~mC?+LB9z#?1}_>`P!6#Z7SP14LqWApj^#I|!{aTn zFSF~E*YXVl_c`OZQ~hLH>ws*w8K*L954rA+N1d^G6q!9AXYL({bys}xTd6w} zw#hJM(k6DpbR#p{`G*^y+CW8Xs!2t&4T?7=F}#cCR+J zZ7SeCS=+K-CZo{EuSR?l1&&#fD^xE_;v{Px@cSQB@`K)8gx$vm;;hxjd9CNm+4o=* zJiK!o`lM~do`Kb(B|#JLQOpXcHcX@TfNk{X%{aWQI{|Ix`trePl1x%#3=aQ1mfMmV zk7s7ZVU=$I1kY0yUrag!4#q0T%6H+nm1d&$sXbH^RSs&a`r*+*eHOSx0Um2@<3nF* z3bXy~;`vL;`0j;?G=IThGK(-}t^x{h88c*FXSMO{^HRtSJV?q{hO$%Biun+M6&U<; z0~iQ%fQ(6Nq0{9OERbJ>dAoLkgsUW9ziK1@YRx_V*xWOu_feB~Q%^zL06DzBvRCMS zt)wHxtN1&QAfB)WZS1Q+C(0d`-Xa0C9Hgs+89`Q2SV;CInNoS-9OH9kIMIsGF) zdAkOC&?m?I>}w%N3+cjwa;kH2#1(h9@*Ae)a1HZvS)seo+lxHU?1CNeiol)obk@TU z$2{3aFEyO@Yqh`w&O+TA0XWlU5wtt1gYga%km?=@3p)PM8r@;s`;xP?*lrS2wA96o zMN_~!b3WTG^nf0kOhJt|_Bj5ZFz4U970*2!gU7E-q+9!aA@0=cJCcMLEN*gL!uGAYOv~<#Wfo3Rc(btwOwxx@`=s^EV|p6+ z-`IxVHt*!ts8(?KnPzxGI4extSJ8tC!AbtS4Zb>V#9c!#(6y z{Dd9l1v`9Hk%sLS%Q4Pz3wrK}W`!n88MQ>)8q7^)VqxF@@kJfXnzW6n1{kw59eZI* z`g?d0sf6*H<#A$u5=@l4&UL1H!7sBrrRHl^VdSV*`YhySULTjmk+$}@?Cvm{SeYS` z3m%Ur&TfN4yKV3}{})vME9NAB+^4>S?l8$l9zu>OLizI~95)~gHTGuF{@yeUs6E1e zFBZ|GA(_kZ|9np~GLsR8%xa3p~DG7@>pGsrqvGxhsy%O$!rA_=Am8Z~_ zEjXIWOW+y*16FTFexZ~xrcUUB?yGNLy~!k-M10N@1a8fTfLQ2Sy_3Eo{CAZ9Pbu&Z7R4 z+6kMDvrf4Bg=KNt5y!0N*B=;HO$#Z4Y`O=DA`nz4^C?_zK(pz9< z*<<0(pwBFWgE{&x15Ufg;_tcg*lqa-#=2?=%-)6k#U?2fTc4zkrgz-D8{@EbB@dq3 zPhrC@pyRpe?A^$6h!|Lak8b7DB?kjutaO8R9ME7-kADEk1Gx}5=as0tXg5vwwPEEe zqXm}lak0Cs;296L!p9wwF!xgw&U6=e?HYT*_}FkADTH6@>U-v%TaxHGmq8q%u5}dA2vcKcZ*;8NyvfvT!B7yA7(W^ z;D3tF!>`A;i{n~SX{VH?(L&3r?|n{5gG2*GRuZX1W&B8JC`DVtN&{I%G8*6eTvo}5 z2q7y85waUHp8NR=dMU5#zOHjV@An5&g#1?fkTe)Zt+czSng%PSuyv8LP}))sI$H$) zQ%Espf0Sl%+A-|6uN&KwSOP2l+2ViJi?D99z>!KhvpEUoSW&Hm%8qZL?^!b1KNYgk z^~x;E-WkWWX5w-=N4$K+9Gdx9&ev~;_+5t;u8NevIX1#TNB_?-)dZt z+yIoJLPHY}SM_G0;sGyaCBGlFr1x`YC*;5qZ*6cMr;ca*g?)Xa0Zv;s4Sc@sqp+1? zIv^}7!k$fork$5yl-Dfy)MLan#(OZ&8QG9y8w=|tK7j65RrcSUM2w8Q15tL`xb<%? z4fEpu|oub)x-)%s{4OC^P(=3BILr_#(8)PSa*Jh8K;%{gpx|(f&Ko z8Ot#pY=H35hxm0Gf4Dh;^Wbo_J|^wdg&%teoOKV-YWo2Y5-^cWXT79~PZq48EshU= z(*<8V4q|oHKyXw#33U_R^PYzV#y;r|vYhL8!_~fFrd{5c$|0Z*m)OoX!oJTv1NBw~z4wB`q)@Z4czlTFOkngrIhxBx{Wb zXL}Agp!21XY|UCbobW3Xmw4}`%?tA2O|2&c1iGTpkud(GbPoHmU;!IapUHjwsYe@k zuYgrk9k`P>!$?gO&F%|qqDVN`gR38tU8x?*y`0YS+FjZ9pHoR`kq#@^9DtX9-NNnJ zuV}Wu3iH{18m%rWa=tdV#pWa3vA}IKWnG%ehYZtYsn%xrrZ)g}-WAf8XO7HnycZtQ zQ%5~vx3T?w7MweA1Jt7vps72RYEP^sB@12j=rKoWM+6TiaKYm4b!y*p*zETS8mpOV>EIa=VE$fwrWg6GtWcHTQ8 zap(9{xZ9OUN5C zc+1@9RR3JHdsQ-&KiXc(<@_+m(xr?au~rj5cJC6hN2mn zCGILXGCdPUi_U=Ib2o-A_vr8H5~>z>2A${C&^Yocgm-Mh5~#oxi6h~US}fc2qlYuP zr^FT;R&jh-C-haM;EMU{QEsFWDI7jR8xG`fLH{YCVbTZ;`eK2z+H&~`{ll4Q?*cr3 zdovdpGzR6gZ1`buSK!*i2~4r;C>UlAz;b!Pi)lEXWeXgVUO)w}shBIq9of9sRH3VA ztsf>d9K!UN5A;G(Lg<`yVrK?P3cEjfyx)6V;I@qM)V44hTGj^rO}02k_%5$1t%IpW zI(#C9(agqm*t&YG@NIL%o?HtSy*z+%lT;x~-vBFh{c-A%C_MeXiCVhe@&-0J@OjfH z43FK-r4Bd1Rl^hE(>5VPt0=`*1!|Lqw>IwCvKL~1Dx&SBJ*1v{7zP<}HSE}2Dbcw}@emd?0+-*=!hc?2>`wR^yd5)9Jb%vH8p{(Z zu=iXg4ehib={;)5)>>ldVIAT7GzGF}IPwp>EBF%eXq@Ntn!6~R-5FmjG3>J`JHB!t zJCtPvkIr0%_k+#?&Ne`m&JL(@JWr$RBJlTXKd{;7gPt{8sVguT1H{!3S)zge@*7F* zzB8$J*}(qeXJBfrHm+1Tjs~j&q4QNaS95X~FSj~VVBo)z*MQ%m_c_w+Y*;C28L5$G zn;us$h+*CdcI}6iki}ev` zozGE~@-G_QxPZ%lbOx^U)?x23eJb8%fUk-Q__LM5-lyX{Oz8`UzSp57_s>pnMu+h7 z>LXbD<)!$qwFFd^?{Lt^lhJ@EcxzG0P4-o0j;4ocsp2ZQ@o^f$jD=_>9mWc?r^6iY z3s~l>$Ciyd!VmKqP5Bl(dAX^+tbfEzw!&72maHs?^s;W~uH8!)PrQSI@>Y=Funuod zoyW|}cY<>CF|p)YV|Js;k8<2j(DPUB_-jcO|1f=UX5t1B3AudM>y~Y~i_@+&8{LNAfKD~(V zIdc-8oz=rPAz!#8-itjx5yl2}n`3kSTnsv{CF;KCgUdHALek65Q>ek7-14H8 ze+HtUdBi323Q%vo(Dhnf&+luVO@Ur1c+~9)#H~!E_V5HWGd+gezy5$C31v+GYl@b4 zmgC}{x7?!EkJM1P8$v5|=(fB+xo`Kt`>{{>$?-@Az9@X=6NhhdSVEw$MQv zu8K3tEa1DICA^s2$vf|f18#8w`g2NH7QqSv8NLzba>&lCMBFJqXzC%J-LOm zE#O44aMyOYNS>W~%#NP}QP<@`c}ND%&WmMt`;2jUeF{kZ7>tQ45|J5a!g24-7&7%R z$Qx8a^mk{R-7yv{hRVZ~tP*h{WMc(%8v&47~_p9u9Dai+g`KpmMOGndZEGx4OW(Q7`umD zhm{ZKVf}M2zDK~BhFLAda-UyRd-wrGp0C0eenvRxyDcBRISA)1vBALU9;o{u91C2e z_+WV{k?(aEOgwb~oI?M=rvZcDz0y#;e?x|Uwq-XgD0IYie+JrVzwYHd5Y~-(!58x7lqhsUH@Jr~*-#<#b!s8B%Wt5m@Au=Ez&R+2w}qgRyL9hT3dNVF z@%0|sDAqAUnSGk5Kk*&NWRz2|#a2v^w%}y`OG2x{W;^3*540Vx020%Ovq-10xJktt z!t!)j>ySQ(EcVCayVKBP-Yzn^y`8GRkHq8t&Ab7-hkBty+3ZON;f+7XjN3BEPt_2& z3>^R+-tBa|P7j|;rc#`c2k)yhMLVDGw9P3Cj_>n^|30TvVSWl2uGhfr-(}FP;0k9l zI}VL6>=TWZFh-?UTi{7i67CaOGDB|}G%nl9r{5gPW5#)yK{}xO{TH_>@dLlVNZ?16 z7~fS^AtbqW;nC4Ufm>#UFXeW@db`K8F~^QAKlhH38{d%bH!%d-)bTw*=Bzg8K3B4L zFQ;@&nEU&WWN9CB*tA8?;B$2iHHVxL@4M0uvj#1Nd;dJ4`+^HS{wBo=f{sG|**UoP za4!CH+YW`oOnbDvES_u|!8Jy00I4~XsP?xydM00kN8&olF|=ab7aA%6HQf`;fiZsJdU#_i68tphWe#O+M|U50bq9Z@Rs3CvO%i`y36p#f4M zSavUj?^b+7qNlNfw?m(qZkEJ7?RsdVW6Cx^{sTWeGhvFNFh5Y!XA%odctt&HrX=uC zTPyn^DWq1kQn+&}y=xa3XnWB2PQpL?oJB>IEi~52jdo;C#RipP@s>YAPj#&)o-9#g zEz5^t@yG~R5i=0FZtS4mS~uJ^%7`iDMN@AY5ARPma$PfDLDwh?Ugp~k(wd;inqCV^ zw1OtkYkf~U0$0MW9!vK7*%9`u!2!#I%&@raD}Q&$E}EWB7&K3k$~CVF{*7hqiETKY z?armtN`2Io7{p3Wr|@&mv_tCqDSXVfRcKNF0p!1$(6rC9z-5#HbJwYa_Hp8Wx1fF~1RZ3Nz1b2Qm=S?VT>jzW6Lu4JE1#8~Pu?TAyl$72{ zE#c!>dX*d3n4HRXWe!2<-~b3Wx5V&ov*5k%U{sarzHKjPeCnis3|j(jtDL6XbYv+N3vjX#Zmq%Ow@*>GKH^@=RmA6Smp}WwShP|^y z!_R$0)6T)3q3igKuF}}yEM(it^62H1am;*@21cC?hj%BuY52s|q>{Xk;y!w_hj(6c z5fPD`X*-9m<=^1w@tIh@N}kMMhR|_g$W&c}@L}>w@~~>+A1pcnDOZvawo0)|FFSOd ze1UGS`#>GNHsYg0qnO8VFmPl&x2&!aqN@GTY{Fum9Hyd1g%dLy9fIS_3&#uL2WeV@5{x6wq98B`;vIyuuWXC;NOgbfwX9aIy+dc zhoY%BiMy4;EjePq*m8fSEPR){TW7Gv8`I&b;#wAQ={$$ORZ#!v5SV6g7&H!7!=acC z8vJr4cr`0y)bl#>E0JUCKkkQzG?4iW@dNR$f#~;f5&oV24W6(6Ngm0wYP$b6bHRd( zbj$8`I^VDcnhV-FR;`Pho5rBXtp!Afj)1pU6R^K~*bliasB&c{Kfi7OroVNf$b+_6 zVe*{Y?KlG_YaYSevOe&&2*A0+)A5#^18zN0&mEMI7k|FBl5dfINEhtWsqS zImwJfnPo04XK5QrZnefW?Jn$=(A^U$+&OD^Yx0UGZu8+8a(LZ!1l~8lLK63t;MS{? z!fdFI)ISovnGN*O--p?HbMPxQt))!W(T-4ppEM-wo2unt+=tl?MpYT><-37p~jKQ(uSedfL17P|a;IO<+|PRKZL z8z_dK0S6(e*PX?t&0zg*DJ;Zg7w#$6AFja!UsmrG-UtQsXhuj3=b2I07OQNuhMWfJ>(qzgvPt`bH5_Y`DmmxG0U1z0K=!g9B@yw&{!(Cjn|vc0`|(}#87CM8KK zKMv5Az7la$WGJ^G^e$hpbuFei8G@Fw8hj4d{5_8IaL^~g=;q^`tzFCq_k8MU% zZ|q6Byv>9uJbukv%qby@7Bj5L(qdbWX41K&DfnYcIZgMdq}AV>X-ZB9mAoB;zh1n9 zy9Wora=Qlt*BZ`Fnd~NudlD@3bPe@Pzeh*!DzITVqiytPdJ0@yhuAa6ojrCn#_#L4(||xtcs#8g zeCi&MrKwOR)E|I9d}8U9+y~O-oN5~L^_cz~8{9VZ54}8`M3Yv>!>EG!?3hg(`7TJ| zPjC5;G^L7R&wUG)`Dr0JN``_WC-6|4=d%|Q)$}{-5S>tIq9=~T`44*zetac$Uvyw2 zf4|_bKa6K@Vun%u-*#RDOR4Q*7^||8VGAl^`NfvgK_cH0ZWFmf7AvhO|7`K$pv87;U9-#Fm*@=(Z>o{lj+uBa&5 zhKkbf?S53%fX$~7u-l~?jziNm}E(rAThK0D~l|RaFFlP@ZB}2nZ0~%WG z3m)zr5O{DNX4KVkvL7Vb@r`3?tc)K_(GoG8n%UT_Y5|j!n@MGN72GQle%E_Tal+$x zs4q5vlj$LNN=cZ3cYdUd{1kR>)=1J=;KF^YeqcLsJmA)~W6Ahm0^54l7oDs!sPt?I z&upK@X z&t?go4R%%quvxnk2G+i&rwKQqcEBeHvX;TP z6)CiVbjaaME(8d(%wy!LDswrT0JjJk^nz)qBFi`0JeYuh}d`E(8A?4=|*>iB8RCJnUi2>1K!|Aa=Z+r08eM@+jt zoz+E;r=Elu{1B(XzkE>!pJ#+)ZuDd5-4cpF1y{Mu@Wps5eJENkNQAS7#w1xcj5!yK z$2HG`*?q}g+A7RA&NyF!1v`Ya8dfS(VOv#K5-l z_Y@wHT9ZdYU#Yh<))ai=p4=52X-lUv%jyFZ(HQ|M@BTn*=3JUwECIfDLU(Zh$NmX_ zhp&d$=-k(C=(#_cb=Rga+lR)`*&?{+bM|4p?N-`2+J-sSXRxJaD)_@}HMTm~v*UqN zxlJ9LLG`!~crE6bX|_CO%&P$ZMIBU`_y(G9*^`l_3cEk+o%sEse7L#Bi{kq&Q14z~ zO{St2+inz#_m)n_Tz(1nWn(NZdnAIbIexhQahu3lSJ(r6^2R-yja>cE2nzqtRJ7DF zid9_uK&4$PnbG(+HJbfbxo)FAnpC=sy}w|sMeb^sRl1_ zY083p2aai(#fv39H=@-yTefcYf1;=!8%SFr!M4ij(5JfVkQFzJO>W-{2EV7VF`d5D zY&w~p(6XgLPJ_tsoH7JEnUgoGChNjCaB|LI!DSr=Mk~{BxN|S}(dCO+az`q}A7ymb zZUj3hJBX(0{3WmcyYQzqn%;KA!iU#}s5({XG#)~r9cI9c$JpUui>F{7eu{MWT@<>G z4e3=yCWPPXhR}XLG?&+46Dy8DL6ri#S+Rr-&pQS??Z&djJBG2Wu=5a{vJo0}bf9)! z8_i)E%szS=ZM-#sO?W?!Eje|Dp6vb$+OD>&?UxM}79ED<4~_KwKsg1t1hU>y!|Bqs z3Xqy5!uaVa;|kCc z_cl?PZ;VLi8?P8Ko2_^FAGf}M#pL1Gwbz|wj*mfeuOyl~<_xr+wqx{Of#tL|&>lh7 z7;`-e&lp^Su5aELanY2mI<^~Td{;wDd!FA{Ye?Mrxom0P930{~fy)i(g6uGk6@Rip z=`uaGLi#5a{RB$gK82H86D3NqI!KxuGNEe($7%wSsO#7=eogFQIvv=|n~e2f#mYbV zh7a3V%N_@YR3cG=RyqyL4ouDNfFt!UDMYj4SNp7SG#8 znOEk6P?*6CswB};=e#KGVK>Q^?x9oO+qjoe$^7V&Il^4}K5VY=Ve=M@W<&NKnu{i_>l};`q&H-&*x(LGhN#LdZPH~xH33qDDXmsI_yV8G7bptuW1{X zMUtx=K+*9eY^dBv6K5@?)bh`e;Az9;{+x#3#E%rlRd9jJh3{k5W%%(?7B!2s*^G@- znPGGwNgNC%i|-}k@McHwpBf~b-AQm@?RaKgd<4=ye}GMLyI9cFFMP|`OxmZkhDuCA zuyREY*`6}Q&ttS;{qA!>mSwP@tq5#hrs3zPee_a)0Q;{ehQ@8FgdJBZiBDF-C?z>j zWcUx*^n3tY`bY$5q}%*?xpI|vzpNL zU}y10?CDWR=lxZ%?!g0k)U3qY3!a^3M`B zk3M~#{AE_t!|LI5r2a5Oi*}NHahQJJq6u8z#b>{LZ88)fzVA{Wz@PG2hz^1aT?6BW3 zh<%g?v}He4>)s&d=?W`nYGeFrNBT~mA>jylPY`xnoyl{}?mrn)p| zn*w{W?^2Dak_t--*#a(B(yVOxtD46`&U)+47&N7lphjqv*<$GBlo!Mjr6>MSAGBB829Ddd2NAUeAIx)L8GeT9Q6#3jYna6WsT=L9}Khn|{lTRX)7U zy%8PeN4;pJkZp{Pw+>@FccjxB$OGf+`sDe23u@Y=vxr>5!!_TEE{+RjuP2Xz>Ue*) zVUYwac|Mr=4^?5#TT`K0@Xh`aGNG}L?F6sGI+{chbatXOP0>^(wVy*+=k+%oRSyHzyM-bd&=eNCPlg^q-4y4Yiq&VPQCPRh&TXjuIw(eTk0A_?nb6cax} z=;rf z1&1yYPxSf#TmPJaQ~W6SuII?7jqt6xCuC%&#y5ku`xf@~S34PfISP}4XMyMU3_kwc zCvI%uI{5e6h5d=RN@iidVUfQCFTZUHXy-{{NyBJdiek>_vK8lYLIe)&$LXz<1g_to z1}hySaN*=>?90e8_^eHZPQQQ8?b{LunLZWVYhz8po9u$BaV{7)a0ErwTGOP6!_azF z28W5A;nvoZ6#P7etc5&aUbY97ub1PkYpZF;%n|JBWqBOfB}*oU)mWpX8NTQY;f%cr zT8`;qbM7m$IJ*x{YeZ9&aDV&t@E|FlegXTUH=um(Tzt0RJh-&1pjXBT6#L!~WpDn6 z^P4(hoY`&o=x52=W=~=#_#ScGq$=Lxh!;itjurJR9LH`Os-daDXRcDomOa|9jL}9% z$oY#3K5LC;2ksx^Pg`~Hf!snkW1_@fYyu{};lb=)PlJ${19b3g3~q23hU>h)Q-7p3 z-X7s9%!%K?l&yt)`ZQfu_-ufv^}%#FHTMwrDsvL#2@cDMn?g5UtrkjGoTKbMe^9wH zmj#Uf2Og^fgjq^Ct&MQPt;19>G3x=JH~utSJh%cUMFEqMRHcOt2Dm|C2eU120c~R! zD$UWt#UdvxSK11{3*^~UxeWGCIv%IrI7|@V8Fwr;^*2X z?E8OSc-Pbj)V2=5&VsY#Ze9bUHOIjp;hQ7X$f~(xrqKVekW=tcCJk`|q|FS*epMBm za!nd%|CfSsn%c}c=ZE0BQ>TCVP8j?>976-Hg0E#dCs8zmlexVMM&G*&0jn-^`syxN zf7qKQT>njL4ZqRK8abxEE15cV6Iryd3ri>%i*5<;;P{XrOw~!H>b2LXe#&af`Rva# zg-Ebc8pYy!hY=>6B-srYNzvmESyT_lLf2-p3T_vvv>}vhrEmfFztD?lji+%DRgnx8(*UmLDcJYUM0Ziap871DQRq!A;0Z1eHa> z@3-gpFKO!R$E0r{yU~VTy#GaG)Q-}ae-1c5)(}UWYXiG}!O=K(Ick-xKyQIn@YXm> zpX}%1An7cS8Dq%W-?VY20dqvd=1*s@$|BjDJ7dr#N)y$?c^K8Qoi@GLLzZkZTGy6S zn3)mNt9@+Owr(i9W8}?Mt_ff(w!ReqG77{p$7Ig$)lg{K5(T+OH1MxaG%WVm!j|TS z;O64_BFXE+*giHC=ghKWyM@`vp@i3D?J(J?)8#|zUk{UkTrLvz0 z$!DecIZ@NFC{#G(BSJyrjVYR4eNUlsp`rzA|I&dlpc%b`*ws2kTK@6?$?g<(=~ov`O7eMd7?Bg!Hx1=L}GsF`E9k3(pBERRsUf}hNaev)M3jW2h z3(B<%6P+GBlWUz3gck=W z!}0S2(esVq9O``zaw)l#=gi39T_itd76(orCes{&^UreL#=q*UAhXir;>o8aDDv)6 zo|mp7wN3hT>uxQqej9|v``5GObDHc1-#80N8y&dt@QElN6$Iz2=JEGuZ?kij523k^ zyD8%J5O(bPS$J)%fpIckXd}EO0`zw>opA|NbAKm|5pofY(<)%E>uItYdys1=Y=doA zwvgGWa@bSfNM5U?q2W;p>5SdUs*WEL4-WgnwduSQCEOp(c8?aEi5tS-;wtS5CYO zDaJNTUpU8?Zk$C;nRPTIcM1zWwi>qeAA~Yt?i(|(jmn#ZH|h9|^lDu+*Zt)&oo*A0 zXImk3&b1TRL3d2blYxA_>+rcEj_+J`3rZ$yv4@X8!Z=}0>hkb9^#7ZQ&zcvoQ#zW= zB>N9t_A_8Qq{B9(N#pZFZ^W7N4RHF{5U@#+V3E%(X|$=p&}Oc(zImQ-P*q=m%9Y;vxwG|8P z4rCwZ_6e-+Oe}ly2!7=hf?;X~T$pqQb{X<;ZqaO3APe+YrOKzytK?m9vTqDiaBUaZ;b<1zIETr248d}{9ylib2ki4F!>b$PAy{bx zx#=)cPLIRosbkqpt?A57>j8}%=tLKD-`Lqtf5`_rr-Q7OtA~ro{Xjn<#bph8cG!V_(7q2AwVM&dUmA1UHzRf zM)v^Jz0ai6ss(}zrw5E3+{CLoQ&3}`F1DI4z}f9T=t@i~)73A8sDP#Xy5zO2X=ENZ z@lG5XnsmaAOA7dV)JV4EZ4>15Phg*%{ov)rle~?sG9H>S2$pD_fK%ETSd$~q^hPhD zB>oH^?lggYZkd2<6clNAW<3PStFytSnWTAFgya58BeltaU~8+%T$~QkkyFLsl2$Bs z4ShyWv;27VH$w0FlSTM-|1BEzP(&V+w(@ss@;L?9Dp;o%K&Rdru|}2e^kVEIsA;b&1`I zPlLW4GQrC?tx@d=Ut_YV3NX`?+U9oh#sbrp@I)J8m%4%0;F(PKg9YAs`4l=U%$^~ zX7$qNhvwAxUIS8Np3w&Ld*TGGlknf`)qL-!Jy4o%h`u|$;qagwb~@h?zp6PxhtMbW z+fA6&w+Qc5Gac67Fkf_3HV;Bd-69Y$C5pNMw07dm*!6dK;_87e6E29R% zwO1(Qr7SVz;&~W*>Xp#ltB#NQ%BXVRFtDv2g#JE*@!b_w@)^-e=dR1J9=$ZXrq2Sa zHK3QAQ&ZrFf`|{rsrWR~iBp-i7bU}P(^_#k|9gNWR&-ZliifLM{%J2*X?ozS{H5U@VH4&xYzB>Ff~Qq$ht;!9wtRTE zD1_Zvk&A*Q>5b|O#>^_r#=C+q0EnXJq z-aJVqd*_1xln&6_y)Q5aezPDBOT7^cj&;v)wy(j-bYby zeGzScTLgE??!&Dy1yFF_gesPfz(e;g+J!0z&NYVz;OQt9XRffL_@E;G@T{%$@!S~f z6fzP0z4augp1^!3f2Vt~-?-bI%J_L~ABnCGMK#~M{BdDE()BlrQpc>P^T}(_J-d{Y zf`o2z!)$I`^D;L7=M<97yi5-5&>&^sAW|_H!(p z`f-WIEKgAwd3O&dA8oMgx~PNMyj_6a-!rSR_t;?0Ir<*F z>Zqlz!yIh<79g$?=CH2K=V0xSbKJJ4b&%>OWZ_GOGta4h_*2(}T)td@S$!(PziB^P z{+|R3SQaEoSvr^vUf&2Gi;JK-RGY;JeA~q+2FC0Df@I`v&hOeloSrZNcfHC6yQ@Dz zxx|t-u>X7%?5SF-M7Nr}iF(sSJoWY_eqNC3~3eNW& z(drqcc&9Rgnm#XJ{a0tfx|On6GjIjoE(~Btl77yfHTYHu}LNsWV&-G#$~$Gmr3J<*?2vCN-AOlSFRQ9N_;~q@7{AA zGtZFQ?0tMtL2Ql-rI`F*5pH+YAf~VK} zxt@=nm^`#p$R??y1^!`%mY z;~DQE`h+IdRUYRpM3d>boIWg`>joMQp^R$AvY6fmvMBo|n&LGA@k%QEyl#oRH(Vus z#o6>__#o8TzMMR6+lzbqF4AgPi(Se9&t^_yw_SGfFEe-3H1&4?tyL8NqnG61e>5}6yR&X84%!ozJ%p|7NI+^|W z)*`qdc2aIHvL8OVbZ&JF9`xFVe3v7)PF|J`F8>Oq`LkiQ77u1IPT2AA86}w|;lV{> z?o8|sxKXdm>Djl|nassi4U6>Mdy2;}PWW@*;mrWhE8oY{6O8V%RRVf)A*@Mpd7d z(aGWqQ1oLrwoY}!y5 zdYP>S1Hagk{IeAhGW$N*mn(t4ycFe{m{N6L5T8H8hyPu<8Md9M;xe{+;hpjc+_K4s zV9lB>OtM*l|Fi!w5>tXKcUH-j5eUb^qDNN;jr_)KD)49`RNt zehfth+2b2a@aHsO=S{?*d^3%V^t4#o zErp26=XebAO>bsb0@8%&}A2c@h>vrL7F_8h+gpC&l`Zv^TP$7 zmSb~YKLiinOfjQ(L-Rj@Jvp5ONiP@T@%yK!>SP{&H^vkzWEa5iG)cVv?;pH2Spo3| zgRye@G@N9az&WgZ#%tX}f!Pb>tbZtDb>(6-j z7fu|e2G=*GvDJB>$)iz?^#y6LsKtADXMsg&X}Tr!>r7zz8%)`(RsTfZb0#uXt0)}K zj}_nVx=C;U$+9L#8Gfe37vWtL2luSj!@Vy?=)Ha*%8gNEH_Y>C{riiM_e)~fLTM6$sni<6n?woKI?n@CX2^(ix~M0NT(5d@9?u` z9Kv@0x%`+#YApC)gZQ3*98I>rKx6N1hn4&<7@2TS^rtPJ`unC~^#BdrJV^polv81C z_fcr=(f~bm4)5v+-~C5YSbALOS9-h+k9aCE-vT47xm!r{95z7gUEyAPC6QZ`Uc)ZO z8|e0a9gSN;O_=`2WL_p`K9yfs1GzWKK`i_~s`{P+5BMx81+Hdk8{8W*87qGrr0tV`LE)W&ysvf$z72au zceNM7E9FdXuF_PHK3Gj%DXpNfavlpzzs3za_M>LdkgpJOq8vwsG=OixWca5gusb`Y z(f`j#=6C!aH}KdNbY53RDMOLc~uR?(6D*gge(7arUAyP_VlL;e{hc58cm>EO+A~21&CM zA9A@j{wnPE8D-S@a2^-tW?+`P1Lf*laWg8gqtu!!BxyAc2Nz{= zU89TOK*=$~vb{yv&aU!DY0aPKf~`C4`1u6`eiUG9K$z4JC+gI2{RW@#e@9YGHbm3un#7TDTZyUo>b>I zr%>Rd9UvdjQ}g{w4JUhX7HVt@Aq`2vhyAnxPGmR2N?$8nT%ZQkrWKGl?E;zPyadS& zB6j{+J0$wu0js)1+@`pR4YL0Yr$?<5aYYhfhY~;FT3hYs7xy@BPBtX|)+U;gv1!xGRaR%GG#FT@!OXCvq$N zU&EI>Wsneg6>jb}$INLD#q&<3i7(sBV5jFq*wy67Iv?-ijQ1|)WehFY^e!o;)hiEI z_RHY22kB@kw+tKHwxeg9HGA=-0OSWhr@^mg)47C0FtuBP%}BlpJfH>a?sE{zuW7_*2z(Vc48GQxq9OhztpF_F5zwkRnl3 zL@6byDA8nWP^6JciAsf15jlH3l}d>Q%}R4pD!qvYq`v+A1^6B3+0S0`P^k&a^eB5qTBbTxbhK20q`sD>~cwZ3>D6z+E z`Ftq8)+ln@ZHTHzHgW?-*HWd0s>rl>9QvlzQC{_T8X>SfPTh23amChnc#s6iU)BV< zUrKDvENKRy3!e911ApxuA9}2uj*DGj{)R2!HS;!wyPqTo%%YMXJ4BDAi|BfOEhxT{ z#b%dMVK;aa8nov_!dn;6jXJ?C+qDy7cQwIq8&&j_m`P0|i$QZ;J1p>$#~Dg@V7j3r zu1jp<#N#U<@BU)4-7yFqtjsu3i89&kzg@OQ&w{1oo#x79@Xr^Pgv7vX3(x`C}QqpBckRc#nbCBNuVo zonxu6z>4fW?6EL1g3G1XdlIZy}t8w2omYZ=r8DPzyly`b|s z5o&(l;jSGtM^BLnGuafu<}|iL#_9PWle831e%}H;ajWp4^iKF~DRgh07Qh6Jd{Pm* zMXRmG;uA>|ti>PDbznU@M!9^

z>J&dWKo=g{?nc#X-?;8Hd3IW& z5i0+m=j*uxPwdPw%UmB{tuGNRdNYys1~k)Djm6}sW6n-*ABpGYPG-qxMu{%?O{d;z z5-jiRBDg60tX;X@_(W-{L$KveDLoP$Zt%?vam>6og|)Z6f1E z(OC9J2KP++3>(H8!REAf-Y+d2Wq&I%&$9`bGRsI{KLCl{71)DU0xz)rIOwhS;4dDT zh94iPa3zx^V1d8}anNYxG_vx!4+rfq%dQw6hgh(U8X-9I(-m-*`X=-m-0+u>M?PBq zk~beHi&v8#LF|KkP9Z#o4V|!+YtfEHm9(3DvrZ-@yp#o3{~SoXRmnX`OJ_RSr%7+~ zIIPW+CL`VNl;JoU7rj2`oO>ym{dp7)&r6%((U(vB#?IYPWuQqG|HYw;ni;6XD&Y;Y z0nGd63#f1y&pb5}L?ie2an?raZ0UlR-0VRacr0IwLEBa?X!;^5TjWb0Z~o!-?~B7N zB~EyG#}Mq<^F_Qy-yRR;%ChSg!u(riAipE+BcHB!L9BMXoK$ZcGxr&()KQiU>pu*} zA`J^#qOHpxvh)O%k*iShPzx^?GZ6l`Pr$xWia1&=0_P;1h7-og0&8guO;1?F7k?6b z>2^xkJ4v3&M95K$c{W@0n?q%3RXmtykK<2T;)=&r+|(mAFn8G~meDT61rMlGa9R() zC-Eee1k8rdfs?6toFe`$>Eo9!{tPzT^61}@D}2}MO2O90#OD@dqMG>_NUA_I7j`kK zTke6I=_D+#`pWe_e+h?WBjG^&JaVpD3jMbyaw$bJRB-elT-?*dO9l0VRa_mF=T~v^ z@jEf;pAmDvuoYg|%)nmzICx{2MYGSprNf)IQf5Q~lZbc)Ki@s#O17G?!%Fp{tHF)L zZ*hi}BCGM_a%coJB@bjS4R+9yTFKc3r{eMd zVzGE-1lTm?vb2pZ?Bj@Y6ri?~QfAE;{B?3?x>EHpG#OC$Zf#o#5to zD~O!C5I+=rhsS~^`lrxOY*_BkoB5cb|GN7S_o$LnNOB>`wZ=%Tfowxj6E{D~mJ5>1 zhdrkQ827D@Kjkq3;QA)c$)p8tf1S*<`&Uu;iXHqt*%55^+pYX%yKXuhHHc}8m{xM5niuFMC@k#V5C=b>ca^zPym+7n!I>Z`l z;os$Ee!TK2c##~&M!P4Xnx3j~*6YyI-9_LUqltQde!;iV^6b;TEV2^vrSs|+G3kpc ztfwY}oQ@wvn-CpQfx##8GK)bs(>YjhjTcL6aQNP%4eAuM_}fXs{N?9Xrzi`?k7y_Y zwR_%N_?`mx>yaADCxlR+#6j|k7=v1egTT|Ii?iIYjQJ>J3j6vzDDE>rr?tTpS9u82 zKN?_G_idOp@2PXS(;&8bv<&+wGQh?$()jk8p$Fi)XtCjMCs61Qga2Imb~LsK^N&OJ@t zs}@4D#bOwuaf^(bd_iK)OtvZUA9qZv8g_U`gYvgvZvCpGFeuxFACw=C*f)_SM*N}n zph(pIt|L571IcwlDV)8t2>Y~)_=9(}k&Pnp!89Qo*s~wh`sdK_Q4wfbB1zVHwp`Zq zbaa@ehBE5m>`GZ3oSSw7+|EtGvA2?GT+j}7!Muj|n%4jqde-6Qx_Xin_)B}EMW|{z z1S|WO0r*Ts*)b!Tf1m;z-m8w9ZcN;+YlPn-M5uB=m;an3Fz7}IuCq36oVC8MX3Tg; z7JpQc4p!)kW;bZz^HL>NDV&+t2FB1dfh&}*-4E}gw6S=*JJtUU25Xm@ApNV6UhK4D zrn)Xz?sFexcIUCQ)a zb^qGQzvNS>$88Mxc1JOdu6la7b{G@6E+ea>jeO+5tqiofpey+`jrVYZs_+jN_R8*} zIWhw8JZv4`eIkg|HLRHJ?_~P?K22PW);Rx#IvewuUrBb+APQSLg?I8uL+Lx(+{XAtSfuBGa+UE^b|H)oIcs5h zTcYs(Xp0KQcC_aE6!t2riay6ybC)%nxYaGq;$?&7SoXSuH9xIV$aHQLtFc|m#s?XR zQ+v*zM4WGnMS7ty+?^k%Hsmyw|N3e72&(S3DF?=B2PRCN6 zap1C#Fj4<8BuWp($HJ^F)_N)&$1_k-rwB499azuO6MWD4818K8JJMNgC=Qye&06mk z@Jo-aV8;wwxqyhD(4Jk+)Le`8@Y%(RVJ*z!Xk2`f|rqx+!YxS?~%? zCI2!FERY)wJ_?IO5$RGi)u<0D(zm-vzE?!eZ#x(_SP73#t>nHwog>@>vf*{75{!Jf z1Hawh58sS>h3rfTXQ!2g)7}aE##j~BQu`A&PAh@rqP=8c?at633qGX31{L`1GV$~i z$ejLGG%;WJ+b%!giY27jvJvvkdf+~|a!ha^zy3;s@f1pb=X&`0E4JVKA856gkF~*(TW|m z*!D$mz$+OsGgU^mL0uGmwT$~E+;Oyy4#L%`S7`kNV>XiIvc{AZkoQT%WPDRt;)?~W zM(l^*E4Gu!I~~{Ro6)d)>tNzj2i!8Wl`NOEKvRej7?pcsbm%l;W@^kzHo2jSz_RpP zK9_Yj9E3j)j}vz&o!t?7Lgnllyz-w--rq;^?C)A!9)19nx35Q|+}(VyaK67?qyitQ zoLe1~0#3Jc=we+Sg_TX>HI!1pIx0ojr}=_(R*p-x)p?qlmJyc(eVhrZC<2 zo8VcXu)Ch%hP#I=!PP($m>_t|S1cZbS@xB1&&nLXHS3YxqJ7ZuCmNb32jgAuEL@>% z%2vJ|3abaNg>{lX5Z58Yl#SF_lhG;h(bePFTYZP>l~a@0VU@KkW{@kBUvmxoh5ni1 z%_uA{3gr|UPLkoe3eptZ3+H}>qo?@~==c5!k`GrfwPYEVHTxsg$Ufko)tnK+uFKG{ zWe3yzFA8NPcEI_N*YI{&A?ZpH^=gK40~e+d2F0?OqwTmLng9!xs$qujPI9>NlrDuO zQ*Y-B7%SQa>y_QmdFV#0Q9J@FmnuopU^EnOcNVxwu^>vlO()l%=bfBZ<2Sb^3h;U0VtIV{75=rCd0?+YIMh)x(>`f1xvQ1h3qCO>}gGA8#t+ z1P8P)oMh``UP#s+A6^KV3{Df;0Zo?b>ZD0_R40oJ=lEwTk+A;Dd z{XX2vm)<+i6<$aoWp<8Edup=E7Ed(PU5%R0-^1H)%VDrGN1m;kIIl)<@+fU&m>GX{wdy^4Q16aIdFM%CFp9)z^MtHHHIMzn7liKR~55psnClx zxOfs=p9HW989gSwX#!-JPi50i7w`)%@8^^bsEg>AzGaB#~Lob_8I`z&TuFauD9_{=_RR90R9z#bEhFRUBKofbQ_m zC|J#f6|H*@%LBHs756s6nqLnf_o@lEsilXsEsDsqcpF7toK49phD<_9iv4;e&Az%@ zqJ`9Ue$4?hTr)}N!Ig(nTJ34Dsa*|nz2jXPAXVv-L_qnF%6;L7Cfcd-9 z`GLcu$#(8@@n-uyH1v`suzX48cD05Ar**^VfMl@u8Ooh1Rw0YH&A3l~6ec7d;|GlC zp~=%f(BJS1{z!i`v;=j+;J|e#g&EwB51Sy>ZzR5{e@&hvm%{Iy;Vkr)C);)<2iSmO z2zFmhr?$GWfOkQ-R7Z*LTxW(&r5|9UoN)HRZImF2#$7^hRCd*2_}Foq4vom;I--o(&DS>Uck&LLDOC=a<1*0V zwG#d*Z=@?mUwHecuQ)Jm;CDA47R_DS!I>tc@j*ex{N`nwDD}DtyGLCD8{jM-kT422 zrVE@}<8gT3{|aQ-1+gZj8XA!}6H-OWXlKz!%2nlL&@@W$QEZ0KpEr`Bl@T*vmqi6y zviL#C39{|{VEUv`Sh2{R)w{L9ipQnmFhyB5qRyJP>D(@w@FM}2PA(ylACY@tO1_ru|r??s{z5B{8IJ-Gf>#ye_DBYj^s_QiaN$0pHo5;mqgj_VNiC&ki94{# zQI1s#_b9JD&uHOb88Uh4P4gZ(@s+6~=-|@Fa7}Qsl{fJ4LVXmoZWZ=KvQ98yRtY>_ z;>C=POoqYdgQ3l`jJv-42>iXYp5?3((>irC;rDB$U9UEi>CZ28a<>)RbL%RkzyzG6 z{0p{OnsO?l%QQ{MtG-h4fS-1SqFWm z{${4|Hk<66`=I*J32-k8fuQX#Xk_IkeEMh|em~Ml-@}!;1Is30o4}dhE#Mg=K5vFY zQJ0|F{3V&^8KRc4Ei>OQFp|E#rP%C^xIUqtOEZaPhWrt3SL_O!`n?!Bg_+GY*J7%< zt1h_C6j2m-ni8XTa`oq8Sb$;}`%`v+c7(N&>$*}-@7Pl2eEgi?CpZo{6+*AKeWtDu#QrTQn_OYZKlx`W)ujgu@ldeFck{Swine@A%V0g}-CzyX_A@1QsL&q1IWPtPq~sVc?{ z?Inlt%ajjZD0*l@8KI8s{-s@Dk@tu6jPzJi&qaFlQk%65Zh=70GJzG7fwv?-^1FoI zim8V)<|PL31#bs1ZfOFW)$B*@TsoVs*uv*V?I#P(DBvlW=UyeEK`IkZp zYf2-t=x02c4H3-^z76H|8Biok@Ml~&FRx;VzL^|Ej=j%mCCy^Cw}xTHnX7#2<#XK1 zgQM7-!)DO?(f~WhmP7Ti3UHY+0oBz);P>{StbEAPn#Dit>CD+sn5k>Rjz5xPey6t4 zo&FrSa4Z~E-?Y#dU16VK+5`&+6p(MFCTG7#mG12Ag(~CI{KdQ9$=RnBdOuI+l7A0^ ztC^EX&vmx=dw4rs7_%F0hUzo_l;iyKw{uvm{&1KXuEC^cj9`tq83gx6i)=3*qGcNv zgHOu|Sa<$9-S9s}-!pGec=TlEG%=PfFrP{%4oIQBY9RZ4#~2j;ra<`eD`YcKn;!qN zXT32gEOyU!K4G0UoH;oSWtZ8qcRK_xfoKzJu1}%f2MrX^{1r0qEM-5AF*+vXs!9W7 zX>y1vT4r43ye28*xQjMe`bUFZ{kD}I6ga$E->z}{>x3C*YXSr%7J%K-M5?RHQy#!VyWF87a*wg!yEYyApd-L!f`hnICocB{yK63apqR#mtgg z=)ko(P`Gmv16jeJRpo#~?nJZTH%?rwo6tS}Ylyw;wDD;DNc1Z-!E4v&(PP>UE?Yye zz5X6g~O3bzYB^1ahLhsr$^svs9-q=hMvYiW9 z_0U1U?b*e3by%~3f0c0WfuB^TbDAz_?V+31+i8D+0XPn;5|4FQ18d?l!8XSVCQlUl ze(_si*7M;^>fLFQetu8vFy$saZqUWMFH*4JvMCn7d^8oAVfkKD4D5-^!C9kYj6 z!p&QO_~NN9w(pvNzTJU%XI(8^+ttO%&+%v92%PSF zQ8?4!1V1C-B=z$Buu+)(z?xKXqf|I&e?5dP)R)G=iVOM2VZ(T*CLgd04H0?}s%ZR4 z1z(*OdO4l#{KlRQlh^L)a-n43DCO1sAgC;(mj)VLEi86oa;h+nv*aHa%W_LIqqQ^_J>$a;{ zQk?MpG`hfhjU2!NWtOw}d)?$1uR|5*{=hC9H`W@Zi@AXVaA4LbcE%{S#xF;Mxld20 z7;Av7c`e+qRx6rd$9OwFnofo`z=7Jc+~Dq1bh>z)n|$H~w-a~JwEy%d4)=@B&oZC| z&%SZ}wUHuz;0Va8`vfa3Ti|l;My{)P0lO7*ROG+7S^V?6B-fyqOh2E`A}8mo)ZgHQ zrQ=eV=OhDqEx#2hq!1bn75RTB6uHs46L7w25WGGWL$>L=VU52kXS)0lU#t3s|7N0q zcak(%zhN`!f4M}*vTazXbc-n4_KQemw;}tp+5{a0hE8GnS{kryJlu3n!=m1ww6aT= zb+*oCuY3eD8Ub?5qK41|9eS>igV$~vF|mm3zdX(LJ8&;dvj$e3-CQOftK$8 z{z!Zq8D^>qoTOgJcp&VrW?csRH-fk1(p$2hrif#sq_KR15n4os2+q3+;;Tb8llm1! zJhNajx$GLt{x;drM<#`G5i^-?b*1RjY%ipLv+=6Pm6hDLfzL)}d`SNZ%18#=d#0GT94ibiZH{HSO|f-fDI8BIqk@%v&@6Nm7XJ(AgVa^KhtZ2bXDT180xuu<1q@xXVs^Xtih%9-Fv{R0f#g)2gZTsND!eH=lDE&D*hT z!bMnC+eY@di8UBZ$LETl#W4ZlOm0x7xNeIq`-$&}OWDj$2)iBb>=O_Tk-$s7uc=2P z40)jkDS7jzs5*NcGdZFU4;qAQ>&g4DI@*ioT^h#XWw*i2&tu2DOBj#u{zXCEW+go0 zBjm+%4cIHAOX9~G)IR8Z0m+uVrfMF{na8Z~7+MS(DHM=D-Qg;{G z-E$IH{ZB~qL;^GK-pBtC_^yrz8sJS{43kdTjQO6kpz4XxcP2S%C`jQSC&p&o47$t8#6K-BQO7ME8m1A*h8!KtCci#QUp4b%4Ic$KihkVfR zk#pUmgzF{LShj2<^}bn*U)pm?;({LglOiw?x-8l0<6}AZZ(1zpd@W5bTn7R3uJIjJ zy%Zl^NGtS=(aiY>9NoVGliu8yioA143SjiR+X=QG3mN1*dT8s2-G0D0vj(f*JX`Ylz)%86Hm&cI#n;OC88 z_nJ)XxLd{BIeXJZVJ;S_@r}0>O=leu_h^{KGfrvOSuSpTmEc~=ffw_(a+75qgS>_v zHZ>~3)*r{f`uk}7$d!n?G=J~~-P_^E>}#;&hAIBFjNxyEETnPqpE>LJ>(I2OkUR8A z$Q4`d;R5G{vKIMBp>y7k zG-UWDNK=u;{EWeDecu^qI9Ll)|15=F@6S<8!2)(6`vdd`4MMieT9~iAr48nmOeD;6 z4pvM=Me7I9s;|iuC!Yr=ubJ@d_k2iNxCo^@yTLE^KbWJ~3a1^lSpJrWRCZpIeF>A` z-0#baha{in6(6~=cb|VzY^6Tf^*`XCYj51Z=-qIzp7dJ0PtF4qg!jR>=e${F@jf*1n8duV2e5E=Yiw>C09)l> z(3F}PY`4ZeI$)F!afQj0m^_)OoNNYT6C1RViDGeox3IR8l9;MiN~Zo>um{wzU-lv_ zd3c2Do)L^U4dSrFVh-M~)xPGk#k6PKi{4l3IR!gKj! zv`FfMI3zHJwHS)fv_2D-4EqSm`mabqU>7@Ty%+a8=;GwpnQ+E*760h60)Ecugg1Y} zN%B8oMrxDFNtV2(DVLQ9*u=-KAB`ZXeR%#4OrQVIxrbLn7K?4c&y5W)OU0t z6NVkU#aR{h;o5cZk}ZStHtnFYn_W<}_DI53ChjUdEU%ng>_IbYZR5af*m61zXp7xO$#Fr{n%eWcR3^ zciC_r_ABTzdx4*}%Q*^%7jL0I*Rr|x{m1FdSX)#MR0Bir!FbN~C0HLnL?w@Q(dN*V zpep$Ky6hCl!}~0(jGD>(8UxYae;ux^eVpIKf; zTdIvAarQ@^vv6j{;%%(Acr|T5_6c_P=upC(t?cY`WSy^kakF6r@|X1 z?7PDcTJOGt)_xLPdfMyQv%}*!_b;Q_=ebkxv$s3wTEvt5zGfJ=u@anp%fVt{G#&mm zge9aJpmaq7d-UA~%l+f%SF}7i`}Ocgf~E2G*FMpOrCoGms5V`>R&+q1v=5u{|3O3ywn zg(*qJ#OXHCtr;GqTfG||-AUnp?zOCW0ote#bCz6_7SS5L(|p3fG=4#TIvqV+L@`NQ zxaX#CC^pcW-YSmA5yE?Leo;AItoh+m9d;DkHdS(Uomasq&V#mY72K=S4$#zKEivqQ zLVt~Bz?$+5f%*Q4SM)oC61ukR$GIMgZ_E^L^~#`Y!aO(l#u`-bo(;zao`zw0qv_zw zl@JthnjVZ-XX6)a!aP|!*0hjg-i@yP?mfX5@n2>fucL z-hK$KD}cN+F)Sx$wFU-O^_)mhvRYh+fUVT-_t;sQ?hfCkXymgaUypY$kdA^vF z)=HrU0||U^ZzH>fq|`#oYIbnM}6u7T2-O7oU9^#v~@{lH!q#V(ZmeuzdXi zSm>l6WIu+Zz(nV7n+u*mnv8Xa-oxryh1BT195j{C0XCz(R9mSA0w5QGb6JLFsbkhaarG{2rHnfjv6eh8;*8m)>!2sdw+l^S+Nn;d^Dd6ueKf$Mz%7J=^y|YQx4e_`Pe|&KJ9o>=X6dBE&&A-|C zovJQaix9Zy;2o}5nRcOTG2UgIB2|ZL6&M2&Lg4lh~DmW0P!8-P&bNvff z(IDwjxC2Hr|AFe1|L-FhS!^NmDtp$L7{>Oymcq>=fzbK0NVxl2@tN8ty0cQ=QIWnH2x z2X>O&D-||nQ!N~vxR1RbBzWOMW!Z@V7QB~+9Kf8!fE4y%bhINTLI8Mw6HLF=xR|V0DMCf^L{9OBnMIURp-*w~ptr=y9cN zZl@$VJRE`-MzwLfmJMgs=k9V!xhtVFL`~H9BZ_(I#c>~VzjBRNw$i@m@_brr9>!T{ zvV-yAm>;K2ZJ%rD?v?W}vUDdH-%-GEBj-_Ds|Kg28m6=&yB%bw}$Cem{ z(kZb9sD_@R>c(kwjB1^316@i1Mxkzy|nz&@9GG-ZDY9NBpU?#s-hQ9tx3 ztF;0i2=8Y(79sqO6((G*_Eda5eJAI!!jz1nhojCtGPqX8f;IGcO9j7x9#y*)*VW0@?!aWb11&M6%K~JhKbjt@QYVENNlxDIx$I_Mp)P>phY!U}VXv|1{TPwFa4*PQ)d`Vb8t94a zFm|$6kr{i)puv+^G|LYWwUx=Sb$7Fw+xvczTI4pa^YCyKH$+l#oh3W8SDseM?g9C{ zvCP_M2^p$Io=h#Z%Odtd=RzpxFG180Eyax3PuDgXya6;Ve`0><|#!Jk<}aQ9;+ zV)Yu3H*p%~wBD+|*SwcjX%1l$F9&1W&nEi4W-Dq$+rpDw8t85=@L9)whPau&@OsK& zK6vaJ_#-~XX%6`ViA!d%G2Vwb-T(CPa`q_p>!Sv{bW(@Ue{2HD`-ih>iCu8r++6&l zRuSeVDB?REO*St2y^x1O=9sw_O5csbgZhHIvU5C__BmcO&w2_JNNlEl-!$O-CRQKr z{0vok!2LLOn4-&l*e0t1wxMt({ya^WxySz& zyD&Lf7xr(U9sY@whrtWusUlVxuXnBIu9+3!zZgr(5xS?JE9F`D=4Wu*_7tQBeWuw> zn+0dq6!vLv8-3N4r;s8CsE?N?GrKmB`aT=Hwh5nK$}C#Fu!QUK&F22r>*7;xJG}39 zL0g5fC=Q9{tWFD@TSp&pSHx~!Ur7_cj1asTx8+gM@GG1eyPLL#ETFtBC-x#xiw6GN z3GN#w(FL1PtbSJ|Wm;-Mf>91g-grdovu<+QvM)GQC&8UF%LJc^Ttv1G3b>#m1-riX z@=vcL*Q+7;*6l~IjO9Z`Di5yUq2~RV8=Q?UFF(SP^(v^aR+_4L!Lzh%4EtB!#n&wn zGJDhB@_8#Q*_5=~6g4yoceomgqm#Ch`3GTUk>ZNo#T)7I-Y=Y{+br?nXeqVrqZ7@W%d@XZtqFZpCWjnkCf5GmC^Jd+!jTS zsnjB^%@Ri+<>EElVW;n6{_zUr1_jDusdxdJydMuA9^9|d`s%~)bo9pF4NE}CIMJhz zd+6^w2N-yI2u_?XPmUK?!h>dcHsjD}mVDlW$^S+6G_(?C>{f?op$?E|V@-P#AJWRu z<;?PW4t*RHz&^OGXL{4~Kw`N8+i2NH_wVn;ZNqo)Qmq+$TAwXb+hz%45BURsPYZKQ z%W-$bb0}AP1Ey;Np?}v-{P1TE8)dwhcNN@wty9M0igWq+_l+<9ZcoKJ^-R9u?*!ao z;190^XXKCx0XSQh!$DR0_(ohVS~uLDrF|L)spC?hCF`}ox;4db-`v^Ns!pDzd0}`; zADx>p9t{g$g8PMc9BDs+ZXxBn!d(SVb!36ezsWSdu3G?#da~jGKS+4Chk7>0f}_S~ z7vJsXbXx5sDNH_!_Wuos;VD^AlH&}RZ-~L4!rAH*v(Q=Y4V>LFg!Zq?A#UYynD9ZK zaH!T!$?gUIKqg57F_k3t)FK65b8f#Me1J zVEp_6SEOo;mrtgli{K%VO4J4|b$iwjZa~R)MqCQ*+>Vnr1G`tvEc1uV2#389khWi~kznr;9`I?TeG5^PUlq6Y`p- zJvYPsNAvO17fakFFzEKx<1Kxjk9uDp_$2&{J^k*iLU#BuQEmX(j3K_h+dI9~F*}z6NKBe2jKJQq4By*S2 zhnsUF=+$6f{Pd<0e?AYy`kzm^z7;Mw@9R*E8aorUovkUqa36J)E?~>MC&DMUJtV(e ziA@++%y#<^WfNUf&^q7%?j9o&I_j_Zjv0=y=Xxju#CwBoHoepA?i zdbpJHhElg5rpWDm{Faqx&|&5%oSt+L`h;0+Ub-eK?3)CKO^SenH~UBXTB zLMQLWQ1K3%CN9T31sCggP{MzwLCVD%+Lr&}uBXqz?p2+nyLcQv3o*lU+V4fu3$|l? z>~wBskut1Q^+nA>ebyXVBCb=?VXq%egvR=vWU%!$Cw3dmNPY{faM;YAH3@gr_3iL* z%OHH#rAWbJ_u?R71`+1CAKF9%Ss)B!`;_fbuB({dlm@e~Bg62}d~?z>8zK0azYAXD zRC+%#lk_BJ!0IRiNLlrgY@UCFLN$SdC~#s{L<`xQ;8Fa&Dr5F$juz)wcwNZZsKxn}(c-6*msF#VM{Y==hIJ)ctDC{@c3`y{xW@ z%ogmTqYb-o{z@SmxNsN5E_?z@p6=md7w2N`NoCmf$rD;_H{;PuQ%NywI`8P>$Nd+X z2HR#@vkZwY(pX+XO;62W(CP-Ll;49fdLCG(cU0tc%bO0R7PEm0!}wK+2T9G3q3VJc zpc$zTAIEo6aHS^b>pSrIQ~#r(5><4-Mh&VzokXJ>$M{4jU>B<|z`$=socg>xI`0w4 zDxOF&pVBX6`#1~NZA!+tcn;mW&9S0x5(_p^hh|YQr2FJF8>Qe)*_b6H@=BX0MiTvR`A2p;bb@GHz*X~Oe-`uf3`#cd8it>5yP-_%Tf z`B!+o9||ndqMHlJ4M&jqO`E1aqSCB!tnl9mOs8B$9Mfc<)wY~F{*dBCg{8CeSsqKqsBW=+dr%ksFL*mS;R`U(o`|<~I0VxGZ(; zP{)`3qj>plHQZ)656x|l*2s5yKyZ&U!{e$@FR-{jS=o}wM;VrIRtDE7N?>(OGh7!u z3Rgxoa98f-;(wwUoaR;uzQu1U=wuJT6D4Lr-QM zF8yYPTgH|MoWa3hIN>;$37nblLf2ijVmW#a5|fl!1?|r~Mj-h7?BaIfBvT!n{X`D+ zpLK!psM+kw(IJSYDoow;BJMr3oj-9^;9;!~W8ZY2h~I|nC!8a}h6Svq9WjFdN8JX^ zRsOJf#w~9C@nv|q4 zcYD^vU7 zSC|_XH`IaTfFbOvxjfBn)MVn$7cPZ*A?%W{<8ZDqq&;iY(BDs+s_vZUQbxR^0|mqv z?0rQ?B9rmP(jc+QW!svBNv_zn%nUV>H=$MSKU$ZROT#;)z-9h9blNwA#m<|6b!%V4 z`xo;md(A6&F}j2{?cC0fFk>KeETKkg7b!d5r=b7ZY51oz6nx#91v!pp>Jep7ZFiTN zGF4#2P8EE#;5ghoF3HB(Uf`2p7^>JlbNTPY5why^xuTmPrg*!b`t!$YPfOzV_3YGGycq+#a2%l4`W(%$-c!M4Nf26>$1KtUg! zI{6A5rRd=G;@qKOFkZt9f7jiE_P4u1yekJrUHeWh(POx(LDxa`+g|wP+$`i($6#07 zaY~X~3v*Un<|34yQKynD^EBk)ZOkK3JFJHTX2xTG_8DHjZ$8toybL{UR=7oEOFs(d zVAtX~=pwvR4wEp%$vv{@A1%*snYRb0=ad$wLMh8VKUm7fBQD%?F&I()D4ey z2e8REf^l<14x}2@g5&297+&WfuxtFG_|zDtKX@z4+O5X4##i$BGT&f@Feg-$uL9$h zF6iPifU09`fF3>vzsCqeW(+1hJ#~s}lV>{%DnX(D7ny#z$|XIGf(nH~diu@~ltn6N z^Bmx+6g+?h<*F!WnqM=So$n@I&5dOk!(u<-}hu0?m9k|4s9LD5~nX=Il>;GCk#bKTElBP z-p_*u=M^|%xh}H}k>wQgYs7EXOQX>q4$mk}rwu(PplHid^jp*izIcWbqa3Kzf17AR z)qe2In#ks^5E!d2s&Lk(f$sbnh6V}SFgGg&i*y@6eoid~UGZfp2QNduhYH#kAL6qY zX0bE>1n2OzyHu2}&Q1Ba3KjmUqwBaHMeB%4FFah#|QJyiThoqgIMMkITetQ#a5c*54BLr@ro3Be)-wE1l^MiXk*n;iK8HvV|qS?3g-ynX* z1-M_a98`x+r)jhA!_jy*8u~ni9J~BPE!kqQlpDo41IY9~*-I&47`Dx?5(h@3r<<4rXXTYlX3OX0o z2hw#L_?uY~sJitWm;EeVG;C=*SFNnh5+a5(gE6%*{dEwxTn7c!eGY}{VgI(o_ALGe>IHqCP7DM*_ z*gj5i*+tIp+%fPKNx&3=6S=0g8?LRg;(Js*gk0bjkW6raK`VFinvqxFfMo+s+$Chy zij%OQAQN88ETI7FaGd*aJLz|J(UhOwOxI%|dwoQ3QsmjN?GG+cu5!K0`0PoX=KGaQ zlJ@+b?;pSq&%=H0b6xNE>m?3aX2Je^@MR|s3eK3vwj>+=l0JUz zhdKSL@YDN~q~0zK>$k7}y$2WeB&C4PNw z20yE82lq$n6PI3IM@EHtl=x#1ANSuJ*8XM%ty%tv{#=ZL<>+2z{ zE(~m9rtqV4M9}>HFsu{$T5i*v*|sPmBT>9?KK&xqj$E!-c`7eCVhlySGQsG@x~%%Y zbl7*%5^SDIvAd(w*`Kq1G(=m4It7MrQK=KEM16$r+e*A=K?wf-w}~IEJ&t4=x5K+X zF?4pJ7#1g-1?jXWqL`6JV7byCLqi3|SIYpr)+LK`F2%!zgb;qb?I6}w*C#H&=)iud zUF2i-oP@}~F{obQLeqU~Nw&5N>izygkm52@-{Xc~f}=&_jdsw^l+$F;xm)Dr>(5-m zkveY$2oB_KauR%%qiV)r^oP+DZYvV_D6&l7RPeG6lwh)5_IPK+5nBCZAX6U{fLa1S zsyx?~6@Lj3xA+WTZ4tuz;*T6upCg5#X*;+uAqRbHWi%-qvS!wW)7fr~!RUExjWC}s z1mE?8@E?t44o3#j%BEiKz@Nb+6X?R$Zm8t#i&Idp&k+MOuG0GWYU+sO*y`0;v@uQ`KWdOQl&+#8M$kiqL)5gWs*A&3-v(L9paNZ6=d>9WO_$ zG?B$UdGI?-&?*uEaY7{I1~4rJS8@}R)8 z0)Dw3qeBAg_vPd`jvIbO#9tf>HbSTVbW0McX{VCR5@ozM)RFC}vu2YGrNH=15DWPs zxC{q#G}*`=9*(P|w6EXl!~3F0F1UtF!`mtDqYwKA*&uDD%X+`5vb_nzSqG;|`!^55 z;(t#_Z`TCsUp9*k_ez6FaV8jiCle%PN8!K;KD6DdiPGfdSxCr8oO;`wy{S^h(1{J) zV#kjZ8W2Yhjy;5;tzOv7{7*P zhL5Bjl54TT|0ooPS#z)ao9Hnm^B$)bg3OI6{Nc%;V8QH77`MlS?HQ)cn!fELe$aMO zKB<6q^EDyxjyc2_RKwEcLoqY`H8GDz&gsKXP>-n{e`-MrtZDj2n+4|Ttzolyp9FuX zc=H9GJHCKd1!?r`&rIA9uOyx=^p%WEO5oGpaoD>15*R*8qRz5P@XotV9Us2&)s^ak zlgS%h9z?L!0g^1qvzT`|><3-La+q20H~ziDO4>PI2E%O(G5o+J2=H`bkCbv)YEd2* zZoLjt4~OD6;cQGz)h37CmP}tpV0N`9u;c%nU_;$hCOzjd|Hfevy|XmK+Q~zh&AMp# zPlG4LezC9@-wPG*AHcR+2{t`k0}5VcibIp;!qIm&Sp3C>GU{Ei?wJkS(J4nCH~He? z{17}IF&Q*uH5i>#r^4^Q$s+VQ9qLkI665M&faE`cv$qFq1rJ!3l|9SW5yQ4|cX^Ef zZPvNKkOix~r3pIn_&wqS_g2D#>x}(DzH7#O*uiYOBr{87R@DL@0?4w6;LGje`j}1w z@ar;VKJ5y8kfRSKZxK+?TWto+8$f z!h>^M*wi}||ICBQ&sc+-6m+nrG>xn?ddT*yCz3N0I@Y>q5EsT~nI>>&9}LDL{+HqN zp(H+S#%KP4kRLm-;0S%{9fwturlS59d8`&P3R3Hy@b!OlLEm={+iFux>bvtO+hRN) zZ7abn_77lBEtkXeuRYYa&y}?%-6tOdQ>_203qR^z>HMt=oRrypIN7W&`n;o@6iOX2 z+_IWi3YviTrqxp9h#fS|;ThE`2|Ut|CH$iwhw1MLJ$~ic^K{SOk`@h><@T7nl7@K@ z)LV6cqN5eQsV;{Lo~=}{R*yQ{n}D9~0TqYMV5L?{kZ>JdivJQjn8Y7y-vS@TOkvWa z8O)2-6`vN(f|q9Jcqq-IJz9F~v(6yYI26E*ym*p3zRDiFgX3_!%~ojln2wtsUZ&tG z5jmeorV$V!c*lh~6`bSLn;e+a-!P0heHsChV^x^F z(P_G;YKSkNl)!7@|J-S89~@wU7weu59=L1A-PM07GK>rXmsm6YZiqa$V=f0XzU}}& z<5PU?Fh$yOpqj=;nlSY#fB0>WGI>`V!0d0Yf#30aVUEz@`nKvfd=<{Ti;t9P`5=Ld zJun5$##MpaqZg3<)e&ns5j&WCikI&lO`l9Nxu`}X*52&~P9ulYb-8(hA7~CV)wDt= zH;@f&jG<}S0&a0(3TwY80_Ub8h_Lzuc~{nfnc`qLU3{93>Bn$)?1CUFU=bHeXwVn*)xBlO}ZYM~X_gGy8$Eo{C_!{Uh3<{)v|wC&7n=y@sA; zg=Fq737b_VVc*+H!ZUp{h<83Ajr`+O+jARS9Cpx_xejbq{z)3{JRUb)Z{|+7ZKuqb zcvd>WiT83FOmFw3vflbVTvosxSU$`WJ4_~E+e#-`T4#*Yq^GmmsOh90I2P?A^PLZx z5}8g(p>e(T+~b}swj(PE#!V7f{|j58TyPs~>hWVWUMb?Z^)dMBzdz7ASs5ma{NmlOy{i<-+TqH(**uZ$;JP zguDHMU;K^-%Sdu|0^NAo1+x!M!{SW?nTL%(>(LA&|Gru9>WbiTKeY(A5B7GoIUHiG3L&k99p9VzREP+Vd983wn&dKJRu(rf9Dl{~~ zsx|9a#d$N)W!;4Wdo)a7ZVI2@xDBKnDn;kxmOwylDtO^e(z4Tr+H(_GWgRfrsSctG zX&Wf`Fyp?NMf0xi>G;l5;H*A5$q&{Y&U*P9lxy}{$Uekl)UAA&c14TSjlyx}o^i}n za7jikOof4Fo?vCs3F@Jhf^Yaez1i+Xb01i9@yicDnbaoKNk7H^8S(+-ROFax-x4g_ zzlsgn?tlXWPeA=AMYc{W8{T&e#)p+&SgAXbZCtU5?mTm)F@F1@?DcV|eyfh=9_qOH zdm@ETtR=J7b=*teY3%#7TByyRhq^oRLF#TYE^#%-|0X^r+vs?)fd0I)Vh#Fl;l)vXkZ+e`oXlK$yEvAP zuX2ayJ{^$eWCj=2%Zb#}L zy3yD%bpX?l%cRQona+1#MX}?V#92=>VY@Ht@;xVZaYfpnx&99`SIBeFC#h;IrM4 zLG_J;ndoypnV$Je?VH-TN8jJV&0iDQRqeS#=Ou(j_tZhR=|hO$+DaGwgf60uH69Rn zxox`IZ1a}!%;wQ~{%z7K_Af{mBR*GB#@&CL$;qU0l<(srd zV1}N|&gH6qlySwMcfuo!k?3=6G>fp_#>M8}1^bRjUVsL0x4&oNj8%4&pgfDDO?&9W z{-HQC`Zm;R&4&vuP2h5RA}qL&$PUN`P|e=&5bs$HQC9?ZakY?bGO|S3cm?)(tr}(p zeWE`v4MFR`S?-9`qharLdKWVs4;-Ar4hY_q$7zC}=It|*#3k(3su@_}Qbl=1i8!JpkmYPP zVx=qkIi;F#wm5SD&E3&jU$b{IWo)}cc}p#5Y=f{5_vog3RYao~pQVM(`@yboB5dl? zCCTt0R@bJ5Us5zccGdx2!OH`E{)`a%Rd4w>-!rMsQU}(a)T6kvBHl);4;phvasA7` zQuvnlgvLK;y1)l&b(o5;-Q2O~VF()CI?8+Q*oX`95|r#UWxpng_`wcF?8ccU>R9`P zhUm$&Q-_`Kgyuxt5o}2R^~gZ?n@X7fv6;8)_|0!E6x?Ls0Tz2Y#g`hc!rO)REbL4Y zli8(&dKTO0mXtNCF0^Cg>`TbOcp^-d*JGP5SWt#pJguMPK!0W5Lye^`PMWF5=P1de z<&=l;Z`e5+Z1E1RnwnF+of{_GY=eXi->K@5BI}+v1`{vJ^J>kS_^DwAG#*N!A&Fa9 z%aKFe&284~u+a7UHZYln+77|(hR5OW3LR*TRmN-G$4EUU9Ik~}z{C%)DEsX~7?*B{ zD<9pXU?K55Bs~Y*2J7PBBw1*_vDVoJ|}nKEtJSw@Xgu>L4R*CH$#%qXrNU)k_g{u;~Kiab(-E_E4 zhv|l2p*po6BwO;HW+tBiYgZFC(gT@W%sUvmbszK}_(cxo&*^LO7SZ2D?kqsAlA9%x zLo@GPRPw=rInO;MzE)HTL4M2N@HPjyRIG$sJ`cyf4d=PQI|{foPJ)fEIK^wG#^EC& z=eE5uA1?j15&ADns3$6r$yL0j=SL5NnWZ1oOHb!VwwU3c+D49j^Wr`{orv3eH?Sd% zQq*-qhvsFRg1|E=*edi&b=8t!<)>#f^ot79|B^&1?_|O0t~6Q<`a*J^XJE5j8mqfC z6d(AF#)Br|OtJDZAG9Hl|Kv4@O?>_e)SlU^$Ua_B)O)i7ZNWm$Zqeg4JPx9Ai zHSwCi6RF{y1YCZS$R}<+!5_QNL&Ca&qFgCyN?pB%b#{yf2Is-{*f*|Dc?yK|65K4y z6fe%208`b@(v?61Ce_A~)z+z0@Xv@>+ieW*WVccB#CX&^_Z52g%CXkl8=0X;C$#?h zMzf0F@~XL7H0E0g*S+xRnOH+1ml31S{0^Fk{R$uRfuilag>Eu;k+o68%v_lDz9)302 zv$~2HGLc`4`s+`^A#HUq-C}?fMyx=cfxkJ;k#D(GO9jV=*#!8mHiVa`I!xM2AM$Od zyqJ{KA>P6AGkta)58FEuxq(XhtVzgrXp{zU$G%51iCIILV{ZsDl z#iY>ofGkUq#U3BR#5)J$jMI*6*hyhGaq}Tuk0^ojD=gXXrTXmr;t&iooeM_B@9Cn{ zBv4kFf=!*Bq}8R(E3MUGdiQpU3+_t8#uz!UtxBa!IjSr#CY6;K9)T@G2k=`&!_jQf zXegWM0)CwwPI;h=o~A#bK*gL!uG&D|->jJ^Xap-ZmB0fwr(mMxDDJ#4i*(p)fW;dg zK>5^2ZdG+1A2QhyJSI=!Pi8FT-g?Mk(gjCeUC#}!Xq=)@qeajrag<*e_#Xu?YoK#R z!!WVSg$!IbG3O>}wq~b1i!u_}c^8+${Vo~wJiZt_cwP2qlUO|U?i(-{vS($!g)r{D z5v%F!ky-%!F^8DmpGfp zVdOj9jHOOBq%ieLSg@y^=DjcE$DJHajiZlcc(VuYtdvJaFa8Dl(1Sj5mhYAg z?;YqfNMu8nt!G(n2@t`LA(LU+AnN)C6`r%;ahf*EO&6RMpLHu+?u6pjiT>sw9&Vt6m(wyxcyI+7>4g##p8exP_BvmPKLZooU76PdPxKyomMG*tN<`&T@dev%$Va{6-8P5s&}fT*K78 z@54r+EB9{tG+tBIjm(!ggS%h3lf(Qe*eeyn<*Pn`HrH-AJK2q_gRYV0IW^wZP=VH} z^zcjdy13bs*0b?3li4Z73W~k@ku-nB;@H2kLWem8;)m`8k7?aB;P+beQ$ezkU0?q& zC>STdXoG;~=3M8Kp{)Pnd^AUbd!gpIY}$0(8QUp}yCE3xH| ztg&dp3;6mi4Pw0)!OrSaFk86-a&~Q{n>m4uiapt0I~|;G(3^Ri-4nVJ32<=3I!w&H z4)ZUsXPf+<^9PO~?zCBstKRIO>pR@oxluaQG~^z6T@GVQx(10}juf(yeJ^R9R4^|t zd`!5tK>feg|s0otG<~QA49OSMZ8Jk_C0^WbC(tfo$M#zkRXTaG$lYe z*_3zDKP5^I{z2n2IyvzZTl#Jp!F1oxLEXM*l-6+wJhF9YhLt-eNGM{-X?^ClVFsrv zp+{qw7h7QVoopj?aL32(U_Czxe6v+Vdw=`_jWM2Vr|feYb-$B4W9q{!9?b-?mJiq` zjb+P>KGNHb`zW~V9~JwY7OUGWV9w1?VN`e-+uQDp`dy>&)Km#}jdfG-)4yDFTL2gN z=o>vg?ZNx(lE4{KnmD0b$nD1^GZkrs&P$&m{@6eWlTu;2J<|m5a3r0q7S8!pAADk0 zCi-I1&R^1-LF#wzg4)u5@ZZ3-{LtR{;Qe$8+tEJ+s zC}L(%9)JD(8q#(bewX(Mf$x-n?YAz7R_u=#9T&^9gnt=qdAT(jf7^<=aeH8nd6Dyo zIZga<|HbGZrN!cweWgaf)i`&i8v8iu3>T_d!?v#_i(A4?ze3c-iKZMl(vqp`!m(=d?3=$UEuueZI#v9a9%2hke zoF70@nqE*fNe8`;z897G8eqhML^d^K9&kq@S@-@*ko#p$2Zxu@>80_UpMDmVk37Q7 zd0EBZ6F7U`$EPy8i$6eeOcmX%eMG|_&*oN49*4c{4^C~qDxR1lu#pX)(tmba zI4_YEy=t+satI|dRbIY|GI${#=Ykiws7;&J!wLZ?U(rren% zKKa**on3H>FEj|i-tfziF1Hbv723kIF}oq-${XmK;>;==M&Qwzo9ZXXSJ21Q%hcHP zLKGBmg?62d5cmv+I8Vs3o4oGiC-~fe#^1BZ)Xs{<6!uY4w-c{nV#1Pj<=BzvPa<_m z6?Q2rk?tApq0>UHWWO51rx62z*1fSp@hO65YeMOWX#&j@cnB-cjNnC1bJ=(~2f7_4 z!K-HfrK;Oz+59r85A%WC7aj0;&@)gR6UkhJc<1Ft!MC$`Ck22m zd!wwu2dV3@`uX#4;iL<~ahwAibb6>&;B4dxI~JuA`BW}A=ElCSg9)4LaQkW=y=;P&LOi|&}|c#ou$h9E~WAF4um29 zJb`<#XFW7*Gi1YKODQRREsndNfV&=>kVN@laI~tY_V>|nxI=Jp1)0M}V<#|>U(9@$ zFT+ubU9osrKe3b2>_OpEvelGegXMHE{nc++|J9vJM(kp@T%{Qcv}gRxv6#XKu+HkC zxS`vMeI7p@N6oW_J%2@Pz_UW+suVEoyB%9zGmuYsI?lQ3bOy$E+v2f73;D=NHq^gE z6;-Au((SSYkSfXHp7ec$U7a6c&gs4Un6bC%>ODrnJO-@!ReJ>}75oO*^lJdut9u{lR(g*2#h8-wR{?2kg;4tcUyOo-Mk4 z)0`Q;Z={vc87w;G3e?3+fPc3wpk;I#oBV4kFB5u*de^-Gx6nKc(G=k#a|xO6xRW+|T(aQ7QA9qlNhgpoD%eXDD@H6hA0jN2{pACwO4(J^$5G{S-NN8|H9 zO+wF3hBK>broqB~>tDlKH1e>BTr;84P8*4biCO%zE6&SVe!=i#Nmzg)PiEt~Ue9?DNs zg_kRbgPLs|&TAUW@UjiIpS?}vZ57ywh8_GpcVuI;8u)p=@tl!@K21%s!0FExi8;9d z{Gg$X)@>T}W_c})N}9#=3M^QZkPGy;v=(-I=jcM)6*6yEz}dB%*-U+V6uT^f73Z$P z&U7o_u5Y2Ig|q4Uw71+YM~&nkcf!Co@nSiVF{T>X)8=Xu)U8osi-j&)kuawj zvwS<&%-Y7c9xiYm``S><`KYslJ{j;QZ~@mFa|KdsQgQZzZ6M)S028L46r0yNF^A_h zRPr+bUp}}+nLiphEgx5=UbmFS*%{y_*D&_e$AP9l{7Xr0D>>sS6|i0Cz~8E@qt#K< zSlU%%yslM5Ew^;g*r^5HWc;M!A+M-Vc(0XK^njUO0q|~=Ehc^Mg1WZbTvEz?PVDJ` zd-R;yz2*Acwf)cOnX4x4DUs*qxdqYngG*SbODH|>*#l3{&xdv@CimIGE=yz!%~^e5 z7Zri`yxxiTPkRF%rXxwEFqkgxbrxpf*Wri_|&5!hEE$D($p!;+F{km?zVwl|IN;gFSN zc=HAwJJHF1nEU`%obhBQ|C>jiCf-6{Y9tiLHIw2%19s3|iCZtQVAijjgsXM)xfbJe z{^Lav>dia|KfWx%Q%jYZ{Em3Ecr=*?lwRdr@fw^w?g@vtXt3uieQ4inS@690lJkT{ zxYp;yl+7GqyZD~?tLIb5v$`zya<|4s8WUi?L$WwU{}&w|IT-W%IBea}4<>iQxV(j8 z?&~NE=6C2gMGl?D^(?3*x3@QGPTFHiv_RN&&4g9mQD-^pFHyC-62!^XiBzRB`HcZ~ z7@j@}Y%-QG|1$x&+o6X7x0T_{-X>W0*qo0ZHk_cTWxi`-Q~|H}!~ zy5Po!3+(wFTZ`bT$z}d}VLJJ_)X|i)z92oMmSR&+(d)CGlv4E$KB+7Q$CArns_?aa=>iXK4H@MeQ{Qxrz@5c|(EI3Z04Y@2f9!+6tt3V?Pc2tBRWs z3}WjOFTnBWaJZK$s+9_=Ga8KEoU*W%gRw z&9603#o2qRd1rfVPI2)rKCW5#Tj`olSvto>)*BSr+JlX?I)<&i3=8n3oAdV($W{;LY+S6NK)l zz{@te!CNcnvu?v=)EcmW&HfcfPfr-K6*W3+dqp(rH)`?^->Tr$bQQkk^$NOl@*x=( zszJ1VF68_i1ebiafWlXOY^<#Z@q>w2NQ+tN`!OgvI-h>^>%hJ$fvfGB#j<|%&?vzyZ0~Q#^+vp({#D5Lq+#d2(W3Y6c5JBsM5pB&@AHR0Td`TY zZotMhDfA$10P_xwWNky&bIY9y;CcTVoZdNu-}XEV-t{GmS{w>!N?SjDZ)}0-70#Ib zC=uV14rNCc!HbO3bU-T@?tL|5l3{DW+VQXG>@``4z9hlq=6d4X(q9vs%FQ(pY$}G?$I7)5nQ-Hgkn-C1m7g$1HbmVb6?z)9k1# zyy(&pl+W#_vsro=lBmi|`zJF+n_7|I*TJme+Cb($_#K6oU4(PrGgz0kJag615xu)& zPViF(Uu~B~&$OQq+2n(yxM ztT@FPH~HV==1wuB?wISM+5_X@r^;pwhp(nd;F+XvlE&jWS zSa41Xd>8M70}p24VpnIp+g}19;V0;7Xg0i!SPZuUZD?;(Jr(*GqwG@|=ZYu`2*2G6 zIO-;?bh^vkO;&VTlYav^uai)w9VmKHITY3gZG#K?3E;k9Hkt}CjLy#tism1H?IXOH zt5Y+4l^yLIeB`#XuaXrvOu`ti#m>Zn9>Ci@@!Z+MJhoxK;Jy8nNItSsY*>sm{%&!C z-wqZmSX;dY-KgOHg^vR9q8thbZ1~`Y% zv4tbqh&3zupaC(=<+%qeUvrT6u`J>G*DK<%r55-jPsl4uZsT;4_mJoJ9=M&Z({ws3Cpz`$8A#eh{@mIcM8 zAU5548SOQFOTSNAv-@wlVcE+uWUl7K_D6+KM1L|ZU!TSNba{yEmV=#7^s!edkp4s+ zfEiy3XinW!SgGR6#=rf^XXqtxFAv{_hdZ+bFSR9}4LV7y^-R$w(T$phCQ!~lWeBYs z&5qk<;I2m;`Zu2;@9#foUW7Jt+5Cjwt%+hc7N3D-0;{tu>2LkAxTVbgOFpOSb^f6uuQrXN4;+P(>BT2HA+X=JF@-sdzlxikD>i`^S@Riw5(bJ06CIRl-fV3K@P1=Gz3}`8UO!^oexrxp;sYPCMd_uugcba|G@; zCvm6#Q|xLQnmR$v*c1jP(x3sJDSsH03Km@hsMLZ6mvHJpwa=U(i6A^=$Ed z16VVEKj*$nO|&HNHr-k$^wnf~spI)noFly7>dwugm1`qd_v?IiV&4I7|M6VfKWr#Y zm|RGoJPldftjBadvVkO|HuC0J-JE($=dyQ#A z)hHJ3IYD6T*U*Fs!t+L}kM?yLGVP%iprh0S^EQk`hX^x-=D&2!v4Xiymc^w*i?|Af zD!3QaO^;(@*okCE{CmxV+@lltZMJgk(aY~ZLH_L3h&B^=h^k;!I z+jq(suFWVweIW~^@Y@w5YAj*ShU0YlRy&=vmBR3-8Cd3-%4N4FQ~89ouyK+pnZ1|7 z6R|6?L14h=|A?X~E7jqmy$j!axt|7I@TFPV&$-A@d%=}cOUnY%d80rJPVsyKpLRom zT`4H$v);cGbKm!(vc2HAy5GQcocaQZqsr)E^B(aC?@N^WP?CSMQ=kt1yGY|_i(&jx z7upq~#kI?2VavK1l+x&oT{OvE2Nb9ANZ@TE~P68vohT9oC7QKv2*u1;TgmYYh-5Qa@wbjjMo5p!S zZs{sE(JmH}->bnpNhivVYk_0l9_Th>7q_@&5Dg!+nMH;UW^t3`quz-5i1QC{ z%A-F)v-eN#?x^)_c=$TlJ$Ib&H!+rRFpg=)9ii)dD^$9khr32M;80FHG$y_T?pin3td8xu~v<(D?DX$Z4>fs4n|31ay1R zi~WOH*P}+TpVkI@mLh%~lgBTeQwaR>;ml#xK?o{nhOmGN?)D}NIC40Roz(EaJ>m0N zt4=Cxi@3)5FHhlQB|mb5(jD-AmMrxwR>xpXNmR=fSTqJgcT({`et|+E1oY*Bz3WSH zeQz0Gxu3&HCDYi*{dsV5x&~IA=%b6P()hmxjoe|AGXDH0RhCwog`PG>uq?{XS?Z${ z(~CEtTRAqYVof%NvcdS?%Tw$#+m{V|agJBHWku#S2l%!Vzaf2rH|>;Mjfbb$LbY== z-f%ESC%ZB-HOv<;NbzDWwojl|^Az2U830*{&HU5a9=N2;!!CO{)|e!Lw^nHg8C+*- zd*>n)D;J<=hy`nP*+bTI$1_RW3>fHnlY3;b0Tv!R2&dv7!zIN8^8MyP{y$C7K6Wg0 z8Re6vatNMUr;m%5xiIdIAJaEV6OFoW#`5P0xqPAL_dK8&8aJJx+OhfU=f4XiRT)k{ zwbQ|fN~zZF9Y4~sjSGIc2;OEHL(}q3en9;Y=KnBWxQ7Jbe|bvy+3A~*cQ^x&sxH#( zoErLD(nR|fNJ8H=d2y=9mn{+4)0aJW(dM;jG%fQhWhF9hP*()my-p-KnhPIKk3|Q4 zCWzYSv)_K1?D~#Z^y$=HZg5+jSg}vUxbB3pHLleY>U!aC4jaEMR87GLjf6vYOZ*23roE6~)# z5uKJgP=RM2>v<4{X}lHl=UzilU=jdY}z zlD<+rrbbL=IuvHyrG;0^F?v-2s6CQqa=O0w^Yba_ z%-IKSf8A-a@a%C=wPx>vT-b(agoi>`Z_;KN(Vc|3>}$^!*yU=*rLI$CZBu73ucOjz z>IzG4K|l@nEk%bFJ{GvEUGaFbc{w$FX@@!@iMi#4v*6Hcuxk`2;I_cW-Ssf6iD7TBjH{0~I_ffwGZab@%>>`1SK+5~?#bEc33 zEYyNGN59hQZ@KhB$(0Q;5&V-yD(uy>xs>|EnWo(mSn&4F(0@b>7u%1*_lF#g+aHed zD`%5Y^&w$Klnx0|f<|kVKaSrsiTtJg*;P$d+$-E=Z?x(3(&F?*0_-IUffIob9c)p#tO09%^TNq!BMFU`cH} z-#@2}dm*`oKBh$DkmZkf&(*D>uPw)^P+}O)&nu$R!e+|YXvHM#rD3efKN!%c%m(ZT zL`Pvx|I}R<&JLW3(-tUUPs3|S342Z1UTWO%EF%gW{7jS;x0yTdu8um@%Gl@{$Y-Au z@|A;>Ks_cxFyXv}3sy4N_xKY3w=fB}O?=5c8>0=UuQt)PqqRhHZSlRrZ8CNmLP<;; zclQi&>ePQut)7x-J@`8e={OC~m6mV=1~rp|g*A2@xT|;Q_9AHsjB4X&o_Kg^88oRXU=q8??M%A{#+I41 zz;_0BtI2}x@EJm9Pzp}ex>D{R7dUZG3gxoO1>cx17NQo;KckMzM&)8iu`x)^8iZ3n zZpN06LvW)@2Q3u#+LCfZ*pHj?eAB-zEalnz1lHR&AHMKP~wZ;&GOv1P_`(>>A zy$n0zr-7kC)5)f*Fy1g+G|W{MZ<|=NwPEf2n1_=f zf9VkXIJ}%<3{gVs9~3wahWUFPV9wSc-1yc4-!5r@ zi*9o0B{~Lg+~aXg|Dw~Kp*&enx+gB|3C3=PJf^uch-?r1rs|@07?8aLBab&yMX@UG zd@!A}*OW%L0VD8*aF!ihSHbmVE)YA;yh4f}qnOW$6BM!`9WV51vCt_gP}0Ahk4aLd zwDWv}?N$zz#be+K-xVSw`{9~H4_+o9~tK3JBY2XmjRGPgNNX#6CS zHXCZu(aJbh@$IfiV(eKm8YYW&Psd_eXd9%reif(fOsdzJv6eYWza*oOdd{;{pY<(j zqo=!U(IcF=k_HX@qM}E}$x7&1I#$#eC4oyaKXW&!Md+h8)AsuzG%K_lj&*FM979uX z{|mc-Clty+bPk<=Yl)(?h3S;B=jo<-$iSU7FyW)u$Izb z+>|d15`(7U{Ir4GV)NzBhn9}Vlx>E5sD?Saa(WCm!NddG3)hOu9<|Yd{Znx19*;%}m%u?gv3{0y5)}`Prn8!tNI5Ex_a3*8)2>X%FM86rtaC4Se#09Y4l^;y zFhS&X|1WPeE1H*&^vAjtlJud%8owuKvWK%a;Glcgsi)^2jVf`(`*+l_Gs++7p$%6X zn8`h_2Xx8R0GCrQ#Y*#%xcM7)Qy=e!vvz8*%|#C>Y)Ucs2>Yh`WHEhv+f75STH}xx z6KUn7vCP*}3?E!FFv&%omB%1>OgCma|DB|ay34S$AeE17aiX%^9T4&O>zNBJ$wY=v zxRjxH_==?I+%5BhJe5 zo>+9u5*5!2GsxcCuzYwnTk5TdjuQp1laS+gx~Ic5&nI(hMz(U&*5!Ox;1S5xJ|e#M z)d3A!RoNcDdU}ze4;i8!NXxf`@!UspsxIb^3HP6kwiaYOF`MoNmD9JdI5v6IO17=# zG*Q%1?)4oR?%3ifx&{u_VA#U)pcOO-)*JG0f4nq1}b5v*u!(@fu+}G~SBHvmm>J#?8*N(|76iyd<3qJz5+v~ z-I>x-YkX1eLs9R9{>oEjjPu}0?YSnTxDTPY7_m50_->zfDAEC01zv{SSY6J3Zf)oX zF6e?gd%SD{t&rFZb9Fw`$66`85^x(nCYiCeod(SHj*2)wK#O_4^1#K(R;U^8%H7N> z=2g9#IXy$+T`Vevmw7S?i@R;vdZ#GJ3v{e^x3i`fZt^rk*nMezucW{Ox=b&(fV={H zSZP}>vzZ=-cBWZ;Lw7i}jF5rjl@oA!G6*x3M5v~HMprY7(Wzk8=L8z;C>o%)x7t9dI|KKB|qE32{Bic?|j zTx)1(mf}t(zN8Bdp=fWkm~y|Xv%URea9zeoJX5f!-e8ET=kq#@E}HVaqr!fS8J;gahLKGEzlec!;Lw&fDKzFLHn z3d*R*$Oum#%tzzNjqpLgkjv2)I38s~`F%34VUd16cg(H7A-%N{S zt2Q@Mc9AxW)BR4X|NBJ}u`8M3EnMhBx!`Rw;d0?V98$KOUfPwB?MHOk5cxK&EVK4iD zcC`dy|AO88<#*eubWIigt^OZH=i!&r`^Ir;?>%YnG_+LDxo(q@2Kn`Uka(PVw3iM!R6SHr&KmEdWel)G7l3z1lAZwV-JX+#T1ub{O zjnK>dJYNm2+gSw_cQ!%{Uxm+_UBiv;YGAqW9cW!sCRYaE!pNS5Emj@k%5xG zc1|MKB2-8zpJNm7#XxT1PcXIp#-312W4dh^kZ663C#@s-?Ca+ksq&tYsZSs$ULJ<+ zA||wFWgwFjJcBk(^Q5zm{a|`b=aH3l+|G5XE-mU!qg^@$;1<6FgXLQJ-vsV3kpsgJ zmbwWCs(Ju+4B?&stf-e8*Ja~4eW@MXU0QuL?j~c*rqgq&8@kXrI@_pbv?2`unukv_ zw^5f45B$%s8V@x);dXz2s{2WnTcMfLF!K<`cFjt{8!W)m?k~)z`MK0=IF@{|+zj8# ze9=~H6|I`-Pig|R>BOT(5T4Y^o>adMLyu(0`b)}aH0LpMKh=*FnYw|O*1ZqbT&w37dWIGhOn3ZRDzE1gF=2{z<-hWQHJZEA8Wyy6lAOp$m7dZ&mrySH!cHk z0CEoGk=qkYU=HUy;KxVdaBl{zFYLn^k0i*-q0`KGM>{sn1G+^ogiKkv8{dESChJmt zsm&J!a%gBRX0%z8%uO>;A*>a<2fg6Uo5}Efx(wvToM4ip`ta=6r;uqSjc!>Q^w72^ z{J*VP_^)1`2y3O1rIT;*yjQg1_>9f;XjCV&k=$c-?Ox%qeIVH+SOGm?g{wp(*rTr& z;o+VPu=_3p1|~~Mx27*QnA%K=MdpFq+BxvyRTlDxg~&zrAUknm5mxrputDd}qimcV zy(!CLyKFphNSjRRx-+3?+9_NjqfUnnbC~7hSJ_2B9YM5N3Ix?ZW%ndU{%w3vH$)@4A7wHN6U+zh@aMj>vw7EwAALsEtG z=xLh*rtWq;O=@$%C6^3|{ip!wA4_C+UUNt1g#W;7`#Ie3s|`;~_{naXw2jKW+(G*{ zE~7#fdgMjNYJQ?>Gwv@Hpbt%p*{RDTxD3Ju)Vm|byx?YM(j}kZ7&q6q;9Sg1**&bQ zSE1Vk7DJPt1^1g&uv<7ki(A7W=v-E#Tb!)vlsl5JU#S3^6Bm$F?jGSEbrEka{EG&= z8IpMAI36(y!(3zD#!j*W=7mmQ|7w!VYCeWbe1$iwcQTtiUt_hX75p{) z4Q4wg)8!uFtax+~IoxkZJUpJWGhc`kUzi1+|I%>2e;NlHTtwYh{lT0^DkRX5dj}_o zQ=Z*Ka5=FF4h&Mqs8gF6;n@lB{kb4rQ2&+9UVR5oj83EvoFBrfP*XaTk`Aj~zp{%4 zGhk8J3i|4`Gb~eh4!eA_VBljEaoyhyue22C$K83b=kYrFp?iS0;*B|uu3Jy*bdKUw z@ld*a%MxPu^*H(^-Gsp<{mf6j@3{X+Ik??iO^fRNnb+MO&{!@+esR8GdJ^a+X;Bh- zzXIDYcQR)-KIDA~{Q{G8Dw%fS8}LS2huOUUADjACoNj(^LO!}U(D%tnR9JW%l;gP#4na%KW9SW#8z zQITfccrg=Q8}o?g{cV^Qv!2F&wuZ02n2RB`zu$}szMN~p zQ=SxjQ6jmI4QNX4H@56#A{N~|0=yI_Vn$Ty+jrN%R{br0Jeq-{w;w~EZ!JoTtzs1P zUgE?8Wz1P!N);1RTi@E`7g*s*lZ|1}9}s+(1xAsgV_VO#)hJSZaU#T4fU-F1Wi7ie(SPvOh;#4|f0lZUi zg2gq**vj!9c%@f?xzG8GWcdWzm^aMa(Bhc!9y4Kvyd_B3EQ7#YQz9pP5EhMg!HCW0 z7J-0MY_H@TY*?j7){Zry#;XX#SIulach{McB1e|28fQoHE!f-r6*#k}1okCNC(BoS zWp~J~f<@wHboQY}99zYCGbT=;rC}i`v`n15JgG#_e5}OJ8BX}Szm#Kia__4_IcCLT z7v2HcJ+MSrnzmLo!k@^u>~7IN*gvlj-M*|KJK8wDdbS-2o2&)fdaN)wLyA6FIT7|e zQKUY5NAPKA81XT3fSvy#8bnLb7ftz4*0u;0$A7}6h03(b^gK+iwqlpJ<&ma;eenC% zPn>xA5r#du0pe+${B=+u{^?~1y z(#YboSx)jj+o8-ml-cuk1gm@$xvuP82-aFlWLj52S@=VIHd4Vf4{d^Fn&Hf)Ium;G z^()*GGzJ^mobZq5R-z@}gi7-RssAKF8c`%hw^M{LAuF1hIh)G;P$r>viKzSU4ou*{ z0RI{}C)a!xD){Ok?*4fW{-sCIH=(ioKTmboo{h^{!H>>VL*O+|@zWqZ#r=@7CW~4- zxMKT~iBwc!0#TbAjsporu)nDU$;6FxbWadee#nHCw^MPGi9c)|M-(sC;rzbA92ZXo z+MX4`e;`ZzPW0e7s1fH>NgBG}1lMnhC0iD42d~5drcCM)oT-0^?lVur$dSKHZt@*y zSCpsica=b3Um^~X`@G!INwnB{9OTAiq0aF-zwD17#FQL>mRTkM^I9QV)dTLIFebAP zk3f|DAjG~}K;7PbXC8$81yLg(R(stWUYolKxoKR4FH9WJcF-PLlRj|f z_#a-2IcIk6bv#@91n<4Lg4jk~^1Cr{$^Smftw%O7Wq^ zPcv}Rh1ZO<`7KtBUyoawJHcZ2L~`QqFMdFB2XA8NFBD|CxoIND=3ioiqJ`^ezo!(v zm$C;s>?A-h zekG#xTWhl5wGQ~`PN!iU%Q{g*jP2O4n>C;J6W!W7V2|`WG!lz~skQf*6NAUWVBQYM zo-ItAY|TjI)JP&`lfw*LHNcw4?Zo)B4gGDg2mE*{_)bcOeK2v1Ie*^(r!0SkQ-9at zq4W~o3cDF}wA_(Cb8)2>-#QspVeZ)<%|x*o`XsnB0>3|1qL8_cE-hd%Wu7mk&IJ&* zK#1IFo)7hg=fVG70Og5^kf1(csvF@&Cl=O#I+tCtKVy!=YC=?Is0B`}OQTdhhe%q+ z)2QdC&^lZUBhunX_XZ6*qB|YWtTe-=iqlZ}>1H_2_0U$Clwrsw&atsvk(9Rmg{dQP zWT-eBD)YNBI@zBr&iW5!N|w^fuxN15Im~Pv-p86GOhw%(xwL8HEvPWo!*&1Vk!!}a z;OibnD+FXoaVoMqMV_&WzTV_oQ32LJwQ5<+^~+S{9oc6AUhLX$pW#dHdF~vX!9K|2 z;|!IRyrD!TcAerg+!|v}D(&;pyu*@Q+rA7pt}lb*Z`Ejfh#Z{zrA!*E^oWv?CcCAo z9Nw!bkdU5r^tU_rzIt&GW5bnEWn32DE4t$7*f6@L*D?hix#aHZy%4G(hRfftB`ajp z=mbr15ET@JLH`xdDJw>$q|C|Nfq!s$V=RqdtVmx+tz?es2{Y4Hegx|`6N&USYjV=} zD$mND^AK0;AnUR(Vv)fv5IvGka?hziY5ppB&H0>cr*X^YRfVK*vMTm}mBj!Tb@J)s z7C5hzNq;CAQHRjixO`wby_&RuZCe!0%_RF6e*Z5vL$(4Oe@!N$&qZmZdIj@wofEM> zyn)%*0mSm89L(L}LwA@jflFl#D3=t=&ZVx@;Z`OS>}p9(4$713b2W(b&BIK%ofEbA zpiWoVMBwRDU07us2DeLRk>i=dbioI2eCV|sr+#z;y(i1?@RGamGW`HNjqV0VS8H-$ zYX|cvXgY~XRc4ZE%<#Xj7hzu4Pl%o_N`-Ad;nuSv#7k%pwy{w}AX1(F?t2I7+gH>5 z*Td-PoonF&`;HwgAB4FbS3xFGgzN|qB)1l?B!N>k>0@Ud3{G1G2?deRLXt@AhazzL zc!Q13Hls;?6G`0~EpkdP4983y$;cfUc=R%zE?`y2(+@lOE}3)S;Q3AD5~+lH?sC*s z@E1x*G=pkRE<|vmfpLWbxNDlnzM7}Y?Bm!``<)F?b;umowX;m);6hS&UlD(A;IlF< z6=3$zhKT-E#VY@EoDYc0G`$lf-SZuBi*61X`>v1M=O2cqN?j7}aDbIQEP;MkzrgKj z;_$-KkDTDTQeL?i*|DP^aqQD|F!XrOlw|y4#`(f@h_u3b{}22%*2%PaAddRvZi8u_ zURb}Y9-NL%r>c62y~R#>hip_>|@dKTwgDSm{{wjASWi~IoBELV2txl_2G zB$A~kLh19!*U(L^h+TL->-fitC{*7@Lt|;;TQ}|xle-QNTvyPS2}-naM23_b8jv5i1WDe}RGfWr2sf!~ zQpMa`Jdc4~p1T^KS!DYYEqB=yALnh1l0zob&Urd+e9flzg6-U$$rMy@JC8556V@No zg0r&u$VN_pCwj|h&EXibHf#@E{#wT8ui>NVHDMxrS%i+8JCYuc7ifQADQ?WnYuRXQ zPo^9JF>c~ ziqRf)hMzIX#PM4$RZ}l!*EEho%cDqYv@DH$D;Q)p+TLLgmanG6_mn}i@fMmU_Q28? zbBSDxC|wnM9gQDuf{II0^ngn@Z*z`42{^q2zT3)RV$Wt0s+tDEOM9_dRf}H{qCypO zcH(i*>Eug|5~=c3!)M{cAQ;n!{Z}rq(p-ktu%r{CqBHQGp9=}#GMDGJCDFsZx0%%a zQrI=-h(%Rl;HLioTWdT}g8QEIaaqzYPp)C)8d6jHi%-j3>JOrQM z9L?{`AYN|-#dv(EtlU9nRf>`)#!al-M<4PyDS&edO$X&ul{jBql3vRyA~(3T!D=&> zk;_Qqy9MOJtEru^;Jp!s+rLBAhD6-pcM{?47gSC^gdJns;Kj^&q;i&$Q{GS}gWEMp zy2Cjj|GmKcJUP0cP@n7xb)XM}b(p50aomxf3>PESAT%Hik5_D@o{dp#uQc$Y z`)807u2N3&>*kT3YzMqq5=gB=w!qWZqpa?b1{SBkRTxk4dEpmTUL-bzS1X3uFp}d_9EDQIfuHxgt>l?02{L9o|Ar7 z8{AEsMKg9sD;A=L9Q;{&^sTFD%P{tH%w!mt#c$k z83wdLV+D$G9w#4n9U2$wN|z6L(a-P!E*=u4g(k*i_eXv5_DTe(wpy?b$6i5F^8q+g zEkI?itCA~oe!)c78o1Z?3p_7rkb@j!>!6iC$0Yv&a|hI!-l9e@{X3tmdV39HJnGmB z+S0T{)05*PO`@sGRN-oG82OvK5)>>}AR|-5q#W-`lBS&m$3e52~g0|%IP@esb<4)L6R)m`iFFD4X>03v79415MJQ3pe(uDjp zzQ<46z{5S7_u(R!|1{LoX3ZYR&>Q6u)ME53o}N|#RROX@r*#i`c=G~k-;k$A>;|Fk zbUHLE#KNvWa#a6933R*t0GBU7>A*bFx~&!JexKo8T~o!3noP#-z6>-O#iZ4khAgRDUK~+UW(aad#)1gKGnngzA8afmp{aq zoJf*3WP{pA@1d7Z8MqhdlB@0-FskSVZTgEDCew|+uo-5G=8Di?wc6y`nIv*Z+JikX z=NI$$)oIZF+JesAUGPD0AL}^~$sANjBQ3}2nPQM?SXPvb-AxHCS{bYQ&yS(2c0TgXcLIkY3cEIr%YM{GI4{68re6g1p?s)Ow-v6 z+XK2lbLTf6Z?PnG7wKXvgm3dyIlj^=)jnpvl{ZnDu0d*_EF~^yCy^QLC*X+XDBgVa zmKj%_Mh>m@Ctaxz`AeVuxiIB%Hx6rGVf8mRz+<-t*1fY6pUMX@_3@d|`s+9NSLL!A z(p-+WR|D3F_hErjH;m^eLExL!AY(2_AKxEgdyX@Fo8M0e)Bl)*pi-_g#gxBIv8MDAFg4^c68jdfwN9H4Qx+M*rkKKUPGx9K1 zFB9?x+=x=+HQvjVB#d}=9gB5Zz)e~O^|ocD)kY=pz z)`5LcG}IljzzwHFxigP4_Wjd_6$*YB{`(vx=__M)k{xNA@*flXQ=16h*@d>wv&fc- z=Q&4|7Pv?aLjNQqRKDHGm}=O;ke39`luyRP6FIN!t`L5=$O)+O`i15WF=TO*D4B7- z6KZ~EFw@@+Vrgd-Tzue1|4SanamPfyQt$|femaUfCdj}~>&<8{vzbh?tzo*ogy2TK z0NHTiCp?XeqB{gW;Vt$wx?IgB-+Zg_cDMxfzWx@UxJ{xDYY*W5rV)6l>qRrZx{!IF zyoke-4SbEK)r{G`GFGEej_&>-jS;2BP>?gq%V^BP_Q?_S?fxTh@>V)yC%v9ThE%aj z*S4T_5|8ZDEoYbPolJCzKAD%i5j2YGAb0Tq%(T&G+ilw68y#eMd5-Ymju};+?nj%v zjB#DyCg#}1Q$Qc=V|CP(P;|*f^d3FKT(8!pFE6N5`6g%R5wC$M1;^PLX99_f;8D1! z190lbeo8ipV#6sZ?7pIb{#@VxOlTHz^-RX#T{Y;P;JiR`OW5M6Q*iOJSKv3=%jMYJ zsqs-mh#c-@b68=FAX+fT`U-}7C6U@5L%g{qoPRnt;KKN@4Lq#PBazLAXtI79bw5>% zaT5EmW$py3C;0)+&N_?MCx_8zr2WFTwxO0~_pR{pp;_c&)=4f~*AHXE&Jd%rkeVM> zhL!mUZ`kixVk}7dxt->BdoMOWgU~<~&ULET!t7P(!UZl*@t>JE(K~ky3_d)B!GE9d zsPlbRGi4viUmr>>a{n40>vVC3P^KLx0S?f!<0vaPGV$oj5TNhsAj$(NT(3 z>kNPgA9li=X-kN-(nFkifn&~1_M|CODj->*3Wi0$!WPLmzEJB7>?$xP8cnATyHY=G%D72!Ec8r;MO&rR2J3a{3Yr9 zhun^5;dUkFal9}sRl5hXwh^*U#FSe5cQH)$S}@59fJ5p|prxxv#jgc}tvkn``R_6c zh&4f%!CvMfY=_hD+o0d(GyZMf&(1Gv!HSeykpA#K-krP{4cjtc!E-U%*`5!Rg66?r zk1@W%$6x5p`@<@z2N6XsuWB-j%YmqWN8@!-F#REmhngf%%io&Zeo%?S#eZSrw{;+# zI++>?te^+4t6-vYJIY+mBXgCM>9RL^MC?@*<2YIblc)~yd=$WZS8U*17F_Rlrx|^* z>I>d*bfwl44d}|D7qF&W88+l=fRVTuNtrA_X1L!%wy_2s9l7VQUzAz#PaZ}7ij(>U zW&G(eaa2}9fYfs{gB?*F=&{Ng8E(!R9%{hxiDmDZAc0WL?UCuD;tvQzR^oc`X zJf7_l$ND)LB&S4{_D)<5=Tl11>vt$ok^K*+{rbW6GTZQvP(FFMNtBG*w6hC_^T3Uf zhB$%4%&d@p=B?awR(-u9_KK*GX|r!L+u9hiT{(^wx+h4EW?jWKT$*=i`(0QT8A)lC z6s_u1B`rsLTc)3Q#~9p;CW})1XyvXcM50fCxQSIjY%!0t&a$AR-Y!^fat#D-uEwd6 zD&*{%0leHgf&KJ&9XRjn!OH_;gh|$+g}WH?M(;D*HC2l2Pu;+*^$bV(Dbt9Q%~9AQ zBtTjp&mc9OH~0!YQ>a>J1pE<=W2~Oc!FuU9Sgd=R+1%ucU)IJm4`Sw0tF=9#bbU23 zR$0v+_s*doRsX}xdqVW^8gIzy=!Ia_DCla}gk(`gnkqU7A2f@wPcI2oyzQvjPam?P z^)~xls~PJZbQnvTO={Ad!QUV8xaD;wVOW}`K6wj^{fHrn>UhCy z6NxWXqw&o-@W$mZRK@?r)+@((Q++F#(Ci1SOUfovS6)F+*G(aRByyqrUOUgM@gY3> zlS!-kQU7_rG`%w$cK~N__oIj;hHut^jj%}R;DvM_xylYXQlbQ$;s?5 ziwxe;Q=TMW<2)u$JO_KS7n9+uDYW@G!AILHh-tkKt`*T^&MdirE0ern$H*t>?9U}D z3f(}x(uf`#nhc?J9(0FI26w)f;RFj!P94q1q4vti=W4r<~Kp;mP4`!H=+aIcUk$&d%hddu*3szFK zYo8$f-DTEy%?)@xG>NYMqCp3OB1!Ob5pb@)4-G;Ssl-?o+?P9w|4P3@!{h@*QSTRa z+I+>i0TrMz(g;_tq*JBJF}~`H3@%%-m)p~QhvckXWVAw@F_6}!uM+owP|Fe0x^)WM zs~}E6-VYmy=RW!FRgts5v|VuY%O6cnoXFn;VCE94nYvOcUO-xxl6YaV8$xOe4e z+dtqiSA&{npTyuud$RHjmrdvX+=b2m@Y(A7?7o5X?4D?Cx}-6eUfrQjW(S@dY>S6pc9&t#u8p-OuB^!)f8sCIq=*J&lZ zyA})Qi(>H9;%PM9;5V!n%!MCYIcFT_W|;lV6I=g&=JE2kK>3*_HuSj#8CoQdB@6E| zk=(8)R+(eY*^J>iaZhIdf^%oZt=%qs z&|3)8PN@>%j!M*)vStTVs^FoUH{B$#hgyxCK%+%9u%PrAYsE3U&de`FMLAKnI!2l( z_}^wsQHh3yjzU4=L}>oP zW{8o^xkG$EadrA+bpY!p;6wS@uK*0ew2hA&8glXGw+EKL(Y1R@D9p3))w+>5F@E zhGlh8C^v8n?sn|K zT;V#Q~4b@B4Y zduI>ut!{eDnr7(|gJbLo?v9~s99#&~4Z0G!h&(9tuFEdh@v)4&&| zG+m|w3NI}o&MKKuaIORw#A{)J#udoD$s+*^50e$s7+U&7llJG?krw0gpx$}{@BS@e z&3e7Cb*l(18%$=8CzqpOg$U8Uu@n}z?|=w>EBMhd0!Lcc z$GoUjoCu`uD5L9_a2?J6KEtg#T|Az%gDh(nV)lPCCueoVp<+TgEx4&eiux}z?H3<2 zpUgasU@>3Tsg;-WCkn=vv6{~oWxW)`VkowUls{{@@P6$y*DaJp%pPWMMRD0tV;`y>?#p`J6h;GX@BPlr7wk%=(NnIg z$tv4J&`y6tdfx`RLGBVZxRkcK><3j1QD*YnXiOEj$81VI$z?;Q(~c@V z6n|BPwvr7@U&&FWL=%95yz8`*5=s1hZax`M|Fr)<3sVoO=QB-7kIE*6)zb{ z!uw5|>Cz-G%47{N3ATZ-vL={LII|wASKbD0W(M;$zTmA?E<0j-o&8hH?Irj6k(GCL zV8D(ExGnr5pLFYyz#Q)Exy_Q=9nr;5MJLYBwu}_dRpL)t@f<8$zcYmv=GdZo7UtXi zg=tbZphM*`o6sggW*RDz%~zfx+ISF&wD0^g^$VEu-dDg;#Fy@$PVm#}?a+Egi!H04 z%q-5H!%W?hMb1x{ zkMTp+gs0JpDQi|=cxgS6=Kos+i&~en_(L3PoD-nubQFyfG9i-;`=QgBbFA~1qe-k4 zc|3!Ug&*a}L%n=(x}!xKuS?MXG>?J%;xA0;t!Av=>OlHEMd;-233T@YJ9b*^I17g# zz}Acp&>l96^_Z4Lhbj?YXIhiAs4Papxtx)ZuSAa{hj47R6_qTBCX+aqh04)S>}K!f zq_F)ePi(>#lHhoXzY@6_>gUDe-X$GW{FsBA2P4_7IfnFWW+h+evnVb3Pm-8+6tc5< zhv59|BcP(ufKHN|=#GJEG#8jj@>;E6_uYJ2HkLrtrNY>#>%ml$3owf4E+U64_fo@H zEh2|6c}vdg)1ELj*jCld$gbm~jY1KKWZuW_)h8j);vX!UoJY2*I+45ji|Dt23vegC z3zm$|A`2f`kj{D&@SNcZjph|-nCC@qs7g|aWhNw{{vw{2`^V0EwwSC{tHAFI)?vHL z1Y-NOh|u*xL@l=!z6oE0y-$zB{2Q65cx^iEa;U}ix(nfMh6J_9+zUdPwosJZiDL)V z$mt_{!LIKq`-WrOYJ8C+Km3BAeW?&_{89&WaTYG!;J`*?--LeusdU=2$6)-h7|h?a zgOuC^khjW)Ew7~*o1z-@?zV)GDa*L5kpcHUw4>Y2Z0SIfF!uG#hlST|Xr-GLk!RL0 zhqJ|*C@EhgPMO>{V-{$0&sU=7UZ@Ouf`UsIlCjVOteLwZ`S3TECv7ZGheEi!S=1`7 zJ6+Dsk(I~hvFYUJhgMYVDrfmAoR8Hfjb3ow%?^9k@C$6y=t(DW+FZ)Vl4>oQbbl4C z=?;gRPSzxPI0z+NxE*$wCGAV^#$6oO>ACezs_DsRR7fxs)BHlXshdZ+AUGsj zyR#!H;@rOHEzC@mA`4dQ(wJEdtYKs@M!e?E?rn>qeSIxHS1_e}XLhr9wH944-Wvy% zf!a)-OdafgIS3{HZ6of|4e(6x1f0M51_R4g(d2G9=V0aD(>?91$9YxeRZA!qlgGUG zV!q^=(`J0XA%#k6@`%yRQrxxcF+bS+0r;Q02(`&6d~uEqcWI>{^SwrayqwEtEx(+E zV3lHQ(`1Ms=gf(;F#{d3#ne908Zy2&LM)djpX6E#@tU*Y?H?)H?R^hsN22?(L7g6?Fl&{vpwu7JM1qe_jP zf5OiIAsUr%mHDCzREU>Me@hs0?9MXq#!@_2{uKteEY91CmE`FS8~P%h>z*uj#OIT^ zXMgw|Ue-SiZbeSy$8t}$lrKOugNy-j2D`q)g67T;BN{{VNMm*!9NGGp32k2val#g~ zZ{NnPUb^Cx3lB#BLbtnd~;0@eQ>0HL=f%%a=FkRvXR!=svX#a3gc zYuaSA7bt}ts|LWFyAyOS`2nGrfyeiaF?;vavDu~^!)eTbgf0{&V)1FL#dhwuRx+li zhD2z@Dwg@{vWwm3!0qYgWneI&XqM&dbVgi|eBka)&x(rSaN0HYRoYXAohv~Oua_k5 zyR7l%^f#w&C9;_t5Dvb|co z@$Kd8nM1;)ea&Y4IVqbQ)QBJxZXAJ(fG~RPb2HR7>4Eo*aTI-NK|`1LQ~dxnXq0^q zzhcfodzUel+o((z{o4+A!G@Nb4#2Uf0=z%*4mv;C1ZrF5X;5P>zAufSj}5M&H#es- z@eafplHOFzxe==~o-ivurO~hRexYBL4STTU2`1hkkQHi1-k2NE$Md;!ojeaBmRw{W zKOTnv#(e0CV-nzzJcBfH4(0DSh2Rk>u=prIgmx?@7ccFGw?*IB_Wmz8Wo#wgb*K}L z-jtvZImWuG^*uE0-VdIqHQCjtb6I|(4bAzqjMptOh2zfYK%JT;d)}#reZI4Xoh~?= zy*^S1mq!ALvqKoY+r@(Fw_zL`kD+fJpJQvPJl%e6HSCQ00O{X8BUz?R6BQ!Z1Xt9Mt>HVohcah7!&P;!w&)&-la?_I9G>n`yjRQRl(;Od7|~v?mw5 zN0`w!8D!>-WOnK1b{ySE=w_$8&^y|ILUkOI`pY0#F5QgUw$WsFiYa+er$j>D2oT|% zzc|@N9gH$3l5fMU%$36|99Nu)&mX)86_w5Gt|>JwMviIJ{-Ymn!HnyC<pw?E+S)4%DuN|CASwem5 zjcMTD1L$w{38U0LVy(U&IX*sxNXA`ag|6;}q4fcD=HDT-7y zTEr@@aiKb!IhWgsS@evhEb-1i0>6TLQS_fRy)U*Gf8G9tXB_ll>eOiZGXEB|xG0nR zZ^tpg?KWD=mohh>DZu2wezbeLl7?PA1V?6?5ec7B3{i_`6mRH|6y`Fky}_9GF~fsg z&P^u?cexq*!T;DG?ddSnHlImS&u7@UVaz$40Pi@~6Mx4>a6E2H9)!n1edcCzpJR$0 z*WuB&b$?iX*aGrhMigfz&4i@kbXvj9`xJNxiX4k(@0TF@!%U1>q4xyeeGP-fyxn;2 zryNMj#34_IK-pFohb?C-S}b?kF(T(DGXrjYaPd($EXk~3ch@&FGAE>{t*;2#z19%xK`}q!$3Y)_VY~sZz5WLK zN3Wvdfs5!YHk*!Rayc5IT-4jDMl*Jv=f9y2cq3>6Iebr;Y+jZ_f9x_sVd2;8k)c(r z_V8x9*3%biKdHi??>OgoD+I*=cgUN32NoJjklg+IAfdMbj$K@a+ssYLF(W4^>v)3h zexacDrpt*D{|faNHHcwfCN=5Zj2pfhvjuXq+0xtpKx9M)@?2!eN0X_vPX|9uqyau^bX73o zeDeu(NN6U}@7Kjbx*u}6XZ=HIIlOZ&g9Uq@^54I#1owkU81`!p)nN6QB;|!YR&TRiuq4Zc{dAt82c?CBD2XFS}D2m6}v z?B3I8-}->p@_8qhyW9nWK62RP_z11Ne8?%N^u|ee|ndD2uu;2KnDID0RvfWvi+<( zqobop>lSS1O{tZqo+E2$U-Csb9c)go%)ANO0gq9lIDyQ;0@9Luf$IhRVrn=KNF_hV{EDQ~jXa(uTy2+lis&`>Bw2g^cIGOj`^ z$}V7Hxd^`?TasuZ4@BacP;q569kyRUnT2CmTj$AnG7Yha>ux!1*~)x0xWx#M46<2! z<>~y`)8H5xNYiZ`NkpX=PL~(NUguzZbjOmu7LukDewfnFR)n~9D$&*lhaoFmmFq%t zEU-WJ%xVV{@^$wT`be)5qSh3(6jnF0PlJ_s=R(xce=FBryyGnI1>rfGISQ|4!Id805`=Kd2G1_)5C!z<`>V;yp&t^^*T8hkX$!W_#PyoLpm zwA{mws!b=LzNmv=^hBOEe=sFK)$GXSA2%7jFJ{F4Q3tBrVeyvm3e1=k4QF_gB#Ovavku>Jmz!KzTU@Lx? z?nRj{KO$~$5SN(T#i#bKp~`3*dB8cpw2pq_R~0$ah02<6Vsaw=9H2v9g`Q%5_~gUZ z#l_gv_ZzF1-sU`d&8*eIz06NR2dMjDfqiQ(0>6#xh}2zz^GzJ1>82#>>gdK!YZRdx z-N*0(=NPDZcoe)>G3?tgQ&?(O&pynR=ZzQF!IG*&*z;4Io|nxb5%-IDQ@_Sj5m#-> zO_m%-??j;H(Kq}3^--!MD6r_diCtmqS$7^q!lL?ouMtcS^M zo`qd89+|02S90v!urF=+TQvwJ&k91>Epc2EH^Ru?+fMHm#v-OvgC_UyW1imOg;g(u z?4&}nVW|n{Y_=xX_=wM**;0e(FdEEdQmVaMHQ20 z{S{x`djk)fO`$0B2b$g}r0JzHu*mE&dtX4D{z=VbzKZ{2MXY|Xb0$xM^_E}x_LApu zYKk!ZDBsJL?+;`HcdS6|k|sjx4O*{Zo%d+cDGoDm zUDZqTOktk55%uU=L!}cW=xKW`)bpB0X6~JduEh*g zy)cHg9!KENA0aYiy_V$uiK21~^Vys;5^xe?XcWh+&GzKxd)ZxVaj*^ja3PqO+IbOO z)mu34Spw`I^CiapHstqqXR>o017mt#glAfTx0kS(8M*^c$C$#OhF9=Hv=$TQj6kjF zLTD%#W^z35vtthnNz7F_c<@V@WCcpntX>^@r|A>_OYcKAXtofkq&cVv}lgC*yze@qpXLA`n zGd((Ia3kJhCy-N8b6NKT>u5*#9`vwXiofo@hb>2=Xz*SQvOi!DZa(?QN`{m}ueua* zy*r2t_vq1IPU$U52^v)6lLIZ|7&rEZ#lbkM8IyYNK$5^2ct3m%o=kXzU!Tj;J$v)v z#$|DGB;q|2C4Ujwdz+YN2Li~Sg99MDvKmugEeE%Z2Qbh+o$job2G_-2v^*o9xNKcb zY{oQ+$d^>a)R#CZLye}2bYi}e0PH(*hW~f*EjVVjmb8iH!lbrjLO$BT4b>)A$XbhL z-k3;V7CF#uS|0S)b01QDbTY9Pxxhqzlcm=s2e4_H3wgVBhzS~@%$lFU$n;L2k0rH8 z^=51Opw*XTgEEai@DG;uw&Btrd06puH6wM-f~rafGMkkpabKG_4S1hTSA>Sbh@s!l0by#G{H2F`Q)jTKCzPaWphIOa8+L-#U)WN)agu3b$6me zKr1RQnh(vlma|HCj7hMAKJuSGfi_*P$a4EOUW__Gqa?Nf^s0nH-b)% zsbTNsx?;^0KD&NnJ_wmd({ABFMr&prgzr2A`*?DsYpx`?tqX@c58kkkA6CFUHBVwV z%JJ&2)N>vOX*w!qOL|d+oPBLTm1HeRg3T4MQ*$Ej7I~PEstAW>PotqfD^Q|t3#`1D zO5OU~aPn4LST7txkGw8q96f3v{97Eyec#TwbKZeE(^<5Peq)c+nz0TeRoSWMz;ez5Ff+zKnR|HSJJpm%0aM>4dNcz=Z79mnVACOApB_3fMz=_put**EKR9|nP2Q_QH}z&SNRVP=q50NbIeJ5b03OCEhI~i=^)+R zh8Hg?6GPq#{2xW<;fUqihG8o+Gh|10l91wkuA7#OrchQ2p_F8nQrX$7NlKJ7BuT~l zT=%bq5+$V24n;$|6zY4v|G?`V&wXFlc^=2~$BV5Kzt2ZYkR!J;nHqC6DsVIBaVG*r{4VdaFgLxm4vFT9>GK%(O(}v~b)wn2? z((@(RN3>W0btziM(;=bNVMH{=ia0+g1C>j~xPGQJ;gSHbsq;I=&Ls3m_XsRHTFXl` z{|Fm80s+%Fj^=Gea!dUj8}}!PUDU9b$$CIxYrheV@^hekb$J@NT7@o?4u#Q21K@p2 z2%g9X&_4B*sC)Jd(_bD0u4f%ly7U~J6LLY%9h`gBU>FW^^QE|Xb5Zn@9%?O=z@=-# z$RBkBy5wF1R(^PbdY%vA$9p?6aJ!a$_}^;!#KV!Ucq&J|@62UW@Ak4k;_Og)t14+a zCc|a26v$bPxm2RO6%M$nQNf31RP5_{DD3|OyCO!iE>0%1GxG4gbQgTJvZja5U1FQKb7IlcXtp&c9&QXS zpc|7K!R#f+YAKxq`gc~drnR^5d4m8b#^1zFZnkG3*vO=ICez{S6j+m62Y+=&p-Siu zY~K_~-J*5q^X)1``1TjDMNe)v+@k<&k|k*3_nI4w<9%0k$^YgJGSiB<+G3d6}?`+rf|X7p-lE zox7rFQR`y%Pv3Mdf3F9dmHMD--zbWnJdOX#<>=ExibT|`2zq|oz~hE)I4H1?cXia2 z&RQdjZS!W)3oSRQ6_Q!2A4h2Fj7LKle^ z>rwo$HG)W>f}YV30jCT!^tR~U<6$c(#R2CAnGboWYWXER9LJFPE6TG zUT%{hu7c83M1Kn@-Czs9H~eM{i)wJyetX#XAGeE>5T(+pIaF_fGPO8j$X&{939p&! zp&i;nerasyyIr42ua)gV&Cj>NZ7>y03>TBp&)4wkr3CiAi2*q^Q;6n@HL$rMlW~n` zCp2~31?JFF;@+T0T#u{3VNY)wshvmu07DKxaHj7=&*F-1C9u&7Wt8~;(A4%bvsTy- z6jF>x+a@LE(8Fd{(xi;N%fAKwjww{!lh5RM|74~fvZgC+jd+@F+03A*EZ@1=5}T8x zA$?^8td@JgYzhiR8^dy*nSv!LUAlqJOy0yeT6g2M8=FW~Uj|vYRG%msiL#P^UxQUp zIGt&igkJl1aQn{1w1{s6Nr}Gn_k|*K%*rQU;vBGKqZ3ZAQ^PrjOsU@CVeEfb0L712 zQ#IFvC~h3bWD9@9D8-qiV&)R)?OlqmE6r&V$A8K@IF){pt3t0w4v_vt1E!}8@}BK+ zBg@r)@dn9jG%#raGog9t8-JC{E3|XHr=y_1?l2UJ&8ExMvdDY1CD-bJGe$zDkZq4h`aNv zX5-T0XuQ2^Gg+Z=98!y(VnXL$vN9@)Y9c|dG>TDTCQvy`o7Vmopj9W2LcMb_Q-yMb{H?^n zX=4Zl6CtQjkbOLCNK51ys62ljBE*)`TdqOGxn?SjcsI_b49#T@86SkQ^@gPDcQ<^| z3!*ZI#VF&gLH3=z!NerzvOgD#)1?ss^g@^c2~=4_zo`w_+~VH1w8TX6tA8G;*k>_2(?{?-Oqt&i$P)Hrq297T=jC*_})qDPkL~rb2DP?K)HL zY+n(kj0x|)vtJY}X&fs+KYHB5aW)<%muuqGsxVBBV&QIYIc5a;1H4+seE7ARDQZfG zhzTk%S6GDPR~BId_YBm#zm(Wr)TG(D+t{TyWSCfH7J2=A8ay!l%`ep&!qTS=82(k0 zb`Rcyo{W!Jn|u}?{hUm~@7+TK)nJZ8s0~kyx%5qr zxY6JJd~|@+Rqjr?P8wC(}&13_8(J zgg@0?h7Py1fS1}~$ULG;vN?~Fk4-(hpB-Xd2*J&huYslH7pQGpNVOFhRDWhc?u-j! z(4Zu(8+S%+*$Om?&qoWfo6MsgFETrC9Su3h-Thy3CofBVbc>aR%>}lgng1Khx!;Q{ z=_%-}`?KDi^TWwlO(JTBVbu7#BBk4P=yfq+(#SD=+#LCMe6ll>D(r~CBM5C<9>3Jv4VR~9VXux7;G zj6%Mu2=C!jFpcl#6`-JJbI@@8{DMvqqhQCA5?;FVLGJ!NDo^1B~z7S zxx`u3kn-v|?_q-l@!jFhR$US#wtW)NUaLsw9-l%Ec}WxNzmoL0R3cetu^!S-{lhC( zlX1S70=b3eR4G6R;ada5_a}h+Jx}_4DwlE6PoQg(qS@E&ccEkT0{YZ>GCMYR3+ajb zg0`2oKu5U&U3EpB2Kns4>1Uqc$Ey<|aj`PdDfxtJ{|tkm(oMDtp0T1A)2Zf6Nn-Mn z^9YY~S&MBGxHI1!#-l`>Z*WtD=<95zrmn)UUz*DSPrVAiQoo@8%^dvP)dj_dsiY}u zgl*&iXp1a02>UXcu;=-B=^usk8)q5io_;hwzM7ovmB*x*Bswrbf_}Pl8;_(JlDjQ} zB=JWt`q%{0eO}i=VY?Chs~%;iDaezfYiCo5?}q$UT-HG>(HC0`q9C$bmu_lxASV!?Z08i?w>vG}2KBFR+!$b@D)kc21= z`otiR^rU-{u{(q)9`=N!pCZ&nuLh?7GKOtlZdi9T7Yy12$aEtedR&siWNwCa8D_x% z>42`_J#g(nGv54I&&-p`K=oyU^!$5e`t#>4o8@OV!sM#0_3!7@aFj@< zIFtJvMWpe?xJ+4VlrFt5f*2~&fKjv=a-ApAU7EL88F~|RcUFOtiz^-byomiGphae1 zu*WmCTD#I`ACLl+!LbC z&08>X`a<%sUKoqU)>0G3gSk8PF!l&fz|Y+7r{~LZs<>n&p7MDI@eRX}^E;U8mU}Wr zI;n8@^b>el^d9NnD%g?S&3BmLOrK0UhlhN6*%8l`WGa^>XUD%XCx4j3={5->AV91x2v9LD<20_m5hX8itPQDXF6VBAReOHe96i(o z*KTcN8Yh3^iKhxMI)j<8c(Nh8|A`LS*&7QoJGAjyr3Sh4$_vMuQz%a)mNI9maia>y zEqrC9P@ID+U@u3@gm$5{89ac@(L12KEd$rQnnIPfRYJqQAR_;CLKm zG!=`8gpLfoZTT5OnjfHIi!qrzcM{rewuO1h?@(Xv50;hbU? z*RPQv{8V{J*uim{^9Yqo-%3RWtw_MF%eeUTWb8gJN5`6X)52PgS0!^9KaYKcpg*Qi+gllc(OsoG3+-hpS~GOiaahh;?&AK!W_HWy| z%&H~RY&`C~h3)<;7-QvVa%8##oL!g1ntpl?mv0=wACsG~<(DbwoV(9IR?!0cuWzNI zhQL1lei6;hPT_6dd3M+&0S@Y=(eWATv}+YZMypcDW@$w(AH!f3*LD7F5{YJBIUFCV zA60+;hSJ}Uarf9YbX)iu-uZ9CxjRl^OR+7yhsnfeiU#{LU>P0UKZ}aYd%(@(CR2~x zWw0eng|g?*v&k-5bfD)JHprJU>F*}8cgo{QyW|ckeefWj=^JO=;`8VS<1P#xm_#EM z3De|)N{HwjwPB5>0U;vPTRZ}Oj$7dC(FgUl0&*mOekU{hUW&LDH6Z2KtcD3f7&c=N zH*FumfSqQ{`5UgpyiS5-n5WPQQm5cYtT-u<*MzItuP|?#AiXl8Kxc)B5Vz{%;KuE2 z$fqwDo)=C}Ozz@6lpf&-#%&P&r~rXR(UZ z9Li-I-c4g)%+X=b=RQY9b~gK7PaI#LlB2ix=+Uc}wnBAS45$q(pv9MFvPVS@aR0Lg z>{5HpI_6yFfBr8M1%~q3-qQz(-OxnlL0>dEcUp)>KMTbp%TD6!OIyg8-E`da^)>9) zI0+Wnszl?sBDr$bnMj^Y#~?`~8Z_z#K}Mgk_~L1f1=E3VUa8TcY$N7dUOg`Kv_gx@ z33U9!HO9%a46BZ(!ILkXf8=uue{}UN_;>XRY&gdG4|k1%cZ>?=iaL{}V-L})eFaw6 z_+UwnI^AQ*oq6sZ!C4PgsZEUtf6;4yWY;Uuk!_Qy{8A;_lD5#sQK*P^i!3mWF5+kvZv7b?j;aNJOni{I^@~*|0kgt7R|y0<-bVvH`s1-Dk>9$AI2Cx~W_>vlyzmLL zJdsEle6hNwG>=9un4ll4WVy8@MT@K+C4yfGBw-*tzWrx*Pk^;2$2G zr$YzBHqQl{SX~lqmc+Vs3gV1=FY(Y{9XR>PfYdBLz;zoFAm*koZrs`qvFqbuZP_9; zcWi?9M|ZHVm3^=nAe??>>khATg&(`GpnTDsX10}Gt-+qqz}UTiC!=QNc_ zBxIv@pA0z@H46>J^TE^gBI8r{(&nmfF6VDMkEeJ8Fy)2@iBx|MTQnO`FiDC&5hV2D zK2tiRs75!|`@rnIS)?`j0lu8T@wn9(vgLjTJ7=*Gd1;YA6cpX5+;KH>%29u5+9JIEk(-Bnc*zEQ)xkE`jbw`U6xwY0o{|1p#+WKEB8l@oN&O)OZZ;`O zja8^0#0n#=e(#*>HnspM=-B2k^Ji%U%BQHRB??9l+u z0n{OlXUE#`tNCu4l3W5tC)e2Ur{}@fF$oe9^B1PP7G_7@8PeCqN0IoB+F0JUr%to> zW51W_T%N2-!Ujq)Fm3S=Q9rx{L*^f>F$D-Uk&UQFuA0a$G$OU>g1`A={2aiW4deXz@z3LT0E zr{W&Ap-+_#2W8W+2F`ga>_dJ;yVG^ziA-o2m)n^C7&lFFM(INmsN$J{{Yx}>XJR>S z-U{w_lV3m#^MvSsPijE8BpqE3&BHBiZ&)h@0kYxdG!l~{P3h#RMD?mQ{koZZ9gQg@ z-_(M%*jKYXP4c{plVpkcFDIIu*U9yMkFfRE(x~sNUJSMQf}2eDWB9-_#$45(-rG4J zCVZMiE^a1BRoS#P{24@?^dobdBtYniD!p+l36&4JLw#5xIW?e!mqS|k6)Dr`^=?hN zQ$(3D;pRjand3O`9AyTPZynO07z2fL@LG-PTT{n2_9Bt2BA*YsTU(VdHrZxrC= zf*i0vH3E$?K6ut>DZY&Uz|1zbVrPhX(Qa2O;{PKbUJg0&J*rJ<=vvzwR34Q*BnJ8#U_Z2>5&t})*(t&J}q@4#gW==4Y5k=FSx55WGhFGho zFgA<4!Th`gqocKx?7S*ZWHZgt^2$_PtmO{>)wq$Il|M0T*Bkt^ObtD_`MUg57d&D8 z3pdrMlD;_>q1>OXX$0!8lfYYkVFFpt&51hdwZUw*8MuE`rsIy! z@!;4q_|dOK@837Y*vR{68_@-e9O8)YEG;;pyPf^Hasm}Oa+*6pZ+{~8%!FRK%5~@^N7-zZ8_=wLr{2$v;2UF}45 zvX6t!%f)n6b1tzT%^{nwx3Up^L3HIw9~_w303Y5_GziT@?e1@|yTY1=us4}>ZZ92e z#4$ua>QfC{4>bO<2CK}~VbO6Z;xx|jPPA(=>9Zu&v)PMxuYG_ssSHS+6NOcb4ZU^u zEAOs=Fqu0nMW^NpGZqHTkbd_#xE%igF-K-$@VEnPW8Sb2uPM_z_dc?9bu*~NoLUG% zN0drlMP>>s(nHpKFym(LS=@~H-ZV#=x;K|&XHFuEk8dR&3(UyRbUSMM>;vES0GAbe z`4K<7bD+dAj+8tzM!PE~A*;p&Kin&38b27LlFEA=p8p9bPkLWv|2XU{R5ounc7g(LY@dy1L;^Ez(a zpO3j|FHvHp8u))d0?}L^74nAR;gvXE#Mr#shPx{<1fy$H-uf7L&~yV;quNE z^i-}8Nis_!d-6`<#7}KZ(qm^FD$=7Pv3H@;r2|*??`4EjrqbB`$FS{yCz*3Q2^_=L zlRNY0)8iFpG`MXPSFMeu_Cc1geXPpDVyNpK#EUwu?YLMn7Nkh)?n93X;I6qW@BjrA{bz3F8 z3RlK3UL{QORL8!e1TgD9huP0EZ~^z+^)-2l=^rC`4eu|(i@%9@qC$lpP7H=u2d*$H zm9*et(srEZ+*=<~EJiz5x{;sei^#UQ%W0+K8K`NNAuNiKzfV%JBq{}^e=cARAO}X* zZGor3$C#$%P4JiF59l63*wFg|A_^WMib&A|2|-|4+>ADBr_p~(iu9Rz44$(~f-^U> z$mUlYsIhn_8+lNV4Oz=&DKU#?g%v^0*@HkVBUTo(BH@Hzaq zQA_y8C%=P^j68^0F^`;^(!vv&n26eGmgLJu7b5v)F@~qgbDb0`YH;~8zh2)SAB$81 zRC2-)zXJBHa5`PqG=$xa`|!AvBzcxdn8??bWa?K3qKGR{;?YaKs%;$vzaD1=hIAPn zm0Ns+`&U3Fi*tYvmf+@I6=q5IUG&`(#_hdDN%-2$^v0+JJU{ao2DinKDAoBqjlePp zYvT9{JvEG)%?CVl=_MX66`}oKJ}`y5=FrjtJ384@lvn%v0lqePjs+sy*w!i2Y1rSX z_+-&Bw*Ny6fA$T|v9RnbyDCMH{x+LOPX`@hq{L;(@z3W$H+dDkC=`k`;stXf%ZkZ& zo=eMul&LE11(BvW_K4s?-j$Dmbhq|J=Fj@4%#>-0VEkVs-FoL9_8R|Yc7)Y{dfpHA z$VLZz`}PlfdFn*{FN+hKUEhEkPtjwWjEQ>sPI&uE8yo(bK!0}`TlYr^woTTia^EcJ zJMJ~dh?c?vr*?F9*QM_I@_0={kS^Yniw3^WFfnW*JIe7KBnG1Jhio9k`pMz4mlaqR zG>hC_JeRH#dC59kI-+i5A3I-Ih9t4BO!9^!ux{Bz6p_dxt2Rxc>+BgC`>~ZRbU4O# zRl0%RL|s;Cwi?~z)`rL8Md^xbhuFI|c__3s1y-RwDGMIOdm%|65MW1Iep}JLgE2Tq zZ31l%NFfO_S`cPdgWEVB>Inxgra#Z0xI8hVo$1yj)8{KZDGp)pioRmKixV(#c^OW) z&-KRr-ea-cICJUb8Yb`UTmHCUH+x{A0M)e+gXE5I&x^a{-uR zi&%vwJGyuD73^GenLV++7p7!U{;p-(4F7j2c%G`^=@p*Ez4_)~Fs? zXyHwcU84S`1kH+`<5#Cc;1(pr@i*LPYrY$1Jh%*n6?bsKzd#^)W#}PZ!n?WBknaDP zh1P-7aoaU-^w>R-XldGFSY8y_T%tsszA^a0I);cJE9E8bokTCyW#Rp!=@?>q7W5ab zA>uo>keI>gY@N>{+#sTM$&8l_%{t;^^FR1%8oM z3M`aPAV#{X#DwX!P7u-Ky2+0rLez&WXmO;|UCijYkKX94m5YZ9{a{{6Dsi1*$n1I{ zLJj7t65F0I{_}O$;d@RXjeHVK+Jx4jnBsH@U33J;Yj&{DUU$MXlW;Bp(9C-U7R1k6 zgRg%-7vrcqZkD|WPM?H``Gx@m9|C9rnOU# zGJ9X0fWA$LQI&qgTT77lwl^6<-fGeRm_+Q2twA-8S7|i(5(xLIi!;BT7O(~n?!d^}!(eEbja84l z!PfTxChwj>W4iu;N}wb>Jh1{kt_rGm-z!aqd@mw+pN8|_Qz<*R8y1LN!awCP@Ng~z z@}*W(Q{WJIroHFq8$N?YIn|uYy@Oe6%JrGPsnIKzO>0znkI(s&Q)SQbN82dq4%ib3{uH`>!Fk4H+0dSXK!)xx%gK#df@=UW#`h^=Xpn=(eo$o?o9(?9TvcZ-<|@^1r+2|qtNSC z4c3eIp?9?xY%&FCGw5RVX70t`LIB=%{v=jBgc&{ekoizq%JM>Z^rZ;5duj3{Mt_Ex z+ds|#X$xgG%`e7f84^^}ISk+JT1x(&5h8*KdJuJrtrla4d$ByoJ*$9l(MADHD(z!a2+Fbe*&ApeODWPGf`moYY7s|9};k(Rh67kdk6tz z)>L@UMHqNWaIok+?tM!@WnmzUt=a&}QTinO)ek(b$>Oqv8jeAw2N7f@4VHY0mXjLr zNY{3D(Y)DIG2;jm%X`T_NH95OyTl&Ss36*N+G0T^BKq$PIe!Pry!uKp<`l8Gu($8M^nhG$|dN!}V!I>AS8yI5PBrkNxxM*Z2-NvF8tS9{EtF!EnSy%yyxPG7jzIk*pRHD(*XSoDo)?aSdWILBSQUNBdv5^0IrX^I; zZhqVTmw5E@5VLW%GZhos%lVwWN#&)LILX|H78mV-ic^(vPfQuSzuaOUv{({}_Y9pZ zww(FAVJ~>^?f@%|DyWZ1AX9#DbDu74NZ%9%2A5Rnb=DdJaya*;&~xUrkOUP~oMi;@a6RM58R48D77U>g~?N(c2~buE8nz-K8AuI<4vB4ZZOGZU+|F zS+LQ8+u4o1iI`}sOdgf`LCL9o921=LS(S2*2p;0=>-+GIog6*2U>>b{VoL(-zc6k8 z8Lm43+dl`D4Jz~pC?KZU(IuD zg}VW1L=pHqq(|a_>#f*Yk#p~6F$;=KdG<$(z>c}kOqw9hRD`X<^Y7QQlXBY`E!jT2 zw5lCmzw@9shcnsOH@je_Tmt#zszqa35pC6X!|UnFWQHxr&0b>7KF`yk&m`Z$*ROks zh@=@^bd#a03Ue44hfvTgzlvh|bBU@>4%e+%K;{If!~Ih-(C4<6c6lq4+uwO~)yPY9 z{FRO+>v`nLeMQpgQNeMnb6|(PAFW-gM>WgpQ6?vo{gt8*S(a6J{G&Gs$y-2Ob?cc6 z)k5&Ggby!fO(guvcW}4nJM-*u40&iGO7e>cHFlL{#;uOQ%jA4|xwjfa`ZPgLXD_p) z!v)%wJHijGDp=go3X43nh-LaQW}g_vH3FBpVM#T!#e5P`JS)%Lt#yf|_IW7RHYWX^ z4{(`m6aIGP(PtIYh>m#=`e$}BhYpm$w~JP&(YTZSecc3q*Q^Kkm~bN5|BhAkGbR1* z)Rf$84&Yvm>z9+*7jO)?Q$2Vf=oIXrGEQ>?}Z54M0PZ&4)4BOPTxs9 z#=y%r*g{hg)PL^AG8~(9bm=;J^=vw<8s!-8?xF1ZyxAoAZ4BFFP=!4=FT>lzLa@Ai zHtz7h%>HSXVs|;4(CMMAI4q_`S5{A<4=-MV_o9Um5IK?V=2?;J$}do@TYy$B7-7o> z#OWQIFbF@_1Sd^aup+&kn0cZXP0riU-@!dhC5XY&z2dk~vw@iwJ`A;{sl2|jO0R1$K+3M;-@Xd?ubSc~*CM#$@SF+V$RnxsKjE+YOB@^gfy$-tLD9&JUA@nPMLWDye!8hpyQXjTAN9 zZB&M?m)GOa<5(2c+Rj)+{$e6#SE25Nj{wVinZ48WN$vcT^+KQ8SqFPXI`LirVvHZo z?q5$L&ds4CI?-V1%aB>o4e-_1gKE^}pkdTS=#)7I%U8>j)%N#6X`}~TbMon(;yivB zcSdJK-eJrICs?%FlAV!>sF;2jV+yv=zUA@k;~VPK)xi@Dw{o-8ogXmj{24g?Jrb{- zo&%}Zk7IQw2q)lY}n-krGEDGkVBnJH+ z)I`OUX=fy`B5Yg8sK{MBy*=agdb_AOUvc6F-L7B ziH@+OGX3u%IQ=@<2u{&tUFXy=5mEQ> zmE1O({KJErO*Elj_BI@k%tX7iL}vaYFP^r-L9ROx$Ysu)aLn=?I<8SA4$B{at?LTZ zSRqO+4aV@sf-o}dEKN6W*-3^b>|)oa&Lne$Gw`Wg7BNt)s(*Fj9@Ys<@(Mc|aK^8B z^vPduINYU0tDFdRYu^bXtO41l*~u7+s!;oUCwfxn6UwYIrg4oyBuht*Y}_u)q;3r) z%O;4E>0e&hXj~DZZ;A`JuA?JrKX_ZuUhV_Cl4AT5@d13L<+1EvEBH;3p?`+6iJ|op zIzRRRxa#Rb{|{@T6lhKruBG5Mg%*T5j!&>dhGnOpX7rPUVaVT)jHRB%Z%SLS_MQ*T zRhfaB+YhrcH(crO+T|o`e*pc~6%DCBljx#-`}kLsaxiE4RI2n}A$zDtkT$h6FhWLc zXw%oowyLDzXx&w2VtzI``1L8g@JWM|F)cEob^}%0t;Bo$pA!AnV@4*HBom=MrOZIN z3h6mzPYz!o~|%qL9T!euM|n$pJ-_rWLM1th;; zhOZ-LOs~s+yd@)xLosjo_YZ#qX~{{%r~C*iP86YoFAZVRUO~F#{d!oW^NsT;YSWXC z7UQ7pKGtA8=bcNEslO4xV|TAh#PnQjK{kQ^*ky z5iot$%4n}$PvULfF#gqhX|GW)FVL@@zuh<*m2$6RO<4>*UGj>3naRz&o>suUUzjE4J-tX3TvF;X5~gf5Ic2qc2Sy-p->RZ1T}>^?N-2E0v0l zUt_0l;=!)70c75Ce>{5odEKv0Q#@z3k~~~TAQ{8Rg`{FgVGz8Fz@D-;eIOzR^#bkyluthg&t<|mvX<^ z{9O`!n|6ELx5Sa`jmg11m9;SC^fbm!J%um3{SNp1&t)@$wvphth3w5DefZuw4KgSD zV(Pj<;5*sS!Q#o_kerBdhkt=lV=j?2_`{5I`8YL~a#+uGpxzvu2hNwjqkrBC8W=nS zLSAGOe%(?Ou1P{{m`XPVFQd-y^~t^BWLz&3LQkm_VrlpT{63Nh6MCyazsZ{1^DkzV z8uaMSg7b{yvk)>}g!5z{)g>{`_LMDNfDav`N&T<`)X&O*KW$0OimD){y)_xs)}2B3 zrBQ6RgCIP)aS$)OnnGLTBfwW}F6wUXfCB?!bjiR`Jgr&=W(gdd_m%=YxmC*?cACQO zOCMwJ*)}pwCg0ci>*+G`bH82&@sj#r;ApZ8APQCWLXNYn)-}Q_)S*wv1i-re*8`2TRzgIvKKBuJI8Y#Jd%c5v&tBo9m|l@bA6!?8> zBe8m;&f41D0*{a&EKy#_vrP_VT5e7Qz0LPv>0Iu)8DN6KtydZ81sSmZ$^_UVp$|LP z>5}=$v#H^$W%OjvB^da33$LwNg)TA8uv_F7f8D~fyvXTG81L=1n7m~(-C8Y7Ty_T% zXQxiaOXwP2Y2&&rjh?v0)0eE~`tf4>Qs_yS4WzK!8+R31P!Hb&yh4-tM6@Rk94|+p zzv@@Kb8G`KlWT?UyDwq#^k|ruUyJ{F9^*N+_Cotm2QFzEgrH4}x$j>GW6EXM3NKs* z!_j!wd{Zj<;BcLl9SS4)cini~)`^qol0W#ieKyRq~2lg`omK|I|=I=};{7Wii!+-Ih8S<=Nw_TYITu`Q8DiV1H&N+*|bp6oWLVYQGoQK90ofZF*2tx{-Q65Tk#> z{Ai;51deAonT~ke#R=Mtyu&JH^iE|n6Zc7X(0J;$r7f@bsQFfJU#|9+wrUYaFC%$i7Q;Bf@eSe~5Jb%(Hk z=TP$}4Fi-;v2*g1U|8TTBsQDVoXBHflfRO!(+`HrLYesW^ED%}r_|_3i!|wbn+fMWZ{kIFI1yezIi6E%W1aaI z*me68!DF%-%Uf6k(#1EyU}8D0dZtaLwkTq9OA32_K5MhR)Pw)_W*cUBdy(^do-kfZ zljzg+eIPKWk7-Ih2A1KRr?uobf9jGjw304o&1KKAh5{PI)Ww?e_irN=?zwdBlW_KH zuNYmc^cm7i6lrM01S0Wg4!f>Q1MNi4!v~!x{B^~KeV9LixMfYK-0jMn90<$5hvx~91$lxMh#B5bg^q* z)!>d0KGu{A&`onTBJa94{=6;^Wi^JQbpth!F(6juYcapcg!t^VCH)8Q z;kSTi%p=KsT2|*o4|Tb*E&|p>M)3^BO>}3CW4A%s(tGfA!wr-((dFjUb|~s7Ustd* z81H;nfGJO>v)NtdR6t#lmlN)S%Gu{}l|>%AE#H9_c%_mZX-`qCNst6|`;vQXJelL5 zMh~=$5GSnyXqv7=0=+`;e)J}ypx$M1pIIN((pFNF767DBn9E?neJUX>6T1k zcCWx?*fq+dE@577h142((mMAKKKg%lv2_*^ zlnWrnjYI5#zHI1tbd34UlctYFmymt3?fk+<9WwjwJgPCa40;Xi8I7NF>8MQx{AWFY zZ~5M!_wWtJm)T5I^i!Eu^GUR;tsWm1o) z(%h3`(K0xinhSEvs&L9tU$9+w0^BMp*`3nzbn6L3LD4i+UP7o=K{t$Vje&z}ud{FK!=3FA@=*pC#_wk1MdlN&*2j2s z(`BecJ@Pt8m(>_;#IomR)cxyiSaQJ)o}Ijgrb5$*ZR%re_iBZoo4O#+uoA_Br$A4| zL7qqXBq~034W{IAjQc6pwE0XWb8Nvr%r!J14^OTpF5I1znEl6jH^1PQX=kuKT7tgj z^3fR`tH|kty?ih3W1$-Q8}}7lg3W6`!yON0BGV&98k<&wRa_zsSnEkVoNnTzEv?8b z7>1IOay&C<7EWnafGDJ9>UX}-3QNf|-w*KnSP6f_ zx9xPl%Ol{aq+ks`!Q=m?F+cTW$eK?ojMK<__QCrj(86jUFY+h8u@(l&`-^EX=Nk+k z*hIZHWTSCm17F&3F+S3mz=oX_$7!}gM7hQmKha#$m~#aUK8X>7NBPuVDhUE~!^z;> zYGCUH$mJyk_-0u(2=8A3=?_ms@{2Wi_whWQY;GBf8F22mpH+DB{|B-i9{RRt_~#8)8=7kjv$Gc{tF9xbKvqxH~Q_`eVk#= zqTT-*Ium~?yDki82$>5dij<*{A%yd+jff~@jK~*BN-`xS88SvlBqB+(iUtbjS-VlM z1_=!`kdi{AG^_ZYe}VHmXYIA_`?`i}@Z6nnnjIL8j9nVB^Wyf8qP_$&AE5v2Q8<-8 znH_(=9&^-gg2V!j$6wvd=((R|><|86)2R@2=%q17Z-tS=H@jfD=Wcepq9WqM+wiht zJ{d^7521Ta=>@(5dC}B|?luFg%&Fb5=|MW%KlwC-)_;Y ziUdgIL8jFlTBCo44Qbmz#;wfhj`efN`hXQ=-DO+MPn%7f*DoZR+7fU_q6kv-pToDr zW|lnTW68tS_}h|!?WR)Le?glDDXG!N29osNa3QbpbRwH)tOoak&1ihYZdNR?0*@qe z1bEJO6Pvh_6rbsYx8IeBHP;I{TjxRlD$awMm0t9^tTFEF)g;2&;%J-CxwNYG;8bo_ zo75LfK1LeBsX{I@;*x~_i{)ag2F{q;x{nHHN%9Um?;%kwqj>D-UJ%RmARgS!;asmX z?vF9R6#he$Soe1L?)A<^Mi|lZ0dI(Q-bTQzyRd~bDmN+eV!9U+}0F`Ts z`DaGnz+S7BMB-=)^*=ws)IE^kJ$<*DTvdAnZe>!`U-~z8y>%lu7Op4bg0gh-hp9~3 zd=J|4vXfmRB1OL1>?Xe5&tQ8&C%dtd+hdeJfK^6Q$TbdH>%3D4f}&2MgM}#Zo~c6< zlvZJ4$4~A}98Lt6aA%d;qu^b)6AYU~dA^OSuwbMd+hdjZ%qvGQD^?*tzB{9*)mgNX z&E?Y{ZkV|I33x_qqW2<};EXO~I${wduy1VMh}?d3jd+t zg>38zHm9G~iqqgsDcU8O3z}}h)U&FVnY4WrykEXzs{?Pt+ISh-C@;wL+tuRFTMndZ zlzWRw^uxC^Tn8n%4jhb{fU?(j;o$(*~F6s?4fV+YyI=Vy{DmiJ(}GqPpZUcsBa zmvQi+AQ^tW5VweF(M#Xn@H%gC9i5IDWIE3UQl(Q+M$HbE9TFv@MhfK3Ia%_l;Sfv~ zh-30om%#!pW6s%`2~GDT(5mMad^8cjy_`qa-uoun9&lr4#u~w&mM!#y!9AWp_XxXn zARcm$R6*kRsbuYn!|-1$moKXeV%CdGpvzZPW^7c8UeF08Eh_%-$!l&d~ASo zza<&*G-)#Yc^8(y&!GDgId*7u6|`v0rNKXifT?)Kru;s{bbTtuV6}C~pZf@lW{+Xn zk2Kn-^0PtXLm$LOjDQvQr`~LlO61CA$!hIyEN}H)=BlMQiF&=C_y~`2{W`ACbhjRl zE99Vqlr`OZ;uM_8qByvyh&}x4E`(;?WCC_2;}acIawU(?7OZ#!nyX`JV8u-ErrXD5y=#~;;8x5t)e-eehtdpcA@s~eNagun~EDW zVR-#G>sO_MdWvSGtbQAt-epLm?r4z9SCaADQ3X1&BO3~2r05*QWXAY@CS#s&NN>LM zfu&1Z8PiKU;nuQ#c3>chZJKn3wF-BH_C@oETEhwK($ysIG|Qp1#0ITeOwfL*B`Y4G z1)J>)Q219f9#?1Cxp%Llw!mwco;VJXZyM`nHMQc8x!L$`jS&6vKR3dTOQE=Y9b5Py z6=$TWz>I^M^!R;KGHP}oizd{FqnbJ?%eEo8Gx!?ltBrJV2 zjn-?n;H}A(9P8YXEL~wu!jwgb_LF8viLypJGZi*Fs+ZLaOyb2Uaoo7$Md11U4fbso zC#`#~VNRwg`C2oV^}RU@z-BtBr)OBbqvg2uZv(sT)E+p@@f(spWa71VYsexdf!gu* z(c6=`&P$O5dHeGOYL^G$;;(A-hh#XnyEh>DckD6h+hkTfdp7mB!@0pfHLw+*9>KKU z-~2nxi(q6;FG|em#o>lZe6)xoT5bvltIvz^;gOeMax?*U?~&jzq%5uocm$zwtf89 z!S7i9!+u_8uoo`ua-s2C&x29Q3L4Zm6Shr6V|4@P%^nbg!4D4H=TZmEAFg9FI#sEC z!hNW?l}uma80_0&Ov}ox$@njhFO=kp#c|>|SkM8|Bh%r%i3mxFYlfLowQ#XL43vUX z@ISG8cx={m99&z@`YD9d5!J1vZCIJ4NGo#QvIfj8ktFW-&%n>OyI{EMDwOKhVPa+h ztO?veru=m!eu;B9wv;?s9Jho1Z=N5WM~!i=#R&R7ddzOq@uM%r&a%AGAhxdH3yu^= z5NG{X_+sUS#x{j0o!I~``J(W&Z7wz2v6iN8pGF-_Q~8Bo-D&gNNo3^}?pEwxj^kcl zB(dZermlO;zM4FlX5IXTyJNoKqrfD7;FbU)B$I`w3>}$K&hJ*BcK{7G>4B_GDyjLa zN;e&`qHC_s;5>$kB%QBI`a9+5^7L~2uXz!E@EyeZj@^v;lg;GfkzSx>tswq*5nU%m zAf!S|B;W(b zzj?vm#A?V(+Cwe`&L9_K)-uN3@+53u8r)8CqMi}DJY9)YTq@>=*JC(;T!jcX^RA+T znmRPd=>ry2uS8L+c3zM`CLUdCPfmrs1oQ1`#In-`8#R0IsW#{S17J!&t58UgAwLe> z04?OvcyTSbtEWtshL^CjCsbLXhe^<;d4u(=jbbhdMe#L61L+3x2I?PLQRyrBkg_lZ zVr{3AJ1SF&>HwDs+USDtsb7*}9VY@_b8bxFIZ@$sL`Siw>nb~_0|PQ1X4k6ckf&mJ~@S0pDczGQxDN27IP z8%8l5DB;wECqw^&TK`{o9_L6J4lN)7fho{j`U)1;|G{lPuYpNk7}k%^pz;BZWLWSW z^XIW0k+-a4qxHhczP!Wi`1?BC5EIW#iKxb@T=wkV=?t{w=RzmdNC`(CmPv2~->)uYnYmE#_KEH zj$_Re_%2C{I)|H)aje0O=fvQGa&Z_LuFnUGFYVS=Y!sc167UtBIE=S_ltA=a8@RGPKmj5H=4_Bf(`CK*n}86NDkS z$6yPsI{gWj&gS^Q^1|fSBPrq~kU^5}EF-7nj=-ztgOKy8ge^OijhbK8$z{1%_A=c= zzQ8^#X$(SRv5%;vS;H78n=)4J-x)=&8QzR0Dw#hG`G)0_# z5lp6o?d!-$lRQ1H^a=l+sDkMvkWLzyL{~bcVvU^u+38*nb()*#_zP2d>dy<_>!v35 z(1vElu6zk?e7%euEPe_y+`PNY%?ZCub^*I@;bg%@O`;me-G5zmNs3!A+3&ZVRCTVP zCu7zSk4IKy5!ZJE(MU4U;KN1;w{Tt#Yj*NVVcMbdmMOQ=XA4xENTbpT^iEZvm5W>< z;!qRg^VA>tmO`}BMv__o_bs?)_CV}bLHbht77EB7fLi(Kv}55DxG{PHJ2NUl%m|^S zzZUDX!+0?@R8p%Ng?zWKlYzkPUaDLY1A*A72 z1d4Txz^$8kkQN2K?~mL6eXR0nM~~c23XXlO)dXR z$Bq-8^nS)9>NT!|evK8NCC0h)ZX|*FtmjzK?!_oL*TG%Q1{_ZMhEAq!?2ZeC%=<_O z_!s@WzU7G~m3TV|N>?6dpPOHV@7%1lR$q!JOp-*ef~gRp(+d9-=9AueJLoE*g?Pcz zixic9!Wfq(h<5eHm*IIdk?ZXmX!SEWaD&?;R$qUJIifFDhzl7_&`t6XUpQ0!pN% zdD@~Y$zZPo+yAD47c#6zdaxIz<1#@rU@I)>h(moP8*ZQKjYH-A`YxV$A{rDBTba zDydQI#NiLn7?jC;9lp%vn-{WYdu+*_tTtx-j?18*Si?BP=fJ&P3S_Wg4$)c2Fxhdb z>|pUq`u^P?lsTXeBZ6^wd-ZNsVbTlS)|3NF#RSPiE`yYA{*ZTqI8$F?L3*P15VEKH znFO(KxW#)aiIVAK^IUnTwW$v}o)%(b?Fndk`Vfm6exs#+Dr*|HhBh_WbM7y9GVtaw ziZ&~;qCuY2G1G?el}y8VuDQ&i&)W25p$?f{+khuSWQlM_GIcuN&6rfDVRohhthzdt zY!)bBvnBVTdgE4F=eU^YH}3=aFWKN1wu{s|+{Kt%OYzaE2&(fX5Pon-q@5bK`OB|_ z&?cJ-=nub$qNdSQaKmA+@f%^jX{KRSL@@+!P+~l1+rb^r&1{)e7KWv{Pa!lkM>x=8b2w znWRDE1g%W(tkm@7_)qJF@IBVHu^*c57+Jsom>E0W)#-%$8X3T&N!79=9L z9-2W1{G2I_jq;-)q+JQyHkgr*27zpF?EvF;&=8h%=)#RJZ}6awGkq~EjG^dEZ`%vf zOXmXl>dF47r=E-gH+Rs@0ZovfTYzbsB&Y)CR^^j&yyjyLrdCDJ9XR$~(Gc7&K0c()cuUa!A9Eo}XvT1PBSyysFvM&B+t(!t!ZH zG{_!-`Bh=4tyzNKd#2DTn20>n{nAo4chWSI{q=F>gQ z1?EA=5}1Oba4A}x3{G7^BvS>*n$4E##a~)6vq8F)5wKY0`$?qXK1#V zMJ!)QQRVUJpd|YkB-4IjWoaOECP>1B=L}k)%IzZfub?169^zRbqMuXg*Cm-`cDFBH zS9Bv+1IU`xLQv9Zr!{sq2d zd{1`5VGSLQU!YEA-jbpr57WubFj2DWv^4!;7)u_vt)>MNeUOv2lXN8MP@{R_D0{IK za{fEbW=&p6{>jT@%H5qGMfVj}Of;X~o^byBX`EP`WW<5m`Pa1V0Ym zfQ{eeNPE(9+F4QnQQ|l8NqL^RgrW{jznMgFkrFn%P6@61E3h528_}CA->_~AQ2vO`D6$4V|NqtHF+@g<~7)oXihZ$G=q;{B&|(wqQ%RN zVdt`H{H>V8WOvEayih?}MRNe>t0Kd2w8LT<5wr_wF##0Ue!ao?j$CnJCgm?_Y1De9KefwF~I0KYIyCh1HuSCNV6xWDpvR+Y zFvG->&a3w(|1FiP?R~gr{iqvAmEMoQ}nY-z8 z-LvHmG@927?Q1XK?FlhBtazCf^qb5I8S4>wVNKd9U&DS~)Q4V6{ppTldzj@v8{krb zAW_}vL+?6k!}*bV&@6d^W0UIG%5hkE2(n>rP|JriuLui?tD^(5qP z9$C(@8_l*>faZrg_@g|T45Z$M(EvWv zl0H^yVFzv);qTTfm@}PwORAa>&wL+7N>_xpo3*3yDO^YNeHF)07NvSgmmAcIYB6}s zhP=Ph!n!P+46U-p*I3?v}TdE8?X43I7Q{QEq?T-79m9}p%>mflz9Zt7EE`FSJQowf*hi|#W{>(0PL%S#xr zZ-(g5bdqv4j}iHKo;meelHUJX3R}Amqgt*Se)_1$&RMyLestBQnJV&hmhL#r)sy2t z>)T9Dtn;P@I%}w^RtTA1GL>-)(x*u;R4~ZUl@$EF0xN!6)XDA{!>rY_X>9BUcryM1 z{pY;mm1c9fgRRTxoG&ln`+Pf1u|mPB<`xeol(KRB$t z3J*A%p>f6`97>a*K`P%NCulD{BJ6^DJ1ofLUJ-Cj6~v_NS73&dGCk#FK{Cd6(XOqP zOuCdT23j4)O(PFb*AxJQmCFZusSb{nslCLe@_TMg-_Iz2ASx{D6~sDvNBpWuXFH{575ARkM! z$a>Po&vl6eL&0INe7b`c918(@li|Kr zqij9DJF$U{c_>6qwVh=98YeSz%C6v?G4Cm_vqt z*p_Qc$my*oQ27bR8}Yk~!NX!C^6Yoq9HLH&Z!98HH`k!Zfksxw+Kndojq!W9@8|vn zvDo{_g6NfeMs1GscxE7!xhFIR2?yOl?!|k&yyzL)D`YeOd_3sP&I0~Z$ycn@>lJi| zv=O~3rOfV2Na7_5sNi=sWs>!C8FT64DjIe=n5ly6&}{pZA6<9>H@`W@S`WR)Lbv0v z%VaMR-&4khD)wOIZZ2b)S%_y}yO$XY2v?$U}^}piMmge#D$(hcW+dH1sQ(ld+gQ{O-M)2EVW%3*PGR;Z`dD6G33K_9eVmpGu!s)FG{v;prr%Nc){%y?ligxX9sfe zx3e@`QaF?3Ozz}A-Mo#|w5fr%g+0_K`j8oRX>jgR1Dpv;=HKmi1%BFQnjoA@HoO%j zMk-y9U%dwmw!UUPB15n`P>ojS&j*Dy`Y^-I3Z6S8u)|B$N%YuibmubC7q1TD1Cxbh zz_JNymMp|%ZsxGZTnzPit3s21VoL}>xCdQBrv^JlS&N z8RbsB!xMq^=)l;3UPCri)Z5|E=Suu|B@d_2|A9jGH?|$`G3C|nh=`CEZOL4l-b`!Ni+(4o&VlHs@FOB9`<0Nb=?(6aICoRe!Y zmunLtzI&E3Q+qFQInOLK%5MUe>e378#r(7VI!s!vGf|b=&qy}q!-I)(Xe;>vM=H<2 zxMnp(eJ=rUT}tvM_TqxG?P&F^-0bnb3(TwYro<<$1M0g4=v2WvnEKxx7)Y?d-FLpD zy2Cl#J71Mpp5!8rI#ggm-zOP`9BvWD+m`39RR z@m1C_rkTr)o2+u6wXN?N?`Abpr0z@8EYCu!Z6I~x8}k#_D3gMk0@Pzq!-iwgbgv$d zG_oluV0INh%)QFx4W}?)dLOd~>w1t>xPYkFYz&-#3N1YM!}J~V&4-#tId3WuD;X{y zm~sUc4r!Bai zqKRBgEv!xUq>2GzR7oZgnsxGNzU&i7Xc8v}XZ{05?I^BgD%n?G^RWkZF%k>MP@Olz zyj!vu4X+rnvgUqNPFtFPM@fWZUq;XX6=$lK{|BFpIMVsE)ZoKHXVPV2Pruq;1&1T% zl+7K2UB5FSYxoIJk0$){$d=S{bNI9Wwvg*>;>2a{PfUN*guWsoWJ1}H>ZU3Z&1gGv zq3$;1a^0JcPVKnpk}P#JpG?D*=E9UGji9`4F%jVTp#3?nCrIzZgP^Izf;nlidch1G`-Fb$u7JEuIM_rlL;A4;?zd%72kd}oCq2~a5v4+n4X9M}07L?A z;@ulzbida%=x)-&Ee|xn`kVmh1?|R&#$+m!BS!x#SU?)8{;`@ODI{SdmkIK&LD9RC zG*Gx1x>JLh6d^ro!*$WUL>3e2O?#oqN)>O3sj@|-IgISa&19O!QjS^Ig(rh1VX~(c zt0$8j^zzHdlQn3g_!x=IIJm&63 zpSn5ddbE#Ov)&Q&x82P{S2w|bivpA5$cpqVZ`{aAq*}wIL>C_XJ#(>$L(rsH}#lH zI2V}z@%Cf+i0QqNwoPQK)YqT(YcBHO_8brt+5Jp;Em!w zvl5U|PowjuieW?761ZR40%m)T!nU$3G9sZ(7n?EUOH8ts?H<7PGsi%hBkVP}*crg~Dxxyx6YIjBzN(>iUyJHnGLM|s^`H`2iEMA!AoI*O4W%plP*SRxS(EV!uKtdr z*R>X7RyFsVo4ytmI3`a=xEQrgGa$t;hnW2jx*$#QE|k2BgCLHp5$!L?mU6qtv5!Ku zI?)-+UQY*$KM^FvaT5;Zw!$CIg&g|7bT)pAGqdHb3o$w(Ni1v^GiDwqA=N>V+Uzby zx0@Asy=*!ODePxY6=gDFvp#@seH0yi?LlSwmXQ_Tt*M%3cSH0@9Bm!p{tvpHu*zvJ JT^#-k{tqSfMsolF literal 0 HcmV?d00001 diff --git a/demos/mnist/hidden2_biases b/demos/mnist/hidden2_biases new file mode 100644 index 0000000000..5e90839228 --- /dev/null +++ b/demos/mnist/hidden2_biases @@ -0,0 +1 @@ +Ã`V»äˆ#;zú+9º×¼M®Ý=B!–¼fû¼A¸=‡Ê=¦T½²Ð^½tv=Ö(~¼ Ë:rm]=ßê=ý¦ˆ¹àÂ=#“\¼T2‡;òÈáZNh)SG3HC0$ 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 PkTu-c(TKY=RJi{EQ#u-X literal 0 HcmV?d00001 diff --git a/demos/mnist/index.html b/demos/mnist/index.html new file mode 100644 index 0000000000..898980bce2 --- /dev/null +++ b/demos/mnist/index.html @@ -0,0 +1,3 @@ +

_so$}Gr6G`|SZo3ihQSI51NY#fx; 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 aCRahz=O)>O;|p(`HQX|wLyY_y3>JaH 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